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
~