0
点赞
收藏
分享

微信扫一扫

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



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

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


  服务端代码:

/*************************************************
Author: xiongchuanliang
Description: 单进程多线程服务端代码
编译命令:
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且小于65535
server_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 while


exit(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同步阻塞工作模式例子​​ >>中。



举报

相关推荐

0 条评论