多线程并发服务器;select的TCP服务器;select的TCP客户端

多线程并发服务器

  1 #include <stdio.h>
  2 #include <sys/types.h>
  3 #include <sys/socket.h>
  4 #include <unistd.h>
  5 #include <arpa/inet.h>
  6 #include <netinet/in.h>
  7 #include <string.h>
  8 #include <stdlib.h>
  9 #include <sys/wait.h>
 10 #include <pthread.h>
 11 
 12 #define IP "192.168.124.47"     //ifconfig查看
 13 #define PORT 8888              //端口号的网络字节序(在1014-49151之间)
 14 #define ERR_MSG(msg) do{\
 15     fprintf(stderr,"line=%d \n",__LINE__);\
 16     perror(msg);\
 17 }while(0)
 18 struct msg //需要传递给分支线程的参数
 19 {
 20     int newsfd;
 21     struct sockaddr_in cin;
 22 };
 23 void * deal_cli_msg(void * arg);
 24 
 25 int main(int argc, const char *argv[])
 26 {
 27 
 28     //创建流式套接字
 29     int sfd = socket(AF_INET,SOCK_STREAM,0);
 30     if (sfd < 0)
 31     {
 32         ERR_MSG("socket");
 33         return -1;
 34     }
 35     printf("流式套接字创建成功\n");
 36     //允许端口被快速使用
 37     int reuse = 1;
 38     if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))<0)
 39     {
 40         ERR_MSG("setsockopt");
 41         return -1;
 42     }
 43     printf("允许端口快速重用\n");
 44     //填充服务器的地址信息结构体,给bind函数使用
 45     //真实的地址信息结构体根据地址族制定,AF_INET-->main 7 ip
 46     struct sockaddr_in sin;
 47     sin.sin_family      = AF_INET;//必须填AF_INET(代表是流式套接字)
 48     sin.sin_port        = htons(PORT);//端口号的网络字节序
 49     //(htons代表是将主机字节序转换为网络字节序)
 50     sin.sin_addr.s_addr = inet_addr(IP);//本机IP,ifconfig查看,
 51     //inet_addr是.十进制转换为网络字节序
 52 
 53     //绑定服务器的地址信息,必须绑定
 54     if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin)) < 0)
 55     {
 56         ERR_MSG("bind");                                                                       
 57         return -1;
 58     }
 59     printf("绑定成功\n");
 60     //将套接字设置为被监听状态
 61     if (listen(sfd,128)< 0)
 62     {
 63         ERR_MSG("listen");
 64         return -1;
 65     }
 66     printf("监听成功\n");
 67     //获取连接成功的客户端信息,生成一个新的套接字文件描述符
 68     struct sockaddr_in cin;
 69     socklen_t addrlen = sizeof(cin);
 70 
 71     int newsfd;
 72     pthread_t tid;
 73     struct msg clifo;
 74     while(1)
 75     {
 76         newsfd = accept(sfd,(struct sockaddr * )&cin,&addrlen);
 77         if(newsfd < 0)
 78         {
 79             ERR_MSG("accept");
 80             return -1;
 81         }
 82         printf("[%s %d] newsfd=%d 客户端连接成功\n",inet_ntoa(cin.sin_addr),\
 83                 ntohs(cin.sin_port),newsfd);
 84         clifo.newsfd = newsfd;
 85         clifo.cin = cin;
 86         if(pthread_create(&tid,NULL,deal_cli_msg,(void *)&clifo) != 0)
 87         {
 88             fprintf(stderr,"pthread_create failed __%d__\n",__LINE__);
 89             break;
 90         }
 91         pthread_detach(tid);
 92     }
 93     //关闭文件描述符
 94     if(close(sfd)<0)
 95     {
 96         ERR_MSG("close");
 97         return -1;
 98     }
 99     printf("sfd关闭成功\n");
100 
101     return 0;
102 }
103 void * deal_cli_msg(void *arg)
104 {
105     char buf[128]="";
106     ssize_t res;
107     int newsfd = ((struct msg*)arg)->newsfd;
108     struct sockaddr_in cin = ((struct msg*)arg)->cin;
109 
110     while(1)
111     {
112         //int newsfd = ((struct msg*)arg)->newsfd;
113         //struct sockaddr_in cin = ((struct msg*)arg)->cin;
114 
115         bzero(buf,sizeof(buf));
116         //接收
117         res = recv(newsfd,buf,sizeof(buf),0);
118         if(res < 0)
119         {
120             ERR_MSG("recv");
121             break;
122         }
123         else if(res == 0)
124         {
125             printf("[%s %d] newsfd=%d 客户端下线\n",inet_ntoa(cin.sin_addr),\
126                     ntohs(cin.sin_port),newsfd);
127             break;
128         }
129         printf("[%s %d] newsfd = %d ; %s\n",inet_ntoa(cin.sin_addr),\
130                 ntohs(cin.sin_port),newsfd,buf);
131 
132         //发送
133         strcat(buf,"*__*");
134         if(send(newsfd,buf,sizeof(buf),0)<0)
135         {
136             ERR_MSG("send");
137             break;
138         }
139         printf("发送成功\n");
140     }
141     close(newsfd);
142     pthread_exit(NULL);
143 }
144 

