作业2:完成局域网CS模型,局域网内一个服务器,多个客户端连接一个服务器,完成局域网聊天(select函数,poll函数,完成TCP并发服务器)。
poll函数应用:
服务器部分代码:
//TCP服务器
#include<myhead.h>
#include<poll.h>
#define IP "192.168.60.68"
#define PORT 1028
#define BACKLOG 20
int main(int argc, const char *argv[])
{
//AF_INET:IPv4通信
int oldfd=socket(AF_INET,SOCK_STREAM,0);
if(oldfd==-1)
{
perror("socket");
return -1;
}
int n=1;
int res=setsockopt(oldfd,SOL_SOCKET,SO_REUSEADDR,&n,sizeof(n));
if(res==0)
{
printf("端口号快速复用成功\n");
}
//2、绑定IP和端口号
struct sockaddr_in server={
.sin_family=AF_INET,//IPv4
.sin_port=htons(PORT),//端口号转为网络字节序
.sin_addr.s_addr=inet_addr(IP),//IP地址
};
if(bind(oldfd,(struct sockaddr *)&server,sizeof(server))==-1)
{
perror("bind");
return -1;
}
//3、监听
if(listen(oldfd,BACKLOG)==-1)
{
perror("listen");
return -1;
}
//4、接受客户端连接请求创建新的描述符用于通信
//1-1设置结构体数组检测描述符发生事件
struct pollfd fds[100];
fds[0].fd=oldfd;//旧的套接字描述符检测数组中
fds[0].events=POLLIN;
int i;
struct sockaddr_in client;
socklen_t client_len=sizeof(client);
int newfd;
int count=1;
while(1)
{
int res=poll(fds,100,-1);
if(res==-1)
{
perror("poll");
}
if(res==0)
{
printf("超时\n");
}
if(fds[0].revents==POLLIN)
{
newfd=accept(oldfd,(struct sockaddr *)&client,&client_len);
if(newfd==-1)
{
perror("accept");
return -1;
}
printf("%s发来连接请求\n",inet_ntoa(client.sin_addr));
fds[count].fd=newfd;
fds[count++].events=POLLIN;
}
for(i=1;i<100;i++)
{
if(fds[i].fd>0&&fds[i].revents==POLLIN)
{
char buff[1024];
memset(buff,0,sizeof(buff));
int len=recv(fds[i].fd,buff,sizeof(buff),0);
if(len==0)
{
printf("客户端下线\n");
close(fds[i].fd);
break;
}
printf("%s\n",buff);
strcat(buff,"今天是周一");
send(fds[i].fd,buff,sizeof(buff),0);
}
}
}
close(oldfd);
return 0;
}
客户端代码部分:
//TCP客户端
#include<myhead.h>
#define IP "192.168.60.68"
#define PORT 1028
int main(int argc, const char *argv[])
{
//1、创建套接字
int oldfd=socket(AF_INET,SOCK_STREAM,0);
if(oldfd==-1)
{
perror("socket");
return -1;
}
struct sockaddr_in server={
.sin_family=AF_INET,
.sin_port = htons(PORT),
.sin_addr.s_addr =inet_addr(IP),
};
if(connect(oldfd,(struct sockaddr *)&server,sizeof(server))==-1)
{
perror("connect");
return -1;
}
char buff[1024];
while(1)
{
fgets(buff,sizeof(buff),stdin);
send(oldfd,buff,sizeof(buff),0);
int len=recv(oldfd,buff,sizeof(buff),0);
if(len==0)
{
perror("服务意外退出\n");
break;
}
printf("接受服务器消息:%s\n",buff);
}
close(oldfd);
return 0;
}
效果演示:
selsect函数部分:
代码效果:
服务器部分:
#include <myhead.h>
#define IP "192.168.60.68"
#define PORT 1028
#define BACKLOG 20
int main(int argc, const char *argv[])
{
//AF_INET:IPV4通信
//SOCK_STREAM:TCP通信协议
int oldfd = socket(AF_INET,SOCK_STREAM,0);//1、创建套接字
if(oldfd==-1)
{
perror("socket");
return -1;
}
//2、绑定IP和端口号
struct sockaddr_in server = {
.sin_family = AF_INET,//IPV4
.sin_port = htons(PORT),//端口号转为网络字节序
.sin_addr.s_addr = inet_addr(IP),//IP地址
};
if(bind(oldfd,(struct sockaddr *)&server,sizeof(server))==-1)
{
perror("bind");
return -1;
}
//3、监听
if(listen(oldfd,BACKLOG)==-1)
{
perror("listen");
return -1;
}
//4、接受客户端连接请求创建新的描述符用于通信
struct sockaddr_in client;//接收客户端信息的结构体
socklen_t client_len = sizeof(client);//计算出结构体大小
//1-1定义容器放入描述符
fd_set readfds,temp;//定义两个集合存放描述符
FD_ZERO(&readfds);//清空集合内的内容
FD_SET(0,&readfds);
FD_SET(oldfd,&readfds);//描述符放入集合
int maxfd = oldfd;//保留最大的描述符
int newfd;
while(1)//循环连入多个客户端
{
//1-2监视所有的描述符
temp = readfds;
int res = select(maxfd+1,&temp,NULL,NULL,NULL);//永久阻塞
if(res==-1)
{
perror("select");
return -1;
}
if(res==0)
{
printf("超时\n");
continue;
}
//程序执行到此,说明某些描述符解除了阻塞
//1-3循环检测是谁发生了IO操作
int i;
for(i = 0;i<=maxfd;i++)
{
if(!FD_ISSET(i,&temp))//如果描述符不在容器中,继续下次循环查找
{
continue;
}
if(i==oldfd)//旧的描述符解除的阻塞
{
newfd = accept(i,(struct sockaddr *)&client,&client_len);
FD_SET(newfd,&readfds);//产生新描述符代表新客户端,需要放入集合
maxfd = newfd>maxfd?newfd:maxfd;//更新最大的描述符
if(newfd==-1)
{
perror("accept");
return -1;
}
printf("%s发来连接请求\n",inet_ntoa(client.sin_addr));
}
else//0号描述符解除的阻塞,进行通话
{
//5、循环收发信息
char buff[1024];
memset(buff,0,sizeof(buff));
int len = recv(i,buff,sizeof(buff),0);//0:阻塞发送 MSG_DONTWAIT:非阻塞
if(len==0)
{
close(i);//关闭新的描述符
FD_CLR(i,&readfds);//移除退出的客户端描述符
if(maxfd==i)//退出后,描述符个数减一
{
maxfd--;
}
printf("客户端下线\n");
break;
}
printf("%s\n",buff);
fgets(buff,sizeof(buff),stdin);
send(i,buff,sizeof(buff),0);
}
}
}
close(oldfd);
return 0;
}
客户端部分:
//TCP客户端
#include<myhead.h>
#define IP "192.168.60.68"
#define PORT 1028
int main(int argc, const char *argv[])
{
//1、创建套接字
int oldfd=socket(AF_INET,SOCK_STREAM,0);
if(oldfd==-1)
{
perror("socket");
return -1;
}
struct sockaddr_in server={
.sin_family=AF_INET,
.sin_port = htons(PORT),
.sin_addr.s_addr =inet_addr(IP),
};
if(connect(oldfd,(struct sockaddr *)&server,sizeof(server))==-1)
{
perror("connect");
return -1;
}
char buff[1024];
while(1)
{
fgets(buff,sizeof(buff),stdin);
send(oldfd,buff,sizeof(buff),0);
int len=recv(oldfd,buff,sizeof(buff),0);
if(len==0)
{
perror("服务意外退出\n");
break;
}
printf("接受服务器消息:%s\n",buff);
}
close(oldfd);
return 0;
}