43、网络编程/IO多路复用相关练习20240305
一、使���select实现tcp服务器多路复用,使用poll实现tcp客户端多路复用,完成两端通信(不使用多进程线程可以实现同时收发)
服务器代码:
#include #define SER_IP "192.168.32.130" #define SER_PORT 8888 int main(int argc, const char *argv[]) { //1.创建监听的套接字 int sfd=-1; sfd=socket(AF_INET,SOCK_STREAM,0); //参数1表示使用的是ipv4的通信 //参数2表示使用tcp通信类型 //表示前面以及特定了通信协议 if(sfd==-1) { perror("socket error"); return -1; } printf("sfd=%d\n",sfd); int reuse=1; if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))==-1) { perror("setsockopt error"); return -1; } puts("端口号快速重用成功"); //2.绑定ip地址和端口号 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\n"); //3.启动监听 允许客户端链接 if(listen(sfd,128)==-1) { perror("listen error"); return -1; } printf("listen success\n"); //4.当有客户端发来连接请求后 创建新的用于通信的套接字 struct sockaddr_in cin; socklen_t socklen=sizeof(cin); char sbuf[128]=""; int newfd=-1; //创建文件描述符容器 fd_set readfds,tempfds; //清空内容 FD_ZERO(&readfds); //将sfd和0放入容器 FD_SET(0,&readfds); FD_SET(sfd,&readfds); int maxfd=sfd;//存放最大的文件描述符 struct sockaddr_in cin_arr[1024];//存放客户端地址 while(1) { tempfds=readfds; int res=select(maxfd+1,&tempfds,NULL,NULL,NULL); if(res==-1) { perror("select error"); return -1; } else if(res==0) { puts("time out"); return -1; } for(int i=0;imaxfd) { maxfd=newfd; } }else if(0==i) { fgets(sbuf,sizeof(sbuf),stdin); sbuf[strlen(sbuf)-1]=0; printf("出发键盘事件:%s\n",sbuf); for(int k=4;k=0;j--) { if(FD_ISSET(j,&readfds)) { maxfd=j; break; } } continue; } printf("[%s:%d]:%s\n",inet_ntoa(cin_arr[i].sin_addr),ntohs(cin_arr[i].sin_port),rbuf); strcat(rbuf,"*_*"); send(i,rbuf,strlen(rbuf),0); } } } close(sfd); return 0; }
客户端代码:
#include #define SER_IP "192.168.32.130" #define SER_PORT 8888 #define CLI_IP "192.168.32.130" #define CLI_PORT 9999 int main(int argc, const char *argv[]) { //1.创建用于通信的套接字文件描述符 int cfd =socket(AF_INET,SOCK_STREAM,0); if(cfd==-1) { perror("socket error"); return -1; } printf("cfd=%d\n",cfd); //2.绑定(非必须) /* 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; } puts("bind success"); */ //3.链接服务器 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"); //使用poll完成客户端的读写操作并发执行 //定义pollfd数组 struct pollfd pfd[2]; pfd[0].fd=0;//检测0号文件描述符 pfd[0].events=POLLIN;//检测0号文件描述付的读操作 pfd[1].fd=cfd; pfd[1].events=POLLIN; //4.数据收发 char wbuf[128]=""; while(1) { int res=poll(pfd,2,-1);//阻塞监视两个文件描述符是否发生读端时间操作 if(res==-1) { perror("poll error"); return -1; }else if(res==0) { puts("time out\n"); return -1; } if(pfd[0].revents==POLLIN)//标准输入事件 输入信息发生给客户端 { bzero(wbuf,sizeof(wbuf)); printf("请输入>>>"); fgets(wbuf,sizeof(wbuf),stdin); wbuf[strlen(wbuf)-1]=0; send(cfd,wbuf,strlen(wbuf),0); printf("发送成功\n"); if(strcmp(wbuf,"quit")==0) break; } if(pfd[1].revents==POLLIN)//cfd发生事件,即服务器发来消息 可以执行recv操作 { bzero(wbuf,sizeof(wbuf)); recv(cfd,wbuf,sizeof(wbuf),0); printf("收到消息:%s\n",wbuf); } } //5.关闭套接字 close(cfd); return 0; }
运行:
思维导图
模拟面试整理:
The End