您的位置:首页 > 理论基础 > 计算机网络

tcp多线程并发服务器

2016-09-02 11:11 232 查看
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 ,会造成数据丢失,同时会造成多个服务器连接错误。对服务器造成不可预知的问题.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  tcp 服务器