tcp多线程并发服务器

来源:互联网 发布:周杰伦演唱会骂人知乎 编辑:程序博客网 时间:2024/05/24 02:03

tcp多线程并发服务器

多线程服务器是对多进程服务器的改进,由于多进程服务器在创建进程时要消耗较大的系统资源,所以用线程来取代进程,这样服务处理程序可以较快的创建。据统计,创建线程与创建进程要快 10100 倍,所以又把线程称为“轻量级”进程。线程与进程不同的是:一个进程内的所有线程共享相同的全局内存、全局变量等信息,这种机制又带来了同步问题。

tcp多线程并发服务器框架:
这里写图片描述

我们在使用多线程并发服务器时,可以直接使用以上框架,我们仅仅修改线程函数里面的内容。

代码示例:

/*************************************************************************    > File Name: tcpServer.c    > Author:  ************************************************************************/#include <stdio.h>#include <time.h>#include <stdlib.h>#include <string.h>                     #include <unistd.h>#include <sys/select.h>#include <sys/types.h>          /* See NOTES */#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>                  #include <pthread.h>int listenfd = -1;              // 套接字int connfd = -1;#define PORT  8080 // 监听端口#define UNUSED(x) (void)x/************************************************************************函数名称:   void *read_thread(void *arg)函数功能:   线程函数,处理客户信息函数参数:   无函数返回:   无************************************************************************/void *read_thread(void *arg){    char rxbuf[1024] = {0,};    UNUSED(arg);    while(1)    {        //recv with timeout        fd_set fdset;        struct timeval timeout;        int retv = 0, readn = 0;        FD_ZERO(&fdset);        FD_SET(connfd, &fdset);        timeout.tv_sec = 2;        timeout.tv_usec = 0;        retv = select(connfd+1, &fdset, NULL, NULL, &timeout);        if (retv == -1)        {            printf("%s: Select: failed. retv=%d", __func__, retv);            close(connfd);            break;        }else if (retv == 0)        {            usleep(500); // 1毫秒            continue;        }        memset(rxbuf,0,sizeof(rxbuf));        readn = recv(connfd, rxbuf, sizeof(rxbuf), 0);        if (readn < 0)        {            if (errno == EINTR)            {                printf("%s: recv failed", __func__);                usleep(500);                continue;            }            else            {                printf("%s: recv timeout. readn=%d, error=%s",                               __func__, readn, strerror(errno));                close(connfd);                usleep(500);                break;            }        }else if (readn == 0)        {            usleep(500);            continue;        }    }    close(connfd);    return  NULL;}//===============================================================// 语法格式:    void main(void)// 实现功能:    主函数,建立一个TCP并发服务器// 入口参数:    无// 出口参数:    无//===============================================================int main(int argc, char *argv[]){    int no;    struct sockaddr_in my_addr; // 服务器地址结构体    listenfd = socket(AF_INET, SOCK_STREAM, 0);   // 创建TCP套接字    if(listenfd < 0)    {        perror("socket error");        exit(-1);    }    /* Enable address reuse */    on = 1;    setsockopt( listenfd, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on));    bzero(&my_addr, sizeof(my_addr));      // 初始化服务器地址    my_addr.sin_family = AF_INET;    my_addr.sin_port   = htons(PORT);    my_addr.sin_addr.s_addr = htonl(INADDR_ANY);    printf("Binding server to port %d\n", port);    // 绑定    if(bind(listenfd, (struct sockaddr*)&my_addr, sizeof(my_addr)) < 0)    {        perror("bind");        close(listenfd);                exit(-1);    }    // 监听,套接字变被动    if( listen(listenfd, 10) < 0)    {        perror("listen");        close(listenfd);                exit(-1);    }    printf("Waiting client...\n");    while(1)    {        struct sockaddr_in client_addr;        // 用于保存客户端地址        socklen_t cliaddr_len = sizeof(client_addr);   // 必须初始化!!!        //获得一个已经建立的连接           connfd = accept(listenfd, (struct sockaddr*)&client_addr, &cliaddr_len);                                    if(connfd < 0)        {            perror("accept this time");            continue;        }               // 打印客户端的 ip        printf("client from : %s\n", inet_ntoa(client_addr.sin_addr)))        printf("----------------------------------------------\n");        if(connfd > 0)        {            pthread_t thread_id;            //由于同一个进程内的所有线程共享内存和变量,因此在传递参数时需作特殊处理,值传递。            pthread_create(&thread_id, NULL, read_thread, NULL);  //创建线程            pthread_detach(thread_id); // 线程分离,结束时自动回收资源        }    }    close(listenfd);    return 0;}

特别注意:
如果void *是4个字节,而connfd为int类型也是4个字节,可以传值。但是connfd为char、short,上面传值就会出错。所以一般不要(不推荐)在线程中传递connfd ,会造成数据丢失,同时会造成多个服务器连接错误。对服务器造成不可预知的问题.

1 0