240401班 6月21日作业

1. 多线程并发服务器

 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 <signal.h>
 11 #include <pthread.h>
 12 
 13 #define PORT 8888             //1024~49151
 14 #define IP "192.168.124.112"  //ifconfig查看
 15 #define ERR_MSG(msg) do{\
 16     fprintf(stderr,"line:%d ",__LINE__);\
 17     perror(msg);\
 18 }while(0)
 19 
 20 //需要传递给分之线程的参数
 21 struct msg
 22 {
 23     int newfd;
 24     struct sockaddr_in cin;
 25 };
 26 
 27 void* deal_cli_msg(void* arg);
 28 
 29 
 30 int main(int argc, const char *argv[])
 31 {
 32     //创建流式套接字
 33     int sfd = socket(AF_INET,SOCK_STREAM,0);
 34     if(sfd <0)
 35     {
 36         ERR_MSG("socket");
 37         return -1;
 38     }
 39     printf("流式套接字创建完毕 sfd=%d\n",sfd);
 40 
 41     //允许端口快速被复用
 42     int reuse = 1;
 43     if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
 44     {
 45         ERR_MSG("setsockopt");
 46         return -1;
 47     }
 48     printf("允许端口快速重用\n");
 49 
 50 
 51     //填充服务器的地址信息结构体,给bind函数使用
 52     //真实的地址信息结构体根据地址族制定,AF_INET--->man 7 ip
 53     struct sockaddr_in sin;
 54     sin.sin_family      = AF_INET;        //必须填AF_INET
 55     sin.sin_port        = htons(PORT);    //端口号的网络字节序 1024~49151
 56     sin.sin_addr.s_addr = inet_addr(IP);  //本机IP,ifconfig查看
 57 
 58 
 59     //绑定服务器的地址信息,必须绑定
 60     if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin)) <0)
 61     {
 62         ERR_MSG("bind");
 63         return -1;
 64     }
 65     printf("绑定成功\n");
 66 
 67     //将套接字设置为被动监听状态
 68     if(listen(sfd,128)<0)
 69     {
 70         ERR_MSG("listen");
 71         return -1;
 72     }
 73     printf("监听成功\n");
 74 
 75     //获取连接成功的客服端信息,生成一个新的套接字文件描述符
 76     struct sockaddr_in cin;     //存储客服端的信息
 77     socklen_t addrlen = sizeof(cin);
 78 
 79     int newfd;
 80     pthread_t pid;
 81     struct msg clinfo;
 82 
 83     while(1)
 84     {
 85         //主线程只负责连接
 86         //获取连接成功的客服端信息,生成一个新的套接字文件描述符
 87         //由于是先阻塞在accept位置,然后客户端再下线。
 88         //accept在阻塞前就会先预选一个没有被使用过的文件描述符
 89         //一旦有客户端连接成功则会使用该预选的文件描述符。
 90         newfd = accept(sfd,(struct sockaddr*)&cin,&addrlen);
 91         if(newfd <0)
 92         {
 93             ERR_MSG("accept");
 94             return -1;
 95         }
 96         printf("[%s:%d] newfd=%d 客户端接受成功\n",\
 97                 inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd);
 98 
 99         clinfo .newfd = newfd;
100         clinfo.cin = cin;
101 
102         //能运行到当前位置,则代表有客户端连接成功,
103         //需要另外一个线程于用户交互
104 
105         if(pthread_create(&pid,NULL,deal_cli_msg,(void*)&clinfo) !=0)
106         {
107             fprintf(stderr,"pthread_create failed __%d__\n",__LINE__);
108             break;
109         }
110 
111         pthread_detach(pid);
112 
113     }
114 
115         //关闭文件描述符
116         if(close(sfd) <0)
117         {
118             ERR_MSG("close");
119             return -1;
120         }
121 
122     //  close(newfd);   线程共用进程里面的所以东西
123 
124         return 0;
125 }
126 
127 void* deal_cli_msg(void* arg)
128 {
129     int newfd = ((struct msg*)arg)->newfd;
130     struct sockaddr_in cin = ((struct msg*)arg)->cin;
131 
132     char buf[128]="";
133     ssize_t res;
134     while(1)
135     {
136         bzero(buf,sizeof(buf));
137         //接受
138         //res = recv(newfd,buf,sizeof(buf),0);等价
139         res = recv(newfd,buf,sizeof(buf),0);
140         if(res <0)
141         {
142             ERR_MSG("recv");
143             break;
144         }
145         else if(0 == res)
146         {
147             printf("[%s:%d] newfd=%d 客户端接受成功i\n",\
148                     inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd);
149             break;
150         }
151         printf("[%s:%d] newfd=%d :%s\n",\
152                 inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd,buf);
153 
154 
155         //发送
156         strcat(buf,"*_*");
157         if(send(newfd,buf,sizeof(buf),0)<0)
158         {
159             ERR_MSG("send");
160             break;
161         }
162         printf("发送成功");
163     }
164 
165     close(newfd);
166     pthread_exit(NULL);
167 
168 }


