我们之前实现了的TCP交互版本,只能实现一台服务器当前只能服务一个客户,是单进程的。
https://blog.csdn.net/ZWE7616175/article/details/80260420
为了解决这一问题,我们实现多进程和多线程的版本。
多进程版本
服务器端
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <string.h>
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
void ProcessRequest(int new_sock, struct sockaddr_in* peer)
{
char buf[1024];
buf[0]='\0';
while(1)
{
ssize_t s = read(new_sock, buf, sizeof(buf));
if(s > 0)
{
buf[s]='\0';
printf("[%s:%d] %s",inet_ntoa(peer->sin_addr),\
ntohs(peer->sin_port),buf);
}
else
{
printf("client quit\n");
break;
}
write(new_sock,buf,strlen(buf)+1);
}
}
void CreateWorker(int new_sock,struct sockaddr_in* peer)
{
pid_t pid = fork();
if (pid < 0)
{
perror("fork");
return;
}
else if(pid == 0)
{
//子进程
if(fork() == 0)
//孙子进程
ProcessRequest(new_sock, peer);//孙子进程为孤儿进程,系统回收
exit(0);//子进程退出
}
else//父进程
{
close(new_sock);
waitpid(pid, NULL, 0);//父进程回收子进程
}
//服务器
int main(int argc, char* argv[])
{
if(argc!=3){
printf("Usage %s is port\n",argv[0]);
return 1;
}
//创建套接字
int sock = socket(AF_INET, SOCK_STREAM, 0);
if(sock < 0){
perror("socket");
return 2;
}
struct sockaddr_in local;
local.sin_family = AF_INET;
local.sin_port = htons(atoi(argv[2]));
local.sin_addr.s_addr = inet_addr(argv[1]);
//绑定
if(bind(sock, (struct sockaddr*)&local, sizeof(local))<0)
{
perror("bind");
return 3;
}
//监听
if(listen(sock, 5)<0)
{
perror("listen");
return 4;
}
while(1)
{
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
//接受请求
int new_sock=accept(sock, (struct sockaddr*)&peer,&len);
if(new_sock<0)
{
perror("accept");
return 5;
}
CreateWorker(new_sock, &peer);
}
return 0;
}
运行服务器端。
打开多个客户端,此处我们只测试两个。
多进程服务器
缺点:
1.只有链接来了才创建进程,影响性能。(类似于我们生活中的例子,到了饭店了老板才开始买菜),解决这一问题,可以提前创建,进程池的引入。
2.能够同时服务量有限,因为每个进程都需要分配资源。
3.进程增多导致切换增多,调度周期增长,进而影响性能。
优点:
1.可以同时服务多个客户。
2.易于编写。
3.稳定,任意一个进程挂掉,不会影响其他进程。
多线程版本
服务器:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <string.h>
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
typedef struct Arg
{
int fd;
struct sockaddr_in addr;
}Arg;
void ProcessRequest(int new_sock, struct sockaddr_in* peer)
{
char buf[1024];
buf[0]='\0';
while(1)
{
ssize_t s = read(new_sock, buf, sizeof(buf));
if(s > 0)
{
buf[s]='\0';
printf("[%s:%d] %s",inet_ntoa(peer->sin_addr),\
ntohs(peer->sin_port),buf);
}
else
{
printf("client quit\n");
break;
}
write(new_sock,buf,strlen(buf)+1);
}
}
void* CreateWorker(void* ptr)
{
Arg* arg = (Arg*)ptr;
ProcessRequest(arg->fd, &arg->addr);
free(arg);
return NULL;
}
//服务器
int main(int argc, char* argv[])
{
if(argc!=3){
printf("Usage %s is port\n",argv[0]);
return 1;
}
//创建套接字
int sock = socket(AF_INET, SOCK_STREAM, 0);
if(sock < 0){
perror("socket");
return 2;
}
struct sockaddr_in local;
local.sin_family = AF_INET;
local.sin_port = htons(atoi(argv[2]));
local.sin_addr.s_addr = inet_addr(argv[1]);
//绑定
if(bind(sock, (struct sockaddr*)&local, sizeof(local))<0)
{
perror("bind");
return 3;
}
//监听
if(listen(sock, 5)<0)
{
perror("listen");
return 4;
}
while(1)
{
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
//接受请求
int new_sock=accept(sock, (struct sockaddr*)&peer,&len);
if(new_sock<0)
{
perror("accept");
return 5;
}
pthread_t tid = 0;
Arg* arg = (Arg*)malloc(sizeof(Arg));
arg->fd = new_sock;
arg->addr = peer;
pthread_create(&tid, NULL, CreateWorker, (void*)arg);
pthread_detach(tid);
}
return 0;
}
运行服务器端。
打开两个终端测试。
多线程版本缓解了多进程的性能问题和资源问题,但只是多进程的一个缓解。也存在着一些缺点:相比多进程来说,不稳定,任意一个线程挂掉,所有的线程都会退出,并且共享资源,有风险。