运行结果

select的TCP服务器

  1 #include <stdio.h>
  2 #include <sys/types.h>
  3 #include <sys/socket.h>
  4 #include <unistd.h>
  5 #include <arpa/inet.h>
  6 #include <netinet/in.h>
  7 #include <string.h>
  8 #include <sys/time.h>
  9 #include <sys/select.h>
 10 
 11 
 12 #define IP "192.168.124.47"     //ifconfig查看
 13 #define PORT 8888              //端口号的网络字节序(在1014-49151之间)
 14 #define ERR_MSG(msg) do{\
 15     fprintf(stderr,"line=%d \n",__LINE__);\
 16     perror(msg);\
 17 }while(0)
 18 
 19 int deal_keyboard_msg(fd_set readfds);
 20 int deal_cliConnect(int sfd,struct sockaddr_in  psavecin[],fd_set * preadfds,int *pmaxfd);
 21 int deal_cliRecvSend(int j,struct sockaddr_in savecin[],fd_set * preadfds,int * pmaxfd);
 22 
 23 int main(int argc, const char *argv[])
 24 {
 25     //创建流式套接字
 26     int sfd = socket(AF_INET,SOCK_STREAM,0);
 27     if (sfd < 0)
 28     {
 29         ERR_MSG("socket");
 30         return -1;
 31     }
 32     printf("流式套接字创建成功 sfd=%d\n",sfd);
 33 
 34     //允许端口被快速使用
 35     int reuse = 1;
 36     if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))<0)
 37     {
 38         ERR_MSG("setsockopt");
 39         return -1;
 40     }
 41     printf("允许端口快速重用\n");
 42     //填充服务器的地址信息结构体,给bind函数使用
 43     //真实的地址信息结构体根据地址族制定,AF_INET-->main 7 ip
 44     struct sockaddr_in sin;
 45     sin.sin_family      = AF_INET;//必须填AF_INET(代表是流式套接字)
 46     sin.sin_port        = htons(PORT);//端口号的网络字节序
 47                                         //(htons代表是将主机字节序转换为网络字节序)
 48     sin.sin_addr.s_addr = inet_addr(IP);//本机IP,ifconfig查看,
 49                                         //inet_addr是.十进制转换为网络字节序
 50 
 51     //绑定服务器的地址信息,必须绑定
 52     if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin)) < 0)
 53     {
 54         ERR_MSG("bind");
 55         return -1;
 56     }
 57     printf("绑定成功\n");
 58 
 59     //将套接字设置为被监听状态
 60     if (listen(sfd,128)< 0)
 61     {
 62         ERR_MSG("listen");
 63         return -1;
 64     }
 65     printf("监听成功\n");
 66     /*
 67     //获取连接成功的客户端信息,生成一个新的套接字文件描述符
 68     struct sockaddr_in cin;
 69     socklen_t addrlen = sizeof(cin);
 70     */
 71 
 72 
 73     //创建读集合
 74     //-->fd——set本质上是一个结构体,其中只有一个整型数组
 75     //若不清空,回事一堆随机值,有可能会随机到有效的文件描述符编号
 76     //但是有效文件描述符编号不要检测,从而导致select异常接触阻塞
 77     fd_set readfds,tmpfds;
 78     FD_ZERO(&readfds); // 清空集合
 79 
 80     //将需要的文件描述符添加到集合中
 81     FD_SET(0,&readfds);
 82     FD_SET(sfd,&readfds);
 83 
 84     int maxfd = sfd;  // 集合中最大的文件描述符
 85 
 86     int s_res;
 87     int newsfd = -1;
 88     struct sockaddr_in savecin[1024];//将客户端的地址信息存储到对应文件描述符下标位置
 89 
 90     while(1)
 91     {
 92         tmpfds = readfds;
 93 
 94         //让内核监测集合中的文件描述符是否准备就绪 
 95         s_res = select(maxfd+1,&tmpfds,NULL,NULL,NULL);
 96         if(s_res<0)
 97         {
 98             ERR_MSG("select");
 99             return -1;
100         }
101         else if(s_res == 0)
102         {
103             printf("time out....\n");
104             break;
105         }
106         printf("__%d__\n",__LINE__);
107 
108         //能运行到当前位置说明集合中有文件描述符准备就绪
109         //判断集合中哪个文件描述符准备就绪,执行对应的处理函数 
110         /*
111          * 集合中会只能存下产生时间的文件描述符
112          * 0号准备就绪,则集合中会只剩下0
113          * sfd准备就绪,则集合中会只剩下sfd
114          * 若0和sfd均准备就绪,则集合中会剩下0和sfd
115          * 所以只要判断集合中剩下哪个文件描述符,就代表该文件描述符准备就绪
116          */
117         int i;
118         for(i = 0;i <= maxfd;i++)
119         {
120             //判断i代表的文件描述符是否在集合中
121             if(FD_ISSET(i,&tmpfds) == 0)
122             {
123                 continue;
124             }
125             //运行到这个位置,说明i代表的文件描述符在集合中
126             if( i == 0)
127             {
128                 printf("触发键盘输入事件\n");
129                 deal_keyboard_msg(readfds);
130             }
131             else if(sfd == i)
132             {
133                 printf("触发客户端连接事件\n");
134                 deal_cliConnect(sfd,savecin,&readfds,&maxfd);
135             }
136             else
137             {
138                 printf("触发客户端交互事件\n");
139                 deal_cliRecvSend(i,savecin,&readfds,&maxfd);
140             }
141         }
142 
143     }
144 
145 //关闭文件描述符
146     if(close(sfd)< 0)
147     {
148         ERR_MSG("close");
149         return -1;
150     }
151     printf("sfd关闭成功 \n");
152     return 0;
153 }
154 
155 //键盘输入事件
156 int deal_keyboard_msg(fd_set readfds)
157 {
158     int sndfd;
159     char buf[128];
160     int res = scanf("%d %s",&sndfd,buf);
161     while(getchar()!='\n');
162     if(res != 2)
163     {
164         printf("请输入正确的格式:fd string \n");
165         return -1;
166     }
167     //判断键盘输入文件描述符是否合法
168     if(sndfd <= 3 || FD_ISSET(sndfd,&readfds) == 0)
169     {
170         printf("sndfd = %d错误,请输入合法的文件描述符\n",sndfd);
171         return -1;
172     }
173     if(send(sndfd,buf,sizeof(buf),0)<0)
174     {
175         ERR_MSG("send");
176         return -1;
177     }
178     return 0;
179 
180 }
181 //处理客户端连接事件
182 int deal_cliConnect(int sfd,struct sockaddr_in  psavecin[],fd_set * preadfds,int *pmaxfd)
183 {
184     struct sockaddr_in cin;          //存储客户端的信息
185     socklen_t addrlen = sizeof(cin);
186     int newsfd =accept(sfd,(struct sockaddr *)&cin,&addrlen);
187     if(newsfd < 0)
188     {
189         ERR_MSG("accept");
190         return -1;
191     }
192     printf("[%s %d] newsfd=%d 客户端连接成功\n",inet_ntoa(cin.sin_addr),\
193             ntohs(cin.sin_port),newsfd);
194 
195     psavecin[newsfd] = cin;
196 
197     FD_SET(newsfd,preadfds);//客户端连接成功后若要检测客户端的交互事件
198 
199     //需要将newsfd添加到集合中,让内核检测
200     *pmaxfd =*pmaxfd>newsfd?*pmaxfd:newsfd;//跟新maxfd中的值
201     return 0;                                                                                                                                  
202 }
203 
204 //处理客户端交互事件
205 int deal_cliRecvSend(int j,struct sockaddr_in savecin[],fd_set * preadfds,int * pmaxfd)
206 {
207     char buf[128] ="";
208     ssize_t res ;
209     bzero(buf,sizeof(buf));
210     //接收
211     res = recv(j,buf,sizeof(buf),0);
212     if(res < 0)
213     {
214         ERR_MSG("recv");
215         return -1;
216     }
217     else if(res == 0)
218     {
219         printf("[%s %d] newsfd=%d 客户端下线\n",inet_ntoa(savecin[j].sin_addr),\
220                 ntohs(savecin[j].sin_port),j);
221 
222         //关闭文件描述符
223         close(j);
224         //将文件描述符从集合中删除
225         FD_CLR(j,preadfds);
226         //更新maxfd,进循环的条件
227         /*
228            while(maxfd > 0 && FD_ISSET(maxfd,&readfds)==0)
229            {
230            maxfd--;
231            }*/
232         while(FD_ISSET(*pmaxfd,preadfds)==0 && (*pmaxfd)-- > 0);
233         return 0;
234     }
235     printf("[%s %d] newsfd = %d  %s\n",inet_ntoa(savecin[j].sin_addr),\
236             ntohs(savecin[j].sin_port),j,buf);
237     //发送
238     strcat(buf,"*__*");
239     if(send(j,buf,sizeof(buf),0)<0)
240     {
241         ERR_MSG("send");
242         return -1;
243     }
244     printf("发送成功\n");
245 
246     return 0;
247 
248 }
~                                                                                                                                                  