2. select的TCP服务器

include <stdio.h>                                                                                                                     
include <sys/types.h>
include <sys/socket.h>
include <unistd.h>
include <arpa/inet.h>
include <netinet/in.h>
include <string.h>
include <sys/select.h>
include <sys/time.h>

define PORT 8888             //1024~49151
define IP "192.168.124.112"  //ifconfig查看
define ERR_MSG(msg) do{\
   fprintf(stderr,"line:%d ",__LINE__);\
   perror(msg);\
while(0)

nt deal_cliRecvSend(int i,struct sockaddr_in savecin[],fd_set *preadfds,int *pmaxfd);
nt deal_cliConnect(int sfd,struct sockaddr_in psavecin[],fd_set *preadfds,int *pmaxfd);
nt deal_keyboard_msg(fd_set readfds);

nt main(int argc, const char *argv[])

   //创建流式套接字
   int sfd = socket(AF_INET,SOCK_STREAM,0);
   if(sfd <0)
   {
       ERR_MSG("socket");
       return -1;
   }
   printf("流式套接字创建完毕 sfd=%d\n",sfd);

   //允许端口快速被复用
    int reuse = 1;
   if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
   {
       ERR_MSG("setsockopt");
       return -1;
   }
   printf("允许端口快速重用\n");


   //填充服务器的地址信息结构体,给bind函数使用
   //真实的地址信息结构体根据地址族制定,AF_INET--->man 7 ip
   struct sockaddr_in sin;
   sin.sin_family      = AF_INET;        //必须填AF_INET
   sin.sin_port        = htons(PORT);    //端口号的网络字节序 1024~49151
   sin.sin_addr.s_addr = inet_addr(IP);  //本机IP,ifconfig查看


   //绑定服务器的地址信息,必须绑定
   if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin)) <0)
   {
       ERR_MSG("bind");
       return -1;
   }
   printf("绑定成功\n");

   //将套接字设置为被动监听状态
   if(listen(sfd,128)<0)
   {
       ERR_MSG("listen");
       return -1;
   }
   printf("监听成功\n");

   //创建读集合
   //---->fd_set本质上是一个结构体,其中只有一个整形数组
   //若不清空,会是一堆随机值,有可能会随机到有效的文件描述符编号
   //但是该有效文件描述符编号不需要监测,从而导致select异常解除阻塞
   fd_set readfds,tmpfds;
   FD_ZERO(&readfds);   //清空集合

   //将需要的文件描述符添加到集合中
   FD_SET(0,&readfds);
   FD_SET(sfd,&readfds);

   int maxfd = sfd;  //集合中最大的文件描述符

   int s_res;
   int newfd =-1;
   struct sockaddr_in savecin[1024];   //将客户端的地址信息存储到对应文件描述符下标位置

   while(1)
   {
       tmpfds = readfds;
       //让内核检测集合中的文件描述符是否准备就绪
       s_res = select(maxfd+1,&tmpfds,NULL,NULL,NULL);
       if(s_res < 0)
       {
           ERR_MSG("select");
           return -1;
       }
       else if(0 == s_res)
       {
           printf("time out.....\n");
           break;
       }
       printf("__%d__\n",__LINE__);

       //能运行到当前位置,则代表集合中有文件描述符准备就绪
       //判断集合中哪个文件描述符准备就绪,执行对应的处理函数
       /*
        * 集合中会只能产生事件的文件描述符:
        * 0号准备就绪,则集合中只会剩下0
        * sfd准备就绪,则集合中只会剩下sfd
        * 若0和sfd均准备就绪,则集合中只会剩下0和sfd
        * 所以只要判断集合中剩下哪个文件描述符,就代表该文件描述符准备就绪
        *
        */
       for(int i=0;i<=maxfd;i++)
       {
           //判断i代表的文件描述符是否在集合中
           if(FD_ISSET(i,&tmpfds) ==0){
               continue;
           }
           //能运行到这个位置,则说明i代表的文件描述符在集合中
           if(0 == i)
           {
               printf("触发键盘输入事件\n");
               deal_keyboard_msg(readfds);
           }
           else if(sfd == i)
           {
               printf("触发客户端连接事件\n");
               deal_cliConnect(sfd,savecin,&readfds,&maxfd);
           }
           else
           {
               printf("触发客户端交互事件\n");
               deal_cliRecvSend(i,savecin,&readfds,&maxfd);
           }
       }
   }
   //关闭文件描述符
   if(close(sfd) <0)
   {
       ERR_MSG("close");
       return -1;
   }
   return 0;


