网络编程(6)单进程多线程并发服务器实现

来源:互联网 发布:织梦cms视频 编辑:程序博客网 时间:2024/05/16 11:11

        相比前面的fork方式的多进程服务器,用线程方式更轻量级。最少用fork时,需要将内存映像,描述符等都要从父进程复制到子进程中,

会占用大量的资源而多线程方式都在一个进程内,就无需占用这些资源,但同步也是个问题,而且一个线程挂了,可能会影响到进程中的其它线程。

        多线程并发的原理就是当accept 成功连接一个客户端后,把与这个客户端的交互丢到新线程去处理它。


  服务端代码:

/*************************************************Author: xiongchuanliangDescription: 单进程多线程服务端代码编译命令:Linux:g++ -o tcpserver_thread tcpserver_thread.cpp -m64 -I./common -lpthread**************************************************/// 服务端代码#include <stdio.h>#include <stdlib.h>#include <string.h>#include <pthread.h>#include <string.h>#include <sys/stat.h>#include <time.h>#include "initsock.h"#include "common.h"CInitSock initSock;//客户端Socket信息结构体typedef struct _client_info{        int    fd;//客户端socket描述符struct sockaddr_in addr;    //客户端地址信息结构体    time_t lastseconds;//可依这个计算空闲时间,空闲太长的连接可以关闭。} client_info; void *TestSocket(void *cinfo);int main(int argc, char* argv[]){struct _client_info *client_info = NULL;//创建套接字SOCKET sListen = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);if(sListen == INVALID_SOCKET){PrintError("socket() failed.\n");exit(EXIT_FAILURE);}//绑定本地IP和端口到套接字struct sockaddr_in server_addr;server_addr.sin_family = AF_INET;    server_addr.sin_port = htons(SERVPORT); //大于1024且小于65535server_addr.sin_addr.s_addr = INADDR_ANY;bzero(&(server_addr.sin_zero),8);   //SO_REUSEADDR : 使bind函数能允许地址立即重用int on = 1;setsockopt( sListen, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on) );if(bind(sListen,(struct sockaddr *)&server_addr,sizeof(struct sockaddr)) == SOCKET_ERROR)    {PrintError("bind() failed.");exit(EXIT_FAILURE);    }    //开始监听    if(listen(sListen, BACKLOG) == SOCKET_ERROR)    {  PrintError("sListen() failed.");        exit(EXIT_FAILURE);    }struct sockaddr_in remoteAddr = {0};socklen_t nAddrlen = sizeof(struct sockaddr_in);pthread_t tpid;//循环接收数据while(1){SOCKET sClient;printf("等待客户端连接中...\n");sClient = accept(sListen,(struct sockaddr *)&remoteAddr, &nAddrlen); if(sClient == INVALID_SOCKET){PrintError("accept() failed.");continue;}printf("接收到一个客户端连接:%s \n",inet_ntoa(remoteAddr.sin_addr));//复制连接client_info = (struct _client_info *)malloc(sizeof(struct _client_info));client_info->fd = sClient;memcpy((void *)&client_info->addr,&remoteAddr,sizeof(remoteAddr));client_info->lastseconds = time(NULL);//在线程中处理if( pthread_create(&tpid,NULL,&TestSocket,(void *)client_info) != 0 )  {  PrintError("pthread_create() failed."); exit(EXIT_FAILURE); }}// end whileexit(EXIT_SUCCESS);}void *TestSocket(void *cinfo){pthread_t tid;char sendData[100] = {0};struct _client_info *client_info = (struct _client_info *)cinfo;tid = pthread_self();SOCKET sClient = client_info->fd;char recvData[MAXDATASIZE]={0};//接收数据int recvbytes = recv(sClient, recvData, MAXDATASIZE, 0);        if( recvbytes == 0){printf("recv() no data!\n");}else if( recvbytes < 0){PrintError("recv() failed");}else if(recvbytes > 0){            recvData[recvbytes]='\0';printf("service thread id=%lu \n收到信息:%s\n",tid,recvData);//发送数据到客户端//char * sendData = "客户端,你好啊!\n";snprintf(sendData,100,"service thread id=%lu \n%s\n",tid, recvData); send(sClient, sendData, strlen(sendData), 0);}  //关闭子进程连接的套接字close(sClient);free(cinfo);pthread_exit(NULL);}


   代码中要注意的一个地方是,当每次pthread_create时,要将客户端连接分配到一个新的空间中再传给线程,因为线程参数是指针方式传递,如果不这

样做,新的客户端连接会替换掉之前的客户端连接。同时不要忘记空间的释放。

可以用<<网络编程(4)select函数实现I/O多路复用服务器>>中的客户端测试程序来测试下并发连接下的情况。

       程序中用到的相关头文件在 <<网络编程(1)跨平台的Socket同步阻塞工作模式例子 >>中。


MAIL: xcl_168@aliyun.com

BLOG: http://blog.csdn.net/xcl168


0 0
原创粉丝点击