美文网首页
并发网络服务器 + 网络编程

并发网络服务器 + 网络编程

作者: 浮若星尘 | 来源:发表于2019-01-06 00:25 被阅读0次

    并发网络服务器:

    • 基本概念解释

    多进程并发服务器

    togglesp.c

    void sigchld_handler(int sig) {  
      while (waitpid(-1, 0, WNOHANG) > 0); 
      return; 
    }
    
     int main(int argc, char **argv) {
        int listen_sock, conn_sock, port;
        socklen_t clientlen=sizeof(struct sockaddr_in);
        struct sockaddr_in clientaddr;
        if (argc != 2) {   …   }
        port = atoi(argv[1]);
        signal(SIGCHLD, sigchld_handler);
        listen_sock = open_listen_sock(port);
        while (1) {
            conn_sock = accept(listen_sock, (SA *) &clientaddr, &clientlen);
            if (fork() == 0) { 
               close(listen_sock); 
               toggle(conn_sock);      /* Child process services client */
               close(conn_sock);      
               exit(0);       
            }
           close(conn_sock);
        }
    }
    
    

    特点:父子共享打开文件表,但不共享用户地址空间。
    优缺点
    优点:每个进程都有独立的地址空间,进程间不会相互影响,有较好的可靠性和安全性
    缺点:进程间共享状态信息变得麻烦,IPC机制开销很高,进程间数据共享低效

    基于多线程并发服务器

    togglest.c:

    int main(int argc, char **argv) { 
    
        int listen_sock, *conn_sock_p, port;
        socklen_t clientlen=sizeof(struct sockaddr_in);
        struct sockaddr_in clientaddr;
        pthread_t tid;  
        if (argc != 2) {。。。    }
    
        port = atoi(argv[1]);
        listen_sock = open_listen_sock(port);
        while (1) {
            conn_sock_p = malloc(sizeof(int));
            *conn_sock_p = accept(listen_sock, (SA *) &clientaddr, &clientlen);
            pthread_create(&tid, NULL, serve_client, conn_sock_p); 
        }
    }
    
    void * serve_client (void *vargp) {  
        int conn_sock = *((int *)vargp);
        pthread_detach(pthread_self()); 
        free(vargp);
        toggle(conn_sock);
        close(conn_sock);
    
        return NULL;
    }
    

    预线程化并发服务器

    1. 基本思想
      预先创建一批工作者线程,每次建立一个连接,工作者线程就领取一个任务,负责与一个客户端通信以消除服务器运行过程中创建、撤销线程的开销.


      image.png

    任务池定义(task_pool.c、task_pool.h)

    生产者/消费者模型

    # inpos、outpos分别是缓冲区写入、读出指针
    # mutex:为互斥信号量
    # avail、ready是同步信号量
    
    typedef struct {
        int *socks;         /* Buffer array */         
        int cnt;            /* Maximum number of cell */
        int inpos;          /* buf[inpos] is first available cell */
        int outpos;         /* buf[outpos] is fist item */
        sem_t mutex;      /* Protects accesses to socks */
        sem_t avail;       /* Counts available cells */
        sem_t ready;      /* Counts ready items */
    } task_pool_t;
    
    

    缓冲区初始化

    void task_pool_init(task_pool_t *tp, int n)
    {
        tp->socks = Calloc(n, sizeof(int)); 
        tp->cnt = n;                     /* socks holds max of n items */
        tp->inpos= tp->outpos = 0;           /* Empty socks iff inpos== outpos */
        sem_init(&tp->mutex, 0, 1);       /* Binary semaphore for locking */
        sem_init(&tp->avail, 0, cnt);       /* Initially, socks has cnt empty cell */
        sem_init(&tp->ready, 0, 0);        /* Initially, socks has zero data items */
    }
    
    

    读写缓冲区

    void task_insert (task_pool_t *tp, int item){  
    
        sem_wait(&tp->avail);           /* Wait for available cell */
        sem_wait(&tp->mutex);          /* Lock the shared variable tail pointer */
        tp->socks[tp->inpos] = item;        /* Insert the item */
        tp-> inpos =(tp-> inpos +1)%(tp->cnt); /* adjuset tail point */
        sem_post(&tp->mutex);          /* Unlock the buffer */
        sem_post(&tp->ready);          /* Announce available item */
    }
    
    int task_remove(task_pool_t *tp){。。。}
    

    预线程化服务器代码(togglest_pre.c)

    int main(int argc, char **argv) { 
        int i, listen_sock, conn_sock, port;
        socklen_t clientlen=sizeof(struct sockaddr_in);
        struct sockaddr_in clientaddr;
        pthread_t tid;  
    
        if (argc != 2) { 。。。    }
        port = atoi(argv[1]);
        task_pool_init(&tp, SBUFSIZE);
        listen_sock = open_listen_sock(port);
    
        for (i = 0; i < NTHREADS; i++)  /* Create worker threads */
            pthread_create(&tid, NULL, serve_client, NULL);
            while (1) { 
              conn_sock = accept(listen_sock, (SA *) &clientaddr, &clientlen);
              task_insert(&tp, conn_sock);    /* Insert conn_sock in task pool */
        }
    }
    
    void * serve_client(void *vargp) {  
    
        pthread_detach(pthread_self()); 
        while (1) { 
           int conn_sock = task_remove(&tp); 
           toggle(conn_sock);
           close(conn_sock);
        }
    }
    
    

    网络编程:

    套接字、


    套接字编程模型

    结构:网卡、TCP协议、套接字(Socket)
    套接字:含有进程接收信息的完整地址(Socket地址:IP地址、端口号)

    • Internet连接客户端与服务器的网卡,
    • TCP/IP协议软件连接Socket与网卡,
    • 套接字接口连接进程与Socket,
    image.png

    TCP连接整合了以上两个工具,tcp连接是连接通讯双方套接字的一条通信线路.一条TCP连接实际上就是一个文件描述符,可用read/write或send/recv进行数据收发.

    • TCP连接实例
      服务器端口号:规定为80
      客户端端口号:随机分配,12345


      image.png

    字节序、

    • 网络序(网络序):高位在低地址字节


      大端模式
    • 主机序(小端模式):低位在低地址字节


      小端模式
    • 主机序与网络序的转换
      unsigned long int htonl(unsigned long int hostlong);
      unsigned short int htons(unsigned short int hostshort);
      返回:网络序的值。
      unsigned long int ntohl(unsigned long int netlong);
      unsigned short int ntohs(unsiged short int netshort);
      返回:主机序的值。

    • IP地址
      由32位整数网络序0x8002c2f2转换成点分十进制128.2.193.242
      128=0x80,2=0x02,193=0xc2,242=0xf2
      转换函数:
      int inet_aton(const char *cp, struct in_addr *inp);
      char *inet_ntoa(struct in_addr in);
      a: 字符串 n:32位整数

    网络通信API函数:

    编程框架

    (一)客户端
    (1)创建套接字
    int socket(int domain, int type , int protocol);
    示例:client_sock = socket(AF_INET , SOCK_STREAM, 0);
    (2) connect 函数
    int connect (int client_sock , struct sockaddr *serv_addr , int addrlen);
    (3)包装函数open_client_sock

    (二)服务器端
    (1)创建
    int socket(int domain, int type , int protocol);
    (2)绑定
    int bind(int serv_sock, struct sockaddr *my_addr , int addrlen);
    (3)监听
    int listen(int serv_sock, int backlog);
    (4)接受连接请求
    int accept(int listen_sock, struct sockaddr *addr , int *addrlen);
    包装函数:open_listen_sock

    服务器与客户端连接

    相关文章

      网友评论

          本文标题:并发网络服务器 + 网络编程

          本文链接:https://www.haomeiwen.com/subject/nsehrqtx.html