/处理客户端交互事件
nt deal_cliRecvSend(int i,struct sockaddr_in savecin[],fd_set *preadfds,int *pmaxfd)

   char buf[128] = "";
   ssize_t res;

   bzero(buf,sizeof(buf));
   //接收
   res = recv(i,buf,sizeof(buf),0);
   if(res <0)
   {
       ERR_MSG("recv");
       return -1;
   }
   else if(0 == res)
   {
       printf("[%s:%d] newfd=%d 客户端下线\n",\
               inet_ntoa(savecin[i].sin_addr),ntohs(savecin[i].sin_port),i);

       close(i);            //关闭文件描述符
       FD_CLR(i,preadfds);  //将文件描述符从集合中删除

       //更新maxfd
       //从目前记录的最大maxfd开始依次往最小的文件描述符判断,是否在集合中
       while(FD_ISSET(*pmaxfd,preadfds)==0 && *pmaxfd-- >0);

       return 0;
   }
   printf("[%s:%d] newfd=%d : %s\n",\
           inet_ntoa(savecin[i].sin_addr),ntohs(savecin[i].sin_port),i,buf);

   //发送
   strcat(buf,"*_*");
   if(send(i,buf,sizeof(buf),0)<0)
   {
       ERR_MSG("send");
       return -1;
   }
   printf("发送成功");
   return 0;


/处理客户端连接事件
nt deal_cliConnect(int sfd,struct sockaddr_in psavecin[],fd_set *preadfds,int *pmaxfd)

   struct sockaddr_in cin;     //存储客服端的信息
   socklen_t addrlen = sizeof(cin);

   int newfd = accept(sfd,(struct sockaddr*)&cin,&addrlen);
   if(newfd < 0)
   {
       ERR_MSG("accept");
       return -1;
   }
   printf("[%s:%d] newfd=%d 客户端连接成功\n",\
           inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd);

   psavecin[newfd] = cin;

   FD_SET(newfd,preadfds);             //客户端连接成功后若要监测客服端的交互事件
   //需要将newfd添加到集合中,让内核监测
   *pmaxfd = *pmaxfd>newfd?*pmaxfd:newfd;    //更新maxfd
   return 0;


/键盘输入事件
nt deal_keyboard_msg(fd_set readfds)

   int sndfd;
   char buf[128];
   int res = scanf("%d %s",&sndfd,buf);
   while(getchar() != '\n');
   if(res != 2)
   {
       printf("请输入正确格式:fd string\n");
       return -1;
   }

   //判断文件描述符是否合法
   if(sndfd<=3 || FD_ISSET(sndfd,&readfds) == 0)
   {
       printf("sndfd=%d 错误,请输入合法文件描述符\n",sndfd);
       return -1;
   }

   if(send(sndfd,buf,sizeof(buf),0) <0)
   {
       ERR_MSG("send");
       return -1;
   }
   return 0;

  }                                                                                                                                    
                                                                                                                                      

