select的TCP服务器
主函数代码
1 #include <stdio.h>
2 #include <myhead.h>
3 #include "select.h"
4 #define PORT 9999 //1024~49151
5 #define IP "192.168.124.17"//ifconfig查找本机的IP地址
6
7 int main(int argc, const char *argv[])
8 {
9
10 //创建流式套接字文件
11
12 int sfd = socket(AF_INET,SOCK_STREAM,0);
13 if(sfd <0)
14 {
15 fprintf(stderr,"line:%d",__LINE__);
16 perror("socket");
17 return -1;
18 }
19 printf("创建流式套接字文件成功 sfd=%d __%d__\n",sfd,__LINE__);
20
21 //允许端口号被快速重复使用
22 int reuse = 1;
23 if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))<0)
24 {
25 fprintf(stderr,"line:%d",__LINE__);
26 perror("setsockopt");
27 return -1;
28
29 }
30
31 //填充服务器的地址信息结构体,真实的地址信息结构体根据地址族制定
32 //AF_INET --->man 7 ip
33 struct sockaddr_in sin;
34 sin.sin_family = AF_INET;//必须填AF_INET;
35 sin.sin_port = htons(PORT);//端口号的网络字节序
36 sin.sin_addr.s_addr = inet_addr(IP);
37 //绑定服务器自身的地址信息--》必须绑定
38 if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))<0)
39 {
40 fprintf(stderr,"line:%d",__LINE__);
41 perror("bind");
42 return -1;
43 }
44 printf("bind success __%d__\n",__LINE__);
45 //将套接字设置为被动监听状态
46 if(listen(sfd,128)<0)
47 {
48 fprintf(stderr,"line:%d",__LINE__);
49 perror("listen");
50 return -1;
51 }
52 printf("listen success __%d__\n",__LINE__);
53
54 //创建一个读集合
55 //由于读集合中的成员是一个+整形数组,若不初始化则集合中为随机值
56 //必须清空
57 fd_set readfds,tempfds;
58 FD_ZERO(&readfds);
59
60 //要监测的文件描述符加入到读集合中
61 FD_SET(0,&readfds);
62 FD_SET(sfd,&readfds);
63 int maxfd = sfd;
64 int s_res;
65 char buf[128];
66 ssize_t res;
67 int newfd;
68 struct sockaddr_in saveCin[1024-3];//将连接成功的客户端信息存储到newfd-3的下标位置
69
70 while(1)
71 {
72 tempfds = readfds;
73 //监测集合中的文件描述符是否准备就绪
74 s_res = select(maxfd+1,&tempfds,NULL,NULL,NULL);
75 if(s_res <0)
76 {
77 fprintf(stderr,"line:%d",__LINE__);
78 perror("select");
79 return -1;
80 }
81 else if(0 == s_res)
82 {
83 printf("time out...\n");
84 break;
85 }
86 //判断集合中剩下的文件描述符是谁
87 for(int i =0 ;i<=maxfd;i++)
88 {
89
90 if(FD_ISSET(i,&tempfds)==0)
91 {
92 continue;
93 }
94
95 if(0==i)
96 {
97 printf("触发键盘输入事件\n");
98 deal_KeyboarEvent(readfds,saveCin);
99
100 }
101 else if(sfd == i)
102 {
103 printf("触发客服端连接事件\n");
104 deal_CliConnectEvent(sfd,saveCin,&readfds,&maxfd);
105 }
106 else
107
108 {
109 printf("触发客服端交互事件\n");
110 deal_CliTalkEvent(i,saveCin,&readfds,&maxfd);
111 }
112 }
113 }
114
115 //关闭文件描述符
116 close(sfd);
117 return 0;
118 }
~
~
~
~
~
功能函数
1 #include <stdio.h>
2 #include <myhead.h>
3
4 /*
5 *function:处理客服端交互事件
6 *@param[in]
7 *@parm[out]
8 *@return
9 *
10 */
11 int deal_CliTalkEvent(int talkfd,struct sockaddr_in saveCin[],fd_set *preadfds,int *pmaxfd )
12 {
13 char buf[128]="";
14 bzero(buf,sizeof(buf));
15
16 //接收
17 ssize_t res = recv(talkfd ,buf,sizeof(buf),0);
18 if(res<0)
19 {
20 fprintf(stderr,"line:%d",__LINE__);
21 perror("recv");
22 return -1;
23 }
24 else if(0==res)
25 {
26 printf("[%s:%d] 客服端下线 newfd=%d __%d__\n",\
27 inet_ntoa(saveCin[talkfd-3].sin_addr),ntohs(saveCin[talkfd-3].sin_port),talkfd,__LINE__);
28 close(talkfd);
29 FD_CLR(talkfd,preadfds); //将newfd从readfds集合中剔除
30
31 for(;*pmaxfd>0;*pmaxfd--)
32 {
33 if(FD_ISSET(*pmaxfd,preadfds))
34 {
35 break;
36 }
37 }
38 return -1;
39 }
40 printf("[%s:%d] 客服端连接成功 newfd=%d %s __%d__\n",\
41 inet_ntoa(saveCin[talkfd-3].sin_addr),ntohs(saveCin[talkfd-3].sin_port),talkfd,buf,__LINE__);
42
43 //发送
44 strcat(buf,"*~*");
45 if( send(talkfd,buf,sizeof(buf),0)<0)
46 {
47 fprintf(stderr,"line:%d",__LINE__);
48 perror("send");
49 return -1;
50
51 }
52 printf("send success __%d__\n",__LINE__);
53
54 return 0;
55
56 }
57 /*
58 *function:处理客服端连接事件
59 *@param[in]
60 *@parm[out]
61 *@return
62 *
63 */
64 int deal_CliConnectEvent(int psfd,struct sockaddr_in* saveCin,fd_set* preadfds,int *pmaxfd)
65 {
66 struct sockaddr_in cin;
67 socklen_t addrlen = sizeof(cin);
68
69 //会从已完成连接队列的对头获取一个客户端的信息,生成一个新的文件描述符
70 //accept(sfd,NULL,NULL);
71 int newfd = accept(psfd,(struct sockaddr*)&cin,&addrlen);
72 if(newfd <0)
73 {
74 fprintf(stderr,"line:%d",__LINE__);
75 perror("accept");
76 return -1;
77 }
78
79 printf("[%s:%d] 客服端连接成功 newfd=%d __%d__\n",\
80 inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd,__LINE__);
81
82 saveCin[newfd - 3] =cin;
83 FD_SET (newfd ,preadfds); //将生成的newfd添加到集合中
84 *pmaxfd = newfd>*pmaxfd?newfd:*pmaxfd;//更新maxfd
85
86 return 0;
87 }
88 /*
89 *function:键盘输入事件
90 *@param[in]
91 *@parm[out]
92 *@return
93 *
94 */
95 int deal_KeyboarEvent(fd_set preadfds,struct sockaddr_in psaveCin[])
96
97 {
98
99 int sndfd;
00 char buf[128];
01 int res = scanf("%d %s",&sndfd,buf);//从终端输入一个客户端对应的文件描述符
02 while (getchar()!='\n' );
03 if(res != 2)
04 {
05
06 printf("请输入正确的格式:fd string\n");
07 return -1;
08
09 }
10 if(sndfd<0||sndfd>1023||FD_ISSET(sndfd,&preadfds)== 0)
11 {
12 printf("请输入正确的文件描述符\n");
13 return -1;
14
15 }
16 if(send(sndfd,buf,sizeof(buf),0)<0)
17 {
18 perror("send");
19 return -1;
20
21 }
22 printf("发送给[%s:%d] fd=%d客服端成功 __%d__\n",\
23 inet_ntoa(psaveCin[sndfd-3].sin_addr),ntohs(psaveCin[sndfd-3].sin_port),sndfd,__LINE__);
24 return 0;
25 }
现象
poll的客户端
1 #include <stdio.h>
2 #include <myhead.h>
3
4 #define CLI_PORT 7777 //1024~49151
5 #define CLI_IP "192.168.124.17"//ifconfig查找本机的IP地址
6
7
8
9 int main(int argc, const char *argv[])
10 {
11 if(argc <3)
12 {
13 printf("请在命令行传入服务器的port和ip\n");
14 return -1;
15 }
16
17 //创建流式套接字
18
19 int cfd = socket(AF_INET,SOCK_STREAM,0);
20 if(cfd <0)
21 {
22 fprintf(stderr,"line:%d",__LINE__);
23 perror("socket");
24 return -1;
25 }
26 printf("创建流式套接字文件成功 cfd=%d __%d__\n",cfd,__LINE__);
27 //绑定客服端自身的地址信息结构体--->非必须绑定
28 //若不绑定,则操作系统会自动给客服端绑定客服端所运行主机的IP及随机端口:
29 //49152~65535
30
31 //填充服务器的地址信息,给connect函数连接服务器的时候使用
32 struct sockaddr_in sin;
33 sin.sin_family = AF_INET;//必须填AF_INET;
34 sin.sin_port = htons(atoi(argv[1]));//服务器要绑定的端口号
35 sin.sin_addr.s_addr = inet_addr(argv[2]);//服务器绑定的IP地址
36 //连接指定的服务器
37 if(connect(cfd,(struct sockaddr*)&sin,sizeof(sin))<0)
38 {
39 fprintf(stderr,"line:%d",__LINE__);
40 perror("connect");
41 return -1;
42 }
43 printf("connect server[%s:%s] 成功 __%d__\n",argv[2],argv[1],__LINE__);
44
45
46 //创建集合
47 struct pollfd fds[2];
48 fds[0].fd = 0; //指定监测0号文件描述符
49
50 fds[0].events = POLLIN; //指定监测读事件
51
52 fds[1].fd = cfd;
53 fds[1].events = POLLIN;
54
55
56 char buf[128]= "";
57 ssize_t res;
58 int p_res;
59 while(1)
60 {
61
62 //poll,让内核监测集合中是否有文件描述符准备就绪
63 p_res = poll(fds,2,-1);
64 if(p_res<0)
65 {
66 fprintf(stderr,"line:%d",__LINE__);
67 perror("poll");
68 return -1;
69 }
70 else if(0 == p_res)
71 {
72 printf("time out ...\n");
73 break;
74 }
75 //能运行到当前位置,则代表集合中有文件描述符产生事件了
76 //需要遍历判断集合中文件描述符实际产生的事件中是否有POLLIN事件
77 //需要将revents & POLLIN判断结构是否为0
78 if((fds[0].revents&POLLIN))
79 {
80 fgets(buf,sizeof(buf),stdin);
81 buf[strlen(buf)-1]='\0';
82
83
84
85 //发送
86 if( send(cfd,buf,sizeof(buf),0)<0)
87 {
88 fprintf(stderr,"line:%d",__LINE__);
89 perror("send");
90 return -1;
91
92 }
93 printf("send success __%d__",__LINE__);
94
95 }
96 if(fds[1].revents &POLLIN)
97 {
98 //接收
99 bzero(buf,sizeof(buf));
00 res = recv(cfd,buf,sizeof(buf),0);
01 if(res<0)
02 {
03
04
05 fprintf(stderr,"line:%d",__LINE__);
06 perror("recv");
07 return -1;
08 }
09 else if(0==res)
10 {
11 printf("服务器下线 __%d__",__LINE__);
12 break;
13 }
14 printf(":%s __%d__\n",buf,__LINE__);
15
16 }
17
18
19 }
20 //关闭文件描述符
21 close(cfd);
22 return 0;
23 }
现象