如果服务器支持线程,那么对于客户/服务进程的架构我们可以采取一个线程处理一个客户连接的设计方案。也就是每当有新的连接请求到达服务器时,服务器会新开一个子线程来专门处理这个连接的信息传递;这种方法类似于服务器为每个客户连接fork一个子进程,但这相对来说更轻量级。
1.首先初始化服务器信息(部分代码):
if ((servfd = socket(AF_INET,SOCK_STREAM,0)) < 0)
{
//建立Socket,程序调用Socket函数,该函数返回一个类似于文件描述符的句柄
printf("create socket error!\n");
exit(1);
}
//sin_zero用来将sockaddr_in结构填充到与struct sockaddr同样的长度,可以用bzero()或memset()函数将其置为零
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(SERVERPORT);
servaddr.sin_addr.s_addr = htons(INADDR_ANY);/*系统自动填入本机IP地址 */
if (bind(servfd,(struct sockaddr*)&servaddr,sizeof(servaddr))<0)
{
//服务端通过调用 bind函数来配置本地信息。Bind函数将socket与本机上的一个端口相关联,随后你就可以在该端口监听服务请求。
printf("bind to port %d failure!\n",SERVERPORT);
exit(1);
}
if (listen(servfd,LENGTH_OF_LISTEN_QUEUE) < 0)
{
//Listen函数使socket处于被动的监听模式,并为该socket建立一个输入数据队列,将到达的服务请求保存在此队列中,直到程序处理它们。
printf("call listen failure!\n");
exit(1);
}
2.接着在主线程中监听客户的连接请求:
while (1)
{
//服务一直在运行,直到被某个操作或命令终止该进程
int recvbytes;
socklen_t length = sizeof(cliaddr);
//accept()函数让服务器接收客户的连接请求
int clientfd = accept(servfd,(struct sockaddr*)&cliaddr,&length);
if (clientfd < 0)
{
printf("error comes when call accept!\n");
break;
}
// 创建用于处理新连接的子线程
pthread_t recv_id ;
pthread_create(&recv_id, NULL, recv_msg_from_client, &clientfd);
}
3.处理每个客户连接的线程函数(recv_msg_from_clien):
void* recv_msg_from_client(void* arg)
{
// 分离线程,使主线程不必等待此线程
pthread_detach(pthread_self());
int clientfd = *(int*)arg;
int recvBytes = 0;
char* recvBuf = new char[BUFFER_SIZE];
memset(recvBuf, 0, BUFFER_SIZE);
while(1)
{
if ((recvBytes=recv(clientfd, recvBuf, BUFFER_SIZE,0)) <= 0)
{
perror("recv出错!\n");
break;
}
recvBuf[recvBytes]='\0';
printf("recvBuf:%s\n", recvBuf);
}
close(clientfd);
return NULL;
}