运行结果

 

select的TCP客户端

  1 #include <stdio.h>
  2 #include <sys/types.h>
  3 #include <sys/socket.h>
  4 #include <unistd.h>
  5 #include <arpa/inet.h>
  6 #include <netinet/in.h>
  7 #include <string.h>
  8 
  9 #define IP "192.168.124.47"     //ifconfig查看
 10 #define PORT 8888              //端口号的网络字节序(在1014-49151之间)
 11 #define ERR_MSG(msg) do{\
 12     fprintf(stderr,"line=%d \n",__LINE__);\
 13     perror(msg);\                                                                                                                 
 14 }while(0);
 15 
 16 int main(int argc, const char *argv[])
 17 {
 18     //创建流式套接字
 19     int cfd = socket(AF_INET,SOCK_STREAM,0);
 20     if (cfd < 0)
 21     {
 22         ERR_MSG("socket");
 23         return -1;
 24     }
 25     printf("流式套接字创建成功\n");
 26     /*
 27     //允许端口被快速使用
 28     int reuse = 1;
 29     if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))<0)
 30     {
 31     ERR_MSG("setsockopt");
 32     return -1;
 33     }
 34     printf("允许端口快速重用\n");
 35     */
 36     //填充服务器的地址信息结构体,给connect函数使用
 37     //代表需要连接到哪个服务器上,服务器绑定的是什么IP和PORT,这里就应该填哪个服务器的IP
 38     struct sockaddr_in sin;
 39     sin.sin_family      = AF_INET;//必须填AF_INET(代表是流式套接字)
 40     sin.sin_port        = htons(PORT);//端口号的网络字节序,服务器绑定的PORT
 41     //(htons代表是将主机字节序转换为网络字节序)
 42     sin.sin_addr.s_addr = inet_addr(IP);//服务器绑定的IP,ifconfig查看,
 43     //inet_addr是.十进制转换为网络字节序
 44     //连接指定的服务器
 45     if(connect(cfd,(struct sockaddr *)&sin,sizeof(sin))<0)
 46     {
 47         ERR_MSG("connect");
 48         return -1;
 49     }
 50     printf("连接服务器[%s : %d]成功\n",IP,PORT);
 51 
 52     //创建读集合
 53     fd_set readfds;
 54     FD_ZERO(&readfds);//清空集合
 55 
 56     //将需要的文件描述符添加到集合中
 57     FD_SET(0,&readfds);
 58     FD_SET(cfd,&readfds);
 59 
 60     char buf[128]="";
 61     ssize_t res;
 62     int s_res;
 63     while(1)
 64     {
 65         s_res = select(cfd+1,&readfds,NULL,NULL,NULL);
 66         if(s_res <0)
 67         {
 68             ERR_MSG("select");
 69             return -1;
 70         }
 71         else if(s_res == 0)
 72         {
 73             printf("time out ...\n");
 74             break;
 75         }
 76         printf("__%d__\n",__LINE__);
 77 
 78         int i;
 79         for(i = 0; i< cfd;i++)
 80         {
 81             if(FD_ISSET(i,&readfds) == 0)//判断i代表的文件描述符是否在集合中
 82             {
 83                 continue;
 84             }
 85             // 运行到这个位置说明i代表的文件描述符在集合中
 86             if(i ==0)
 87             {
 88                 printf("触发键盘输入事件\n");
 89                 bzero(buf,sizeof(buf));
 90 
 91                 //发送
 92                 printf("请输入>>>");
 93                 fgets(buf,sizeof(buf),stdin);
 94                 buf[strlen(buf)-1] = '\0';
 95                 if(send(cfd,buf,sizeof(buf),0)<0)
 96                 {
 97                     ERR_MSG("send");
 98                     return -1;
 99                 }
100                 printf("发送成功\n");
101             }
102             else if( i == cfd)
103             {
104                 printf("触发服务器交互事件\n");
105                 bzero(buf,sizeof(buf));
106                 res = recv(cfd,buf,sizeof(buf),0);
107                 if(res < 0)
108                 {
109                     ERR_MSG("recv");
110                     return -1;
111                 }
112                 else if(res == 0)
113                 {
114                     printf("[%s %d] cfd=%d 服务器下线\n",IP,PORT,cfd);
115                     break;
116                 }
117                 printf("[%s %d] cfd = %d ; %s\n",IP,PORT,cfd,buf);
118             }
119 
120         }
121     }
122 
123     //关闭文件描述符
124     if(close(cfd)<0)
125     {
126         ERR_MSG("close");
127         return -1;
128     }
129     printf("newsfd关闭成功\n");
130 
131     return 0;
132 }

运行结果

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值