1,select服务端
- 1 #include
- 2
- 3 #define PORT 8888 //端口号
- 4 #define IP "192.168.228.165" //IP地址
- 5
- 6
- 7 int main(int argc, const char *argv[])
- 8 {
- 9 //1、创建用于接受连接的套接字
- 10 int sfd = socket(AF_INET, SOCK_STREAM, 0);
- 11 if(sfd == -1)
- 12 {
- 13 perror("socket error");
- 14 return -1;
- 15 }
- 16
- 17 printf("socket success sfd = %d\n", sfd); //4
- 18
- 19
- 20 //设置端口号快速重用
- 21 int reuse = 1;
- 22 if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1)
- 23 {
- 24 perror("setsockopt error");
- 25 return -1;
- 26 }
- 27 printf("设置端口快速重用成功 _%d_ %s_ %s_\n", __LINE__, __FILE__, __func__);
- 28
- 29
- 30
- 31
- 32
- 33 //2、绑定IP地址和端口号
- 34 //2.1、填充要绑定的地址信息结构体
- 35 struct sockaddr_in sin;
- 36 sin.sin_family = AF_INET; //表明是ipv4
- 37 sin.sin_port = htons(PORT); //端口号
- 38 sin.sin_addr.s_addr = inet_addr(IP); //IP地址
- 39
- 40 //2.2、绑定
- 41 if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin))==-1)
- 42 {
- 43 perror("bind error");
- 44 return -1;
- 45 }
- 46 printf("bind success _%d_ %s_ %s_\n", __LINE__, __FILE__, __func__);
- 47
- 48 //3、将套接字设置成被动监听状态
- 49 if(listen(sfd, 128) == -1)
- 50 {
- 51 perror("listen error");
- 52 return -1;
- 53 }
- 54
- 55 printf("listen success _%d_ %s_ %s_\n", __LINE__, __FILE__, __func__);
- 56
- 57 //4、阻塞等待客户端连接请求,如果有新的客户端连接,则创建一个新的用于通信的套接字
- 58 //4.1、定义客户端地址信息结构体
- 59 struct sockaddr_in cin; //客户端地址信息结构体
- 60 cin.sin_family = AF_INET;
- 61 socklen_t socklen = sizeof(cin); //客户端地址信息的大小
- 62
- 63
- 64 定义一个用于检测文件描述符的集合
- 65 fd_set readfds, tempfds; //在栈区定义
- 66
- 67 清空容器中的内容
- 68 FD_ZERO(&readfds);
- 69 将要检测的文件描述符放入集合中
- 70 FD_SET(sfd, &readfds); //将sfd文件描述符放入
- 71 FD_SET(0, &readfds); //将0号文件描述符放入
- 72
- 73
- 74
- 75 //定义一个容器
- 76 char buf[128] = "";
- 77 int res = 0; //接收select的返回值
- 78 int newfd = -1; //存放用于最新连接客户端的套接字
- 79 int maxfd = sfd; //定义控制select函数中最大文件描述符
- 80
- 81 struct sockaddr_in saveCin[1024]; //用于存放客户端地址信息结构体
- 82
- 83
- 84 while(1)
- 85 {
- 86 将集合内容复制一份
- 87 tempfds = readfds;
- 88
- 89 使用select阻塞等待集合中的文件描述符有事件产生
- 90 res = select(maxfd+1, &tempfds, NULL, NULL, NULL);
- 91 if(res == -1)
- 92 {
- 93 perror("select error");
- 94 return -1;
- 95 }else if(res == 0)
- 96 {
- 97 printf("time out\n");
- 98 return -1;
- 99 }
- 100
- 101
- 102 //遍历所有集合中文件描述符
- 103 for(int i=0; i<=maxfd; i++)
- 104 {
- 105 //判断当前i是否在集合中,如果不在,直接判断下一个
- 106 if(!FD_ISSET(i, &tempfds))
- 107 {
- 108 continue;
- 109 }
- 110
- 111 判断sfd是否还在集合中
- 112 if( i == sfd)
- 113 {
- 114 //4.2、阻塞接收客户端的链接请求,并且获取客户端的地址信息
- 115 newfd = accept(sfd, (struct sockaddr*)&cin, &socklen);
- 116 if(newfd == -1)
- 117 {
- 118 perror("accept error");
- 119 return -1;
- 120 }
- 121 printf("accept success _%d_ %s_ %s_\n", __LINE__, __FILE__, __func__);
- 122
- 123 将newfd放入readfds中
- 124 FD_SET(newfd , &readfds);
- 125
- 126 //更新maxfd
- 127 if(newfd > maxfd)
- 128 {
- 129 maxfd = newfd;
- 130 }
- 131
- 132 //将最新的客户端套接字放入数组的下标为new的位置
- 133 saveCin[newfd] = cin;
- 134 printf("newfd = %d\n", newfd);
- 135
- 136 }else if(i == 0 ) //判断是否是终端输入
- 137
- 138 {
- 139 char buf1[1000] = "";
- 140
- 141 bzero(buf, sizeof(buf));
- 142 //从终端获取数据
- 143 fgets(buf, sizeof(buf), stdin); //从终端获取数据
- 144 buf[strlen(buf)-1]='\0';
- 145 printf("触发终端输入事件:%s\n", buf);
- 146
- 147 sprintf(buf1, "%s%s", "系统消息:", buf);
- 148
- 149 //将数据发送给所有客户端
- 150 for(int j=4; j<=maxfd; j++)
- 151 {
- 152 send(j, buf1,sizeof(buf1), 0);
- 153 }
- 154
- 155
- 156 }else
- 157 {
- 158 //5、收发数据使用newfd完成通信
- 159 char buf[128] = "";
- 160 //清空字符串
- 161 bzero(buf, sizeof(buf));
- 162 int ret = recv(i, buf, sizeof(buf), 0); //从套接字中读取客户端发来的消息
- 163
- 164 //判断收到的结果
- 165 if(ret == 0)
- 166 {
- 167 printf("客户端已经下线\n");
- 168 close(i); //关闭通信的套接字
- 169
- 170 将当前的文件描述符从集合中删除
- 171 FD_CLR(i, &readfds);
- 172
- 173 更新maxfd
- 174 for(int j=maxfd; j>=0; j--)
- 175 {
- 176 //判断当前的j是否在集合中,如果在,则为maxfd
- 177 if(FD_ISSET(j, &readfds))
- 178 {
- 179 maxfd = j;
- 180 break;
- 181 }
- 182 }
- 183
- 184 continue; //继续判断下一个
- 185 }else if(ret < 0)
- 186 {
- 187 perror("recv error");
- 188 return -1;
- 189 }
- 190
- 191 printf("[%s:%d]:%s\n", inet_ntoa(saveCin[i].sin_addr), ntohs(saveCin[i].sin_port), buf);
- 192
- 193 //将读取的信息,加上一些字符发送回去
- 194 strcat(buf, "*_*");
- 195 send(i, buf, sizeof(buf), 0);
- 196
- 197
- 198 }
- 199 }
- 200
- 201 }
- 202
- 203
- 204
- 205 //6、关闭所有套接字
- 206 close(sfd); //关闭监听
- 207
- 208 return 0;
- 209 }
- ~
- #include
- 2
- 3 #define SERPORT 8888 //服务器端口号
- 4 #define SERIP "192.168.228.165" //服务器IP地址
- 5
- 6 int main(int argc, const char *argv[])
- 7 {
- 8 //创建用于通信的套接字
- 9 int cfd = socket(AF_INET,SOCK_STREAM,0);
- 10 if(cfd == -1)
- 11 {
- 12 perror("socket error");
- 13 return -1;
- 14 }
- 15
- 16 //连接服务器
- 17 ///填充服务器地址信息结构体
- 18 struct sockaddr_in sin;
- 19 sin.sin_family = AF_INET;
- 20 sin.sin_port = htons(SERPORT);
- 21 sin.sin_addr.s_addr = inet_addr(SERIP);
- 22
- 23 ///连接服务器
- 24 if(connect(cfd,(struct sockaddr *)&sin,sizeof(sin)) == -1)
- 25 {
- 26 perror("connect error");
- 27 return -1;
- 28 }
- 29
- 30 //创建用于检测文件描述符的集合
- 31 fd_set readfds,tempfds;
- 32
- 33 //清空集合
- 34 FD_ZERO(&readfds);
- 35
- 36 //将要检测的文件描述符放入集合中
- 37 FD_SET(cfd,&readfds);
- 38 FD_SET(0,&readfds);
- 39
- 40 int res = 0; //接收select的返回值
- 41 int maxfd = cfd; //集合中值最大的文件描述符
- 42
- 43 //向服务器进行数据的收发
- 44 char buf[128] = "";
- 45 int ret = 0; //接收recv的返回值
- 46 while(1)
- 47 {
- 48 tempfds = readfds;
- 49
- 50 res = select(maxfd+1,&tempfds,NULL,NULL,NULL);
- 51 if(res == -1)
- 52 {
- 53 perror("select error");
- 54 return -1;
- 55 }else if(res == 0)
- 56 {
- 57 printf("time out\n");
- 58 return -1;
- 59 }
- 60
- 61 //遍历集合中所有的文件描述符
- 62 for(int i = 0;i <= maxfd;i++)
- 63 {
- 64 //判断当前文件描述符是否在集合中
- 65 if(!FD_ISSET(i,&readfds))
- 66 {
- 67 continue;
- 68 }
- 69
- 70
- 71 //判断0号文件描述符是否还在集合中
- 72 if(0 == i)
- 73 {
- 74 //从标准输入中读取数据
- 75 fgets(buf,sizeof(buf),stdin);
- 76 buf[strlen(buf)-1] == 0;
- 77
- 78 //将数据发送到服务器
- 79 if(send(cfd,buf,sizeof(buf),0) == -1)
- 80 {
- 81 perror("send error");
- 82 return -1;
- 83 }
- 84
- 85 }else if(cfd == i) //判断cfd是否还在集合中
- 86 {
- 87 //接收来自服务器的消息
- 88 ret = recv(cfd,buf,sizeof(buf),0);
- 89 if(ret == -1)
- 90 {
- 91 perror("recv error");
- 92 return -1;
- 93 }else if(ret == 0)
- 94 {
- 95 printf("服务器已关闭\n");
- 96 return -1;
- 97 }
- 98
- 99 printf("服务器消息:%s\n",buf);
- 100 }
- 101 }
- 102 }
- 103
- 104 //关闭文件描述符
- 105 close(cfd);
- 106
- 107 return 0;
- 108 }
效果图

