学习C高级(二十六)
服务端管理连接请求:
- 通过两个链式队列来管理连接请求
a. 已完成的连接请求队列
accept在该队列为空时阻塞,不空则取队头的已完成的连接
b. 未完成的连接请求队列
协助实现三路握手的过程 - listen函数的第二参数决定了已完成的连接请求队列的最大长度
如果大于/proc/sys/net/core/somaxconn 则为somaxconn - /proc/sys/net/ipv4/tcp_max_syn_backlog来决定未完成的连接请求队列的最大长度
设计多进程版服务器和多线程版服务器:客户端接收用户输入的整数n,并产生n个随机数(1000以内)发送给服务器,服务器求出n个随机数的平均值发送回给客户端。
多进程版服务器
运行结果:
先打开服务器。
打开一个客户端,服务器与客户端连接后,客户端输入整数count,并产生n个随机数(1000以内)发送给服务器,服务器求出count个随机数的平均值发送回给客户端。
再打开另外一个客户端,此时服务器也可以为这个客户端提供服务。
刚才打开了一个服务器和两个客户端,现在查看进程,可以看到为客户端提供服务的其实是孙子进程。这个能生成多个孙子进程为多个客户端提供服务的就是多进程版服务器。
多线程版服务器
把上面的多进程版服务器改为多线程版服务器。需要改动的只有server.c中的main()和HandleClient()两个函数,再在头文件加上#include<pthread.h>。
运行结果:
先打开服务器。打开一个客户端,再打开另外一个客户端,此时服务器也可以为这个客户端提供服务。现在查看服务器线程,可以看到为客户端提供服务的其实是服务器的线程。这个能生成多个线程为多个客户端提供服务的就是多线程版服务器。
与多进程方案的比较:
- 线程间通讯更加方便
- 线程效率更高
- 但会受描述符数量的限制
多进程版服务器和多线程版服务器代码
(randpdu.h, server.c服务器, client.c客户端):
//randpdu.h
#ifndef RAND_PDU_H
#define RAND_PDU_H
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>//多线程版要用到的头文件
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct RandPDU
{
int count;
char buf[1];
};
#endif
//server.c
#include "randpdu.h"
int CreateServerSocket(char *ip,unsigned short port);
struct RandPDU *ReceiveRandPDU(int datafd);
float GetAve(int *pi,int count);
/***************多进程版*************/
/*
int HandleClient(int datafd);
int main(int argc,char *argv[])
{
int port = 0;
int fd = -1;
int datafd = -1;
pid_t spid;
pid_t gpid;
if(argc < 3)
{
printf("argument too few\n");
return 1;
}
sscanf(argv[2],"%d",&port);
if(port <= 0 || port > 65535)
{
printf("port is invalid\n");
return 2;
}
fd = CreateServerSocket(argv[1],(unsigned short)port);
if(fd < 0)
{
return 3;
}
while(1)
{
datafd = accept(fd,NULL,NULL);
if(datafd < 0)
{
if(errno == EINTR)
{
continue;
}
else
{
perror("accept failed");
break;
}
}
spid = fork();
if(spid < 0)
{
perror("fork son-process failed");
close(datafd);
datafd = -1;
continue;
}
if(spid == 0)
{//son-process
close(fd);//子进程和孙子进程无需使用处理连接用的描述符
fd = -1;
gpid = fork();
if(gpid < 0)
{
perror("fork grandson-process error");
close(datafd);
datafd = -1;
return 1;
}
if(gpid == 0)
{//孙子进程为对应客户端提供服务
HandleClient(datafd);
}
else
{
close(datafd);
datafd = -1;
}
return 0;
}
else
{//father-process:只做监控客户端连接的工作,不做数据交互
close(datafd);
datafd = -1;
wait(NULL);
}
}
close(fd);
fd = -1;
return 0;
}
int HandleClient(int datafd)
{
struct RandPDU *pstPDU = NULL;
float ave = 0.0f;
int ret = 0;
while(1)
{
pstPDU = ReceiveRandPDU(datafd);
if(NULL == pstPDU)
{
break;
}
ave = GetAve((int *)pstPDU->buf,pstPDU->count);
free(pstPDU);
pstPDU = NULL;
ret = write(datafd,&ave,sizeof(ave));
if(ret != sizeof(ave))
{
perror("Send Ave Error");
break;
}
}
close(datafd);
datafd = -1;
return 0;
}
*/
/*********************************/
/***********多线程版***************/
/*
void* HandleClient(void* datafd);//线程的入口函数
int main(int argc,char *argv[])
{
int port = 0;
int fd = -1;
int datafd = -1;
pthread_t tid;//存放新线程ID
int ret = 0;
if(argc < 3)
{
printf("argument too few\n");
return 1;
}
sscanf(argv[2],"%d",&port);
if(port <= 0 || port > 65535)
{
printf("port is invalid\n");
return 2;
}
fd = CreateServerSocket(argv[1],(unsigned short)port);
if(fd < 0)
{
return 3;
}
while(1)
{
datafd = accept(fd,NULL,NULL);//系统执行入口函数时用的实际参数
if(datafd < 0)
{
if(errno == EINTR)
{
continue;
}
else
{
perror("accept failed");
break;
}
}
ret = pthread_create(&tid,NULL,HandleClient,(void*)(long)datafd);//创建新线程
if(ret != 0)
{
perror("pthread_create failed");
break;
}
}
close(fd);
fd = -1;
return 0;
}
void* HandleClient(void* fd)
{
struct RandPDU *pstPDU = NULL;
float ave = 0.0f;
int ret = 0;
int datafd = (int)(long)fd;
//线程自己将自己设置成分离的
pthread_detach(pthread_self());//线程自己将自己设置成分离的
while(1)
{
pstPDU = ReceiveRandPDU(datafd);
if(NULL == pstPDU)
{
break;
}
ave = GetAve((int *)pstPDU->buf,pstPDU->count);
free(pstPDU);
pstPDU = NULL;
ret = write(datafd,&ave,sizeof(ave));
if(ret != sizeof(ave))
{
perror("Send Ave Error");
break;
}
}
close(datafd);
datafd = -1;
return NULL;
}
*/
/**************************/
float GetAve(int *pi,int count)
{
float sum = 0.0;
int i = 0;
for(i = 0;i < count;i++)
{
sum += *(pi + i);
}
return sum / count;
}
struct RandPDU *ReceiveRandPDU(int datafd)
{
int count = 0;
int ret = 0;
struct RandPDU *pstPDU = NULL;
ret = read(datafd,&count,sizeof(int));
if(ret != sizeof(int))
{
perror("read rand count failed");
return NULL;
}
if(count <= 0)
{
printf("Rand Count is invalid\n");
return NULL;
}
pstPDU = (struct RandPDU *)malloc((count+1) * sizeof(int));
if(NULL == pstPDU)
{
perror("Malloc Failed");
return NULL;
}
memset(pstPDU,0,(count + 1) * sizeof(int));
pstPDU->count = count;
ret = read(datafd,pstPDU->buf,pstPDU->count * sizeof(int));
if(ret != pstPDU->count * sizeof(int))
{
perror("Receive Rand Data Failed");
free(pstPDU);
pstPDU = NULL;
return NULL;
}
return pstPDU;
}
int CreateServerSocket(char *ip,unsigned short port)
{
int fd = -1;
int ret = 0;
struct sockaddr_in servaddr;
fd = socket(AF_INET,SOCK_STREAM,0);
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(port);
inet_aton(ip,&servaddr.sin_addr);
ret = bind(fd,(struct sockaddr *)&servaddr,sizeof(servaddr));
ret += listen(fd,6);
if(ret < 0)
{
close(fd);
fd = -1;
perror("bind or listen error");
return -1;
}
return fd;
}
//client.c
#include "randpdu.h"
int CreateClientSocket(char *ip,unsigned short port);
int InputNumber();
int main(int argc,char *argv[])
{
int port = 0;
int fd = -1;
int count = 0;
struct RandPDU *pstPDU = NULL;
int i = 0;
float ave = 0.0f;
int ret = 0;
if(argc < 3)
{
printf("argument too few\n");
return 1;
}
sscanf(argv[2],"%d",&port);
if(port <= 0 || port > 65535)
{
printf("port is invalid\n");
return 2;
}
srand(time(NULL));
fd = CreateClientSocket(argv[1],(unsigned short)port);
if(fd < 0)
{
return 3;
}
do
{
count = InputNumber();
if(count <= 0)
{
printf("Your input error\n");
break;
}
pstPDU = (struct RandPDU *)malloc((count+1) * sizeof(int));
if(NULL == pstPDU)
{
perror("Malloc Failed");
continue;
}
memset(pstPDU,0,(count + 1) * sizeof(int));
pstPDU->count = count;
for(i = 0;i < count;i++)
{
*((int *)pstPDU->buf + i) = rand() % 1000;
}
ret = write(fd,pstPDU,(pstPDU->count + 1) * sizeof(int));
free(pstPDU);
pstPDU = NULL;
if(ret != (count + 1) * sizeof(int))
{
perror("Send Rand Failed");
break;
}
ret = read(fd,&ave,sizeof(float));
if(ret != sizeof(float))
{
perror("Receive Ave Failed\n");
break;
}
printf("The avrange is %.2f\n",ave);
}while(1);
close(fd);
fd = -1;
return 0;
}
int InputNumber()
{
int num = 0;
printf("Please input a count:\n");
scanf("%d",&num);
while(getchar() != '\n')
{
}
return num;
}
int CreateClientSocket(char *ip,unsigned short port)
{
int fd = -1;
int ret = 0;
struct sockaddr_in servaddr;
fd = socket(AF_INET,SOCK_STREAM,0);
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(port);
inet_aton(ip,&servaddr.sin_addr);
ret = connect(fd,(struct sockaddr *)&servaddr,sizeof(servaddr));
if(ret < 0)
{
close(fd);
fd = -1;
perror("connect failed");
return -1;
}
return fd;
}