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

2019-01-06  本文已影响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地址、端口号)

image.png

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

字节序、

网络通信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

服务器与客户端连接
上一篇下一篇

猜你喜欢

热点阅读