c语言实现io多路复用(select),进程,线程并发服务器
创始人
2024-11-06 16:07:58

io多路复用(select)代码

#include #include  #define PORT 8888  #define IP   "192.168.250.100" int main(int argc, char const *argv[]) {         //创建套接字     int sfd = socket(AF_INET, SOCK_STREAM, 0);     if(sfd == -1)     {         perror("socket error");         return -1;     }     //端口重用     int reuse = 1;     if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1)     {         perror("setsockopt error");         return -1;     }     printf("设置端口快速重用成功 _%d_ %s_ %s_\n", __LINE__, __FILE__, __func__);          struct sockaddr_in sin;     sin.sin_family     = AF_INET;         //表明是ipv4     sin.sin_port     = htons(PORT);        //端口号     sin.sin_addr.s_addr = inet_addr(IP);     //IP地址     //绑定端口     if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin))==-1)     {         perror("bind error");         return -1;     }     printf("bind success _%d_ %s_ %s_\n", __LINE__, __FILE__, __func__);     //监听模式     if(listen(sfd, 128) == -1)     {         perror("listen error");         return -1;     }     printf("listen success _%d_ %s_ %s_\n", __LINE__, __FILE__, __func__);     struct sockaddr_in cin;             //客户端地址信息结构体     cin.sin_family     = AF_INET;     socklen_t socklen = sizeof(cin);          //客户端地址信息的大小     //定义文件描述符集合     fd_set readfds, tempfds;     //清空readfds     FD_ZERO(&readfds);     //将套接字和标准输入放入集合     FD_SET(sfd,&readfds);     FD_SET(0,&readfds);     //定义信息容器     char buf[128] = "";     int res = 0;             //接收select的返回值     int newfd = -1;          //存放用于最新连接客户端的套接字     int maxfd = sfd;          //定义控制select函数中最大文件描述符          struct sockaddr_in saveCin[1024];       //用于存放客户端地址信息结构体 while (1)    {             将集合内容复制一份         tempfds = readfds;          使用select阻塞等待集合中的文件描述符有事件产生         res = select(maxfd+1, &tempfds, NULL, NULL, NULL);         if(res == -1)         {             perror("select error");             return -1;         }else if(res == 0)         {             printf("time out\n");             return -1;         }         for(int i=0; i<=maxfd; i++)         {                          if(!FD_ISSET(i, &tempfds))             {                 continue;             }             if( i == sfd)             {                 newfd = accept(sfd, (struct sockaddr*)&cin, &socklen);                 if(newfd == -1)                 {                     perror("accept error");                     return -1;                 }                 printf("accept success _%d_ %s_ %s_\n", __LINE__, __FILE__, __func__);                 //将新的套接字放入select集合                            FD_SET(newfd , &readfds);                 //更新maxfd                 if(newfd > maxfd)                 {                     maxfd = newfd;                 }                 saveCin[newfd] = cin;             }             else if(i == 0 )             {                 char buf1[128] = "";                 bzero(buf, sizeof(buf));                 fgets(buf, sizeof(buf), stdin);                        buf[strlen(buf)-1]='\0';                 printf("终端输入:%s\n", buf);                 sprintf(buf1, "%s%s", "系统消息:", buf);                 //将数据发送给所有客户端,群发功能相当于将整个套接字集中的每一可客户端都发一遍                 for(int j=4; j<=maxfd; j++)                 {                     send(j, buf1,sizeof(buf1), 0);                 }              }else{                  //收发数据使用newfd完成通信                 char buf[128] = "";                 //清空字符串                 bzero(buf, sizeof(buf));                 int ret = recv(i, buf, sizeof(buf), 0);        //从套接字中读取客户端发来的消息                  //判断收到的结果                 if(ret == 0)                 {                     printf("客户端已经下线\n");                         close(i);             //关闭通信的套接字                      将当前的文件描述符从集合中删除                     FD_CLR(i, &readfds);                      更新maxfd                     for(int j=maxfd; j>=0; j--)                     {                         //判断当前的j是否在集合中,如果在,则为maxfd                         if(FD_ISSET(j, &readfds))                         {                             maxfd = j;                             break;                         }                     }                     continue;           //继续判断下一个                 }else if(ret < 0)                 {                     perror("recv error");                     return -1;                 }                  printf("[%s:%d]:%s\n", inet_ntoa(saveCin[i].sin_addr), ntohs(saveCin[i].sin_port), buf);                  send(i, buf, sizeof(buf), 0);               }          }         }        return 0; }

io多路复用(select)结果图

进程代码