现象


3. select的TCP客户端

 #include <stdio.h>                                                                             
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <unistd.h>
 #include <arpa/inet.h>
 #include <string.h>
 #include <netinet/in.h>
 #include <stdlib.h>
 
 #define ERR_MSG(msg) {fprintf(stderr, "行:%d", __LINE__);perror(msg);}
 #define PORT 8888           //1024~49151
 #define IP "192.168.124.112"  //服务器ip
 
 int main(int argc, const char *argv[])
 {
     //创建流式套接字
     int cfd = socket(AF_INET, SOCK_STREAM, 0);
     if(cfd < 0)
     {
         ERR_MSG("socket");
         return -1;
     }
     printf("套接字创建完成\n");
     //填充地址信息结构体,给connect使用
     //需要链接的服务器
     struct sockaddr_in sin;
     sin.sin_family      = AF_INET;          //必须填AF_INET
     sin.sin_port        = htons(PORT);      //服务器端口号的网络字节序
     sin.sin_addr.s_addr = inet_addr(IP);    //服务器IP,ubuntu 中ifconfig
 
     //链接服务器
     int fd = connect(cfd, (struct sockaddr*)&sin, sizeof(sin));
     if(fd < 0)
     {
         ERR_MSG("connect");
         return -1;
     }
     printf("[%s:%d]服务器链接成功\n", IP, PORT);
 
     //创建一个读集合和临时集合
     fd_set readfds, tempfds;
     //清空集合
     FD_ZERO(&readfds);
     FD_ZERO(&tempfds);
     //将文件描述符添加到读集合
     FD_SET(0, &readfds);
     FD_SET(cfd, &readfds);
     //创建一个变量储存最大文件描述符
     int maxfd = cfd;
     int s_res;
     ssize_t res, res1;
     char buf1[128] = "";
 
     while(1)
     {
         tempfds = readfds;
         //让内核监测集合中的文件描述符是否准备就绪
         s_res = select(maxfd+1, &tempfds, NULL, NULL, NULL);
         if(s_res < 0)
         {
             ERR_MSG("select");
             return -1;
         }
         else if(0 == s_res)
         {
             printf("超时等待\n");
             break;
         }
         //能运行到这里,说明有文件描述符准备就绪
         //判断哪一个文件描述符准备就绪,执行对应的处理函数
         /*
          * 当有文件描述符准备就绪,则集合中只剩下准备就绪的文件描述符
          * 所以只要判断剩下哪个文件描述符,就知道谁准备就绪          
          */
         //循环判断是触发了哪个事件
         for(int i=0; i<=maxfd; i++)
         {
             if(FD_ISSET(i, &tempfds))
             {
                 if(0 == i)
                 {
                     //触发键盘输入,发送事件                                  
                     bzero(buf1, sizeof(buf1));
                     scanf("%s", buf1);
                     res = send(cfd, buf1, sizeof(buf1), 0);
                     if(res1 < 0)
                     {
                         ERR_MSG("send");
                         return -1;
                     }
                     printf("cfd=%d已发送数据:%s 给服务器:[%s: %d] \n",  cfd, buf1, IP, PORT);
                 }
                 else if(cfd == i)
                 {
                     //触发接收事件
                     bzero(buf1, sizeof(buf1));
                     res1 = recv(cfd, buf1, res+1, 0);
                     if(res1 < 0)
                     {
                         ERR_MSG("recv");
                         return -1;
                     }
                     else if(0 == res1)
                     {
                         printf("[%s:%d]服务器关闭 cfd=%d\n", IP, PORT, cfd);
                         return -1;
                     }
                     printf("cfd=%d 从服务器:[%s %d] 接收到数据:%s\n", cfd, IP, PORT, buf1);
                 }
             }
         }
 
     }
     //7.关闭
     if(close(cfd) < 0)
     {
         ERR_MSG("close");
         return -1;
     }
     return 0;
 
 }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值