poll客户端
- 1 #include
- 2
- 3 #define IP "192.168.228.165"
- 4 #define PORT 8888
- 5
- 6 int main(int argc, const char *argv[])
- 7 {
- 8 //创建用于连接的套接字
- 9 int sfd = socket(AF_INET,SOCK_STREAM,0);
- 10 if(sfd == -1)
- 11 {
- 12 perror("socket error");
- 13 return -1;
- 14 }
- 15
- 16 //绑定服务器IP和端口号
- 17 ///填充服务器地址信息结构体
- 18 struct sockaddr_in sin;
- 19 sin.sin_family = AF_INET;
- 20 sin.sin_port = htons(PORT);
- 21 sin.sin_addr.s_addr = inet_addr(IP);
- 22
- 23 ///绑定
- 24 if(bind(sfd,(struct sockaddr *)&sin,sizeof(sin)) == -1)
- 25 {
- 26 perror("bind error");
- 27 return -1;
- 28 }
- 29 printf("bind success\n");
- 30
- 31 //将连接用套接字设置为被动监听状态
- 32 if(listen(sfd,128) == -1)
- 33 {
- 34 perror("listen error");
- 35 return -1;
- 36 }
- 37 printf("listen success\n");
- 38
- 39 //定义一个集合管理sfd和打开的通信用文件描述符
- 40 struct pollfd fds[1024];
- 41 int maxfd = 0;
- 42
- 43
- 44 //手动放入sfd
- 45 fds[0].fd = sfd;
- 46 fds[0].events = POLLIN; //表明为读事件
- 47
- 48 //将fds中其余元素初始化为-1
- 49 for(int i = 4;i <= 1024;i++)
- 50 {
- 51 fds[i].fd = -1;
- 52 }
- 53
- 54 //填充客户端地址信息结构体
- 55 struct sockaddr_in cin;
- 56 cin.sin_family = AF_INET;
- 57 socklen_t socklen = sizeof(cin);
- 58
- 59
- 60 char cbuf[128] = ""; //给客户端用的容器
- 61 int nfd;
- 62 int res = 0; //接收poll返回的结果
- 63 while(1)
- 64 {
- 65 res = poll(fds,maxfd+1,-1);
- 66 if(res == -1)
- 67 {
- 68 perror("select");
- 69 return -1;
- 70 }
- 71 else if(res == 0)
- 72 {
- 73 continue;;
- 74 }
- 75 else if(res > 0) //说明检测到了有文件描述符对应的缓冲区的数据发生了改变
- 76 {
- 77 if(fds[0].revents == POLLIN) //表明有新的客户连接进来了
- 78 {
- 79 int nfd = accept(sfd,(struct sockaddr*)&cin,&socklen); //阻塞在此处,直到有客户端连接上来
- 80 if(nfd == -1) //增加这些错误的判断非常重要,可以帮助找到出现问题的地方
- 81 {
- 82 perror("accept");
- 83 return -1;
- 84 }
- 85
- 86 //将新的文件描述符加入到集合中
- 87 for(int i = 1;i < 1024;i++)
- 88 {
- 89 if( fds[i].fd == -1)
- 90 {
- 91 fds[i].fd = nfd;
- 92 fds[i].events = POLLIN;
- 93 break;
- 94 }
- 95 }
- 96
- 97 //更新最大的文件描述符
- 98 if(nfd > maxfd)
- 99 {
- 100 maxfd = nfd;
- 101 }
- 102 }
- 103
- 104 for(int i = 1;i <= maxfd;i++) //轮询客户端对应的文件描述符
- 105 {
- 106 if(fds[i].revents == POLLIN) //说明此文件描述符对应的客户端发送来了数据
- 107 {
- 108 int ret = read(fds[i].fd,cbuf,sizeof(cbuf));
- 109 if(ret == -1)
- 110 {
- 111 perror("read");
- 112 exit(-1);
- 113 }
- 114 else if(ret == 0)
- 115 {
- 116 printf("client closed\n");
- 117 close(fds[i].fd); //关闭对应的文件描述符
- 118 fds[i].fd = -1; //在fds中清空对应的文件描述符
- 119 }
- 120 else if(ret > 0)
- 121 {
- 122 printf("read buf = %s\n",cbuf);
- 123 write(fds[i].fd,cbuf,strlen(cbuf)+1);
- 124 }
- 125
- 126
- 127 }
- 128 }
- 129 }
- 130 }
- 131 //关闭所有套接字
- 132 close(sfd);
- 133
- 134 return 0;
- 135
- 1 #include
- 2 #define SERIP "192.168.228.165"
- 3 #define SERPORT 8888
- 4 #define CLIIP "192.168.228.165"
- 5 #define CLIPORT 6666
- 6
- 7 int main(int argc, const char *argv[])
- 8 {
- 9 //1、创建客户端用于通信的套接字
- 10 int cfd = socket(AF_INET, SOCK_STREAM, 0);
- 11 if(cfd == -1)
- 12 {
- 13 perror("socket error");
- 14 return -1;
- 15 }
- 16 printf("cfd = %d\n", cfd); //3
- 17
- 18
- 19 //2、绑定(可选)
- 20 //2.1、填充地址信息结构体
- 21 struct sockaddr_in cin;
- 22 cin.sin_family = AF_INET; //使用的是ipv4通信
- 23 cin.sin_port = htons(CLIPORT); //服务器端口号
- 24 cin.sin_addr.s_addr = inet_addr(CLIIP); //服务器IP地址
- 25 //2.2、绑定工作
- 26 if(bind(cfd, (struct sockaddr*)&cin, sizeof(cin)) == -1)
- 27 {
- 28 perror("bind error");
- 29 return -1;
- 30 }
- 31 printf("bind success\n");
- 32
- 33
- 34 //3、连接服务器
- 35 //3.1、填充服务器地址信息结构体
- 36 struct sockaddr_in sin;
- 37 sin.sin_family = AF_INET; //使用的是ipv4通信
- 38 sin.sin_port = htons(SERPORT); //服务器端口号
- 39 sin.sin_addr.s_addr = inet_addr(SERIP); //服务器IP地址
- 40
- 41 //3.2、连接服务器
- 42 if(connect(cfd, (struct sockaddr*)&sin, sizeof(sin)) ==-1)
- 43 {
- 44 perror("connect error");
- 45 return -1;
- 46 }
- 47 printf("connect success\n");
- 48
- 49 //4、收发数据(send、recv、read、write)
- 50 char buf[128] = "";
- 51 char rbuf[128] = "";
- 52
- 53 定义一个集合管理0号文件描述符和cfd
- 54 struct pollfd fds[2];
- 55
- 56 //将0号文件描述符放入
- 57 fds[0].fd = 0;
- 58 fds[0].events = POLLIN; //表明要进行读事件
- 59
- 60 //将cfd放入集合
- 61 fds[1].fd = cfd;
- 62 fds[1].events = POLLIN;
- 63
- 64 int res = 0; //接收poll返回的结果
- 65
- 66
- 67 while(1)
- 68 {
- 69 res = poll(fds, 2, -1); //第三个参数如果是负数,表明一直等待
- 70 if(res == -1)
- 71 {
- 72 perror("poll error");
- 73 return -1;
- 74 }else if(res == 0)
- 75 {
- 76 printf("time out\n");
- 77 return -1;
- 78 }
- 79
- 80
- 81 bzero(buf, sizeof(buf));
- 82 bzero(rbuf, sizeof(rbuf));
- 83
- 84
- 85
- 86 //判断是否是发送数据满足条件
- 87 if(fds[0].revents == POLLIN)
- 88 {
- 89 fgets(buf, sizeof(buf), stdin); //从标准输入中读取数据
- 90 buf[strlen(buf)-1] = '\0';
- 91
- 92 //将数据发送给服务器
- 93 send(cfd, buf, sizeof(buf), 0);
- 94
- 95 //如果输入的是quit则退出
- 96 if(strcmp(buf,"quit") == 0)
- 97 {
- 98 break;
- 99 }
- 100 }
- 101
- 102
- 103
- 104 //判断是否为接收数据满足条件
- 105 if(fds[1].revents == POLLIN)
- 106 {
- 107
- 108 //接收服务器发送来的消息
- 109 int res = recv(cfd, rbuf, sizeof(rbuf), 0);
- 110 if(res == 0)
- 111 {
- 112 printf("服务器已经关闭\n");
- 113 break;
- 114 }
- 115 printf("rbuf = %s\n", rbuf);
- 116 }
- 117
- 118 }
- 119
- 120 //5、关闭客户端套接字
- 121 close(cfd);
- 122
- 123
- 124 return 0;
- 125 }
- 126
- ~