#include #define PORT 8888  #define IP   "192.168.250.100" //处理客户端请求 int cli_msg(int newfd,struct sockaddr_in cin) {     char buf[128]="";     while (1)     {         bzero(buf, sizeof(buf));         int res = recv(newfd, buf, sizeof(buf), 0);         if (res==0)         {            printf("客户端已经下线\n");            break;         }else if(res<0)         {             perror("recv error");             return -1;         }         printf("[%s:%d]:%s\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), buf);         send(newfd, buf, sizeof(buf), 0);      }     close(newfd);        return 0;      } //回收僵尸进程 void handler(int signo) {     if(signo == SIGCHLD)     {         while(waitpid(-1, NULL, WNOHANG) > 0);            } }  int main(int argc, char const *argv[]) {     //创建套接字     int sfd = socket(AF_INET, SOCK_STREAM, 0);     if(sfd == -1)     {         perror("socket error");         return -1;     }     //设置端口重用     int reuse = 1;     if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1)     {         perror("setsockopt error");         return -1;     }     printf("设置端口快速重用成功 _%d_ %s_ %s_\n", __LINE__, __FILE__, __func__);          //设定地址信息结构体     struct sockaddr_in sin;     sin.sin_family     = AF_INET;         //表明是ipv4     sin.sin_port     = htons(PORT);        //端口号     sin.sin_addr.s_addr = inet_addr(IP);     //IP地址     //绑定地址     if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin))==-1)     {         perror("bind error");         return -1;     }     printf("bind success _%d_ %s_ %s_\n", __LINE__, __FILE__, __func__);     //开启监听     if(listen(sfd, 128) == -1)     {         perror("listen error");         return -1;     }      printf("listen success _%d_ %s_ %s_\n", __LINE__, __FILE__, __func__);      struct sockaddr_in cin;             //客户端地址信息结构体     cin.sin_family     = AF_INET;     socklen_t socklen = sizeof(cin);          //客户端地址信息的大小          pid_t pid;      //绑定进程的信号,信号触发就执行handler函数     if(signal(SIGCHLD, handler) == SIG_ERR)     {         perror("signal error");         return -1;     }     while (1)     {         //阻塞接收客户端请求         int newfd = accept(sfd, (struct sockaddr*)&cin, &socklen);         if(newfd == -1)         {             perror("accept error");             return -1;         }         //创建子进程         pid = fork();          //父进程负责连接         if(pid > 0)         {             //父进程不用newfd,回收newfd             close(newfd);          }          //子进程负责请求响应          else if(pid==0)         {             //子进程不用sfd,就回收sfd             close(sfd);             cli_msg(newfd, cin);             //回收子进程             exit(EXIT_SUCCESS);         }         else         {             perror("fork error");             return -1;         }     }     //关闭所有套接字并关闭监听     close(sfd);          return 0; } 

进程的结果图

线程代码

 

#include #define PORT 8888  #define IP   "192.168.250.100" //定义存储客户端地址信息结构体和连接请求套接字的结构体 struct msg_info{   int newfd;   struct sockaddr_in cin; };  //定义线程体 void *task1(void *arg) {   //将外部值传入    int newfd=((struct msg_info*)arg)->newfd;    struct sockaddr_in cin=((struct msg_info*)arg)->cin;    //定义收发容器    char buf[128]="";     while (1)    {      //响应客户端请求      bzero(buf,sizeof(buf));      int res=recv(newfd,buf,sizeof(buf),0);      if(res==0)      {       printf("客户端已经下线");       break;       }else if(res>0)      {      printf("[%s:%d]:%s\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), buf);      send(newfd,buf,sizeof(buf),0);      }else{      perror("recv error:");      return NULL;      }    }     //关闭连接套接字     close(newfd);     //退出线程     pthread_exit(NULL); } int main(int argc, char const *argv[]) {      pthread_t tid;     int sfd=socket(AF_INET,SOCK_STREAM,0);     int reuse = 1;     if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1)     {         perror("setsockopt error");         return -1;     }     printf("设置端口快速重用成功 _%d_ %s_ %s_\n", __LINE__, __FILE__, __func__);      //绑定的地址信息结构体     struct sockaddr_in sin;     sin.sin_family     = AF_INET;         //表明是ipv4     sin.sin_port     = htons(PORT);        //端口号     sin.sin_addr.s_addr = inet_addr(IP);     //IP地址     if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin))==-1)     {         perror("bind error");         return -1;     }     printf("bind success _%d_ %s_ %s_\n", __LINE__, __FILE__, __func__);     //将套接字设置成监听状态     if(listen(sfd, 128) == -1)     {         perror("listen error");         return -1;     }      printf("listen success _%d_ %s_ %s_\n", __LINE__, __FILE__, __func__);      struct sockaddr_in cin;                  cin.sin_family     = AF_INET;     socklen_t socklen = sizeof(cin);               while(1)     {       //阻塞接收客户端的链接请求,并且获取客户端的地址信息       int newfd = accept(sfd, (struct sockaddr*)&cin, &socklen);       if(newfd == -1)       {             perror("accept error");             return -1;       }       printf("accept success _%d_ %s_ %s_\n", __LINE__, __FILE__, __func__);       //定义用于向线程体传参的结构体变量       struct msg_info info = {newfd, cin};       //线程创建,并向线程体传参的结构体       pthread_create(&tid,NULL,task1,&info);       //线程分离        if(pthread_detach(tid) != 0)        {             printf("分离失败\n");             return -1;        }     }     close(sfd);     return 0; } 

线程结果图

相关内容

热门资讯

裸辞做“一人公司”,我后悔了 去年这个时候,一位以色列程序员正在东南亚旅行。他顺手把一个在脑子里转了很久的想法做成了产品,一个让任...
南京建成国内首个Pre-6G试... 4月21日,2026全球6G技术与产业生态大会在南京开幕。全息互动技术展台前,一名远在北京的工作人员...
超梵求职受邀参加“2025抖音... 超梵求职受邀参加“2025抖音巨量引擎成人教育行业生态大会”,探讨分享优质内容传播,服务万千学员。 ...
摩托罗拉Razr 2026(R... IT之家 4 月 22 日消息,摩托罗拉宣布新一代 Razr 折叠手机将于 4 月 29 日在美国发...
库克卸任,特纳斯领航:苹果新纪... 苹果首席执行官蒂姆·库克将卸任,硬件工程主管约翰·特纳斯将接任,苹果公司今天宣布此事。 库克将在夏季...