首页 >> 大全

TCP/IP网络编程 学习笔记_11 --多进程服务器端

2023-10-07 大全 30 作者:考证青年

并发服务器

首先,我们来假设有下面这样两种类型的服务器:第一种,第一个连接请求的受理时间为1s,第50个连接请求的受理时间为50s,第100个连接请求的受理时间为100s。即同时很多客服端连接,需要依次排队受理,但只要受理了,他们的服务时间平均只需1s。第二种,所有连接请求的受理时间不超过1s,但平均服务时间要2~3s。

即使有可能延长服务时间,我们实际网络编程中也一般选择第二种方式,使其可以同时向所有发起请求的客服端提供服务,以提高平均满意度。而且,网络程序中数据通信时间比CPU运算时间占比更大,因此,我们更应该选择第二种方式的服务器–并发服务器,充分利用CUP。

下面我们来看看具有代表性的并发服务器端的实现模型和方法:

本章先来讲讲第一种:多进程服务器。这种方法不支持,因此将重点放在Linux平台…

多进程理论基础

pid_t pid;
pid = fork();
if(pid == 0)
{//子进程
}
else
{//父进程
}

int status;
pid_t pid = fork();
if(pid == 0)
{exit(1);  //或者return 0;
}
else
{wait(&status); //成功时返回终止的子进程ID,失败时返回-1if(WIFEXITED(status))//宏,子进程正常终止返回真printf("Child return: %d \n", WEXITSTATUS(status)); //宏,返回子进程的返回值
}

这就是用wait函数销毁子进程,但是要注意一点,wait函数是一个阻塞函数,如果没有已终止的子进程,那么程序将阻塞直到有子进程终止,因此要谨慎使用该函数。

销毁子进程方式2:使用函数

wait函数会引起程序阻塞,我们还可以使用函数,它可以不阻塞。

int status;
pid_t pid = fork();
if(pid == 0)
{sleep(15);return 24;
}
else
{//没有终止子进程返回0,有则返回终止子进程ID,失败时返回-1while(!waitpid(-1, &status, WNOHANG))//不会阻塞{sleep3);puts("sleep 3s");}if(WIFEXITED(status))printf("Child return %d \n", WEXITSTATUS(status));
}

//时间响应回调函数
void timeout(int sig)
{if(sig == SIGALRM)puts("Time out!");
}int main()
{signal(SIGALRM, timeout);//注册事件alarm(2);//2s后产生SIGALRM类型事件sleep(100);return 0;
}

2,实际中使用更多的注册函数(函数在UNIX系列的不同操作系统中可能存在区别,但函数则完全相同)

int (int signo, const act, );

成功时返回0,失败返回-1

void (*)(int); //回调函数

; //一般全初始化为0

int ; //初始化为0

实例如下:

void timeout(int sig)
{if(sig == SIGALRM)puts("Time out!");
}int main()
{//初始化结构体struct sigaction act;act.sa_handler = timeout;sigemptyset(&act.sa_mask); //设置sa_mask成员的所有位为0act.sa_flags = 0;sigaction(SIGALRM, &act, 0);//注册事件alarm(2);sleep(100);return 0;
}

好,以上就是我们所有的理论知识了,下面我们就可以实际运用中使用这些知识编写基于进程的并发服务器了。

基于多进程的并发服务器

