1,select服务端
1 #include<myhead.h>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); //418 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; //表明是ipv437 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 <myhead.h>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 <myhead.h>2 3 #define IP "192.168.228.165"4 #define PORT 88885 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 //手动放入sfd45 fds[0].fd = sfd;46 fds[0].events = POLLIN; //表明为读事件47 48 //将fds中其余元素初始化为-149 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<myhead.h>2 #define SERIP "192.168.228.165"3 #define SERPORT 88884 #define CLIIP "192.168.228.165"5 #define CLIPORT 66666 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); //317 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号文件描述符和cfd54 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
~