1.思维导图
2.使用select实现TCP客户端的并发
#include
#define SER_PORT 8888
#define SER_IP "192.168.122.25"
#define CLI_PORT 9999
#define CLI_IP "192.168.122.41"
int main(int argc, const char *argv[])
{
//创建用于通信的套接字文件描述符
int cfd = socket(AF_INET,SOCK_STREAM,0);
if(cfd==-1)
{
perror("socket error");
return -1;
}
//绑定(可选)
struct sockaddr_in cin;
cin.sin_family = AF_INET;
cin.sin_port = htons(CLI_PORT);
cin.sin_addr.s_addr = inet_addr(CLI_IP);
if(bind(cfd,(struct sockaddr*)&cin,sizeof(cin))==-1)
{
perror("bind error");
return -1;
}
printf("bind success");
//链接服务器
//填充服务器地址信息
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(SER_PORT);
sin.sin_addr.s_addr = inet_addr(SER_IP);
//链接
if(connect(cfd,(struct sockaddr*)&sin,sizeof(sin))==-1)
{
perror("connect error");
return -1;
}
printf("connect success\n");
//准备文件描述法容器
fd_set readfds,tempfds;
//清空容器
FD_ZERO(&readfds);
//将要检测的文件描述符放入集合
FD_SET(0,&readfds);
FD_SET(cfd,&readfds);
int maxfd=cfd;
struct sockaddr_in cin_arr[1024];
char buf[128]="";
while(1)
{
tempfds = readfds; //存储一份数据
使用select函数对容器中的文件描述符进行检测
int res = select(maxfd+1,&tempfds,NULL,NULL,NULL);
if(res == -1)
{
perror("select error");
return -1;
}else if(res==0)
{
printf("timeout\n");
return -1;
}
//程序执行至此,说明已经有事件产生并且解除了select的阻塞
//并且此时文件描述符集合中只剩下本次触发的事件对应的文件描述符
//判断哪个文件描述符还在集合中,如果在,就执行相关函数
if(FD_ISSET(cfd,&tempfds))
{
//接收服务器发来的消息
bzero(buf,sizeof(buf));
recv(cfd,buf,sizeof(buf),0);
printf("[%s:%d]:%s\n",SER_IP,SER_PORT,buf);
}
//判断0号文件描述符是否在集合中
if(FD_ISSET(0,&tempfds))
{
//向服务器发送消息
bzero(buf,sizeof(buf));
printf("请输入>>>");
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1]=0;
send(cfd,buf,sizeof(buf),0);
printf("发送成功\n");
if(strcmp(buf,"quit")==0)
{
break;
}
}
}
//关闭套接字
close(cfd);
return 0;
}
3.使用poll实现TCP服务器的并发
#include
#define SER_PORT 8888
#define SER_IP "192.168.122.41"
int main(int argc, const char *argv[])
{
int sfd = socket(AF_INET,SOCK_STREAM,0);
if(sfd==-1)
{
perror("socket error");
return -1;
}
int reuse=-1;
if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))==-1)
{
perror("setsockopt error");
return -1;
}
printf("端口号快速重用成功\n");
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(SER_PORT);
sin.sin_addr.s_addr = inet_addr(SER_IP);
if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))==-1)
{
perror("bind error");
return -1;
}
printf("bind success %s %s %d \n",__FILE__,__func__,__LINE__);
if(listen(sfd,128)==-1)
{
perror("listen error");
return -1;
}
printf("listen success %s %s %d\n",__FILE__,__func__,__LINE__);
struct sockaddr_in cin;
socklen_t socklen = sizeof(cin);
int newfd =-1;
//定义一个等待文件描述符结构体数组
struct pollfd pfd[1024];
pfd[0].fd=0;
pfd[0].events = POLLIN;
pfd[1].fd=sfd;
pfd[1].events = POLLIN;
int num=2;
struct sockaddr_in cin_addr[1024];
while(1)
{
//阻塞检测集合中是否有事件产生
int res = poll(pfd,num,-1);
if(res ==-1)
{
perror("poll error");
return -1;
}else if(res ==0)
{
printf("time out\n");
return -1;
}
//程序执行到此,表示已经有事件产生
//判断是否sfd产生事件
if(pfd[1].revents == POLLIN)
{
newfd=accept(sfd,(struct sockaddr*)&cin,&socklen);
if(newfd==-1)
{
perror("accept error");
return -1;
}
printf("[%s:%d]: %s %s %d\n",inet_ntoa(cin.sin_addr),\
ntohs(cin.sin_port),__FILE__,__func__,__LINE__);
num++;
pfd[num-1].fd=newfd;
pfd[num-1].events = POLLIN;
cin_addr[newfd]=cin;
}
if(pfd[0].revents == POLLIN)
{
//收发数据
char wbuf[128]="";
scanf("%s",wbuf);
printf("触发了终端输入事件\n");
if(strcmp(wbuf,"quit")==0)
{
break;
}
for(int i=2;i