//
//  main.cpp
//  hello_server
//
//  Created by app05 on 15-8-18.
//  Copyright (c) 2015年 app05. All rights reserved.
//
//每连一个客服端就对应开一个进程,即服务端子进程数与客服端连接数对应#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include #define BUF_SIZE 1024
void error_handling(char *message);
void read_childproc(int sig); //子进程销毁回调int main(int argc, const char * argv[]) {int serv_sock, clnt_sock;struct sockaddr_in serv_adr, clnt_adr;pid_t pid;struct sigaction act;socklen_t adr_sz;int str_len, state;char buf[BUF_SIZE];if (argc != 2) {printf("Usage: %s  \n", argv[0]);exit(1);}act.sa_handler = read_childproc;sigemptyset(&act.sa_mask);act.sa_flags = 0;state = sigaction(SIGCHLD, &act, 0); //注册子进程终止事件serv_sock = socket(PF_INET, SOCK_STREAM, 0);memset(&serv_adr, 0, sizeof(serv_adr));serv_adr.sin_family = AF_INET;serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);serv_adr.sin_port = htons(atoi(argv[1]));if(bind(serv_sock, (struct sockaddr *) &serv_adr, sizeof(serv_adr)) == -1)error_handling("bind() error");if(listen(serv_sock, 5) == -1)error_handling("listen() error");while (1) {adr_sz = sizeof(clnt_adr);clnt_sock = accept(serv_sock, (struct sockaddr *)&clnt_adr, &adr_sz);if(clnt_sock == -1)continue;elseputs("new client connected...");pid = fork(); //创建子进程if(pid == -1){close(clnt_sock);continue;}if (pid == 0) //子进程运行模块{/*子进程复制了父进程中的serv_sock句柄,它们都指向同一套接字, 只有两个都close后,这个套接字才会销毁。所以要注意一一对应close关闭。*/close(serv_sock);while((str_len = read(clnt_sock, buf, BUF_SIZE)) != 0)write(clnt_sock, buf, str_len);close(clnt_sock);puts("client disconnected...");return 0;}elseclose(clnt_sock);}close(serv_sock);return 0;
}void error_handling(char *message)
{fputs(message, stderr);fputc('\n', stderr);exit(1);
}void read_childproc(int sig)
{pid_t pid;int status;pid = waitpid(-1, &status, WNOHANG);  //销毁子进程printf("removed proc id: %d \n", pid);
}

//
//  main.cpp
//  hello_client
//
//  Created by app05 on 15-8-18.
//  Copyright (c) 2015年 app05. All rights reserved.
//
//客服端I/O分割,父进程负责接收数据,子进程负责发送数据,分开处理#include 
#include 
#include 
#include 
#include 
#include #define BUF_SIZE 1024
void error_handling(char *message);
void read_routime(int sock, char *buf);  //接收数据
void write_routine(int sock, char *buf);  //发送数据int main(int argc, const char * argv[]) {int sock;pid_t pid;char buf[BUF_SIZE];struct sockaddr_in serv_adr;if(argc != 3){printf("Usage: %s   \n", argv[0]);exit(1);}sock = socket(PF_INET, SOCK_STREAM, 0);if(sock == -1)error_handling("socket() error");memset(&serv_adr, 0, sizeof(serv_adr));serv_adr.sin_family = AF_INET;serv_adr.sin_addr.s_addr = inet_addr(argv[1]);serv_adr.sin_port = htons(atoi(argv[2]));if (connect(sock, (struct sockaddr *) &serv_adr, sizeof(serv_adr)) == -1)error_handling("connect() error");/*客服端I/O分割,父进程负责接收数据,子进程负责发送数据。这样分开可以提高频繁交换数据的程序性能,而不用像以前一样,只有完全接收上一条数据后才能发送下一条。*/pid = fork();if(pid == 0)write_routine(sock, buf);elseread_routime(sock, buf);close(sock);return 0;
}void error_handling(char *message)
{fputs(message, stderr);fputc('\n', stderr);exit(1);
}void read_routime(int sock, char *buf)
{while (1) {int str_len = read(sock, buf, BUF_SIZE);if(str_len == 0)return;buf[str_len] = 0;printf("Message from server: %s", buf);}
}void write_routine(int sock, char *buf)
{while (1){fgets(buf, BUF_SIZE, stdin);if (!strcmp(buf, "q\n") || !strcmp(buf, "Q\n")){shutdown(sock, SHUT_WR);}write(sock, buf, strlen(buf));}
}

这里写图片描述

这里写图片描述

关于我们

最火推荐

小编推荐

联系我们


版权声明:本站内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 88@qq.com 举报,一经查实,本站将立刻删除。备案号:桂ICP备2021009421号
Powered By Z-BlogPHP.
复制成功
微信号:
我知道了