• linux之linux并发编程(3)


    I/O复用服务器
    I/O 复用技术是为了解决进程或线程阻塞到某个 I/O 系统调用而出现的技术,使进程不阻塞于某个特定的 I/O 系统调用。它也可用于并发服务器的设计,常用函数 select() 或 epoll() 来实现。详情,请看《select、poll、epoll的区别使用》。
    socket(...); // 创建套接字
    bind(...);   // 绑定
    listen(...); // 监听
    while(1)
    {
        if(select(...) > 0) // 检测监听套接字是否可读
        {
            if(FD_ISSET(...)>0) // 套接字可读,证明有新客户端连接服务器  
            {
                accpet(...);// 取出已经完成的连接
                process(...);// 处理请求,反馈结果
            }
        }
        close(...); // 关闭连接套接字:accept()返回的套接字
    }

    参考代码:

      1 #include <stdio.h>
      2 #include <unistd.h>
      3 #include <stdlib.h>
      4 #include <errno.h>
      5 #include <string.h>
      6 #include <sys/socket.h>
      7 #include <sys/types.h>
      8 #include <netinet/in.h>
      9 #include <arpa/inet.h>
     10 #include <sys/select.h>
     11 #define SERV_PORT 8080
     12 #define LIST 20                //服务器最大接受连接
     13 #define MAX_FD 10              //FD_SET支持描述符数量
     14 int main(int argc, char *argv[])
     15 {
     16     int sockfd;
     17     int err;
     18     int i;
     19     int connfd;
     20     int fd_all[MAX_FD]; //保存所有描述符,用于select调用后,判断哪个可读
     21     
     22     //下面两个备份原因是select调用后,会发生变化,再次调用select前,需要重新赋值
     23     fd_set fd_read;    //FD_SET数据备份
     24     fd_set fd_select;  //用于select
     25     struct timeval timeout;         //超时时间备份
     26     struct timeval timeout_select;  //用于select
     27     
     28     struct sockaddr_in serv_addr;   //服务器地址
     29     struct sockaddr_in cli_addr;    //客户端地址
     30     socklen_t serv_len;
     31     socklen_t cli_len;
     32     
     33     //超时时间设置
     34     timeout.tv_sec = 10;
     35     timeout.tv_usec = 0;
     36     
     37     //创建TCP套接字
     38     sockfd = socket(AF_INET, SOCK_STREAM, 0);
     39     if(sockfd < 0)
     40     {
     41         perror("fail to socket");
     42         exit(1);
     43     }
     44     
     45     // 配置本地地址
     46     memset(&serv_addr, 0, sizeof(serv_addr));
     47     serv_addr.sin_family = AF_INET;            // ipv4
     48     serv_addr.sin_port = htons(SERV_PORT);    // 端口, 8080
     49     serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); // ip
     50     serv_len = sizeof(serv_addr);
     51     
     52     // 绑定
     53     err = bind(sockfd, (struct sockaddr *)&serv_addr, serv_len);
     54     if(err < 0)
     55     {
     56         perror("fail to bind");
     57         exit(1);
     58     }
     59     // 监听
     60     err = listen(sockfd, LIST);
     61     if(err < 0)
     62     {
     63         perror("fail to listen");
     64         exit(1);
     65     }
     66     
     67     //初始化fd_all数组
     68     memset(&fd_all, -1, sizeof(fd_all));
     69     fd_all[0] = sockfd;   //第一个为监听套接字
     70     
     71     FD_ZERO(&fd_read);    // 清空
     72     FD_SET(sockfd, &fd_read);  //将监听套接字加入fd_read
     73     int maxfd;
     74     maxfd = fd_all[0];  //监听的最大套接字
     75     
     76     while(1){
     77     
     78         // 每次都需要重新赋值,fd_select,timeout_select每次都会变
     79         fd_select = fd_read;
     80         timeout_select = timeout;
     81         
     82         // 检测监听套接字是否可读,没有可读,此函数会阻塞
     83         // 只要有客户连接,或断开连接,select()都会往下执行
     84         err = select(maxfd+1, &fd_select, NULL, NULL, NULL);
     85         //err = select(maxfd+1, &fd_select, NULL, NULL, (struct timeval *)&timeout_select);
     86         if(err < 0)
     87         {
     88                 perror("fail to select");
     89                 exit(1);
     90         }
     91         if(err == 0){
     92             printf("timeout
    ");
     93         }
     94         
     95         // 检测监听套接字是否可读
     96         if( FD_ISSET(sockfd, &fd_select) ){//可读,证明有新客户端连接服务器
     97             
     98             cli_len = sizeof(cli_addr);
     99             
    100             // 取出已经完成的连接
    101             connfd = accept(sockfd, (struct sockaddr *)&cli_addr, &cli_len);
    102             if(connfd < 0)
    103             {
    104                 perror("fail to accept");
    105                 exit(1);
    106             }
    107             
    108             // 打印客户端的 ip 和端口
    109             char cli_ip[INET_ADDRSTRLEN] = {0};
    110             inet_ntop(AF_INET, &cli_addr.sin_addr, cli_ip, INET_ADDRSTRLEN);
    111             printf("----------------------------------------------
    ");
    112             printf("client ip=%s,port=%d
    ", cli_ip,ntohs(cli_addr.sin_port));
    113             
    114             // 将新连接套接字加入 fd_all 及 fd_read
    115             for(i=0; i < MAX_FD; i++){
    116                 if(fd_all[i] != -1){
    117                     continue;
    118                 }else{
    119                     fd_all[i] = connfd;
    120                     printf("client fd_all[%d] join
    ", i);
    121                     break;
    122                 }
    123             }
    124             
    125             FD_SET(connfd, &fd_read);
    126             
    127             if(maxfd < connfd)
    128             {
    129                 maxfd = connfd;  //更新maxfd
    130             }
    131         
    132         }
    133         
    134         //从1开始查看连接套接字是否可读,因为上面已经处理过0(sockfd)
    135         for(i=1; i < maxfd; i++){
    136             if(FD_ISSET(fd_all[i], &fd_select)){
    137                 printf("fd_all[%d] is ok
    ", i);
    138                 
    139                 char buf[1024]={0};  //读写缓冲区
    140                 int num = read(fd_all[i], buf, 1024);
    141                 if(num > 0){
    142                     //收到 客户端数据并打印
    143                     printf("receive buf from client fd_all[%d] is: %s
    ", i, buf);
    144                     
    145                     //回复客户端
    146                     num = write(fd_all[i], buf, num);
    147                     if(num < 0){
    148                         perror("fail to write ");
    149                         exit(1);
    150                     }else{
    151                         //printf("send reply
    ");
    152                     }
    153                     
    154                     
    155                 }else if(0 == num){ // 客户端断开时
    156                     
    157                     //客户端退出,关闭套接字,并从监听集合清除
    158                     printf("client:fd_all[%d] exit
    ", i);
    159                     FD_CLR(fd_all[i], &fd_read);
    160                     close(fd_all[i]);
    161                     fd_all[i] = -1;
    162                     
    163                     continue;
    164                 }
    165                 
    166             }else {
    167                 //printf("no data
    ");                  
    168             }
    169         }
    170     
    171     }
    172     
    173     return 0;
    174 }
  • 相关阅读:
    WPF / Win Form:多线程去修改或访问UI线程数据的方法( winform 跨线程访问UI控件 )
    TCP 流模式与UDP数据报模式(转)
    hibernate 检索方式
    【UVA】1449-Dominating Patterns(AC自己主动机)
    软件项目工作流程图
    iOS7 UIKit动力学-碰撞特性UICollisionBehavior 下
    东莞无人工厂变成现实,中国无人工厂将非常快普及,保住世界工厂地位
    小米手机与魅族的PK战结果 说明了什么
    python Debug 单步调试
    合并两个排序的单链表
  • 原文地址:https://www.cnblogs.com/heych/p/12582890.html
Copyright © 2020-2023  润新知