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

并发网络服务器:

  • 基本概念解释

多进程并发服务器

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

服务器与客户端连接

你可能感兴趣的:(并发网络服务器 + 网络编程)