linux网络编程之socket:使用fork并发处理多个client的请求
在回射客户/服务器程序中,服务器只能处理一个客户端的请求,如何同时服务多个客户端呢?在未讲到select/poll/epoll等高级IO之前,比较老土的办法是使用fork来实现。网络服务器通常用fork来同时服务多个客户端,父进程专门负责监听端口,每次accept一个新的客户端连接就fork出一个子进程专门服务这个客户端。但是子进程退出时会产生僵尸进程,父进程要注意处理SIGCHLD信号和调用wait清理僵尸进程,最简单的办法就是直接忽略SIGCHLD信号。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
|
/*************************************************************************
> File Name: echoser.c
> Author: Simba
> Mail: dameng34@163.com
> Created Time: Fri 01 Mar 2013 06:15:27 PM CST
************************************************************************/
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<unistd.h>
#include<stdlib.h>
#include<errno.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<string.h>
#include<signal.h>
#define ERR_EXIT(m)
do {
perror (m);
exit (EXIT_FAILURE);
} while (0)
void do_service( int );
int main( void )
{
signal (SIGCHLD, SIG_IGN);
int listenfd; //被动套接字(文件描述符),即只可以accept, 监听套接字
if ((listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
// listenfd = socket(AF_INET, SOCK_STREAM, 0)
ERR_EXIT( "socket error" );
struct sockaddr_in servaddr;
memset (&servaddr, 0, sizeof (servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(5188);
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
/* servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); */
/* inet_aton("127.0.0.1", &servaddr.sin_addr); */
int on = 1;
if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0)
ERR_EXIT( "setsockopt error" );
if (bind(listenfd, ( struct sockaddr *)&servaddr, sizeof (servaddr)) < 0)
ERR_EXIT( "bind error" );
if (listen(listenfd, SOMAXCONN) < 0) //listen应在socket和bind之后,而在accept之前
ERR_EXIT( "listen error" );
struct sockaddr_in peeraddr; //传出参数
socklen_t peerlen = sizeof (peeraddr); //传入传出参数,必须有初始值
int conn; // 已连接套接字(变为主动套接字,即可以主动connect)
pid_t pid;
while (1)
{
if ((conn = accept(listenfd, ( struct sockaddr *)&peeraddr, &peerlen)) < 0) //3次握手完成的序列
ERR_EXIT( "accept error" );
printf ( "recv connect ip=%s port=%dn" , inet_ntoa(peeraddr.sin_addr),
ntohs(peeraddr.sin_port));
pid = fork();
if (pid == -1)
ERR_EXIT( "fork error" );
if (pid == 0)
{
// 子进程
close(listenfd);
do_service(conn);
exit (EXIT_SUCCESS);
}
else
close(conn); //父进程
}
return 0;
}
void do_service( int conn)
{
char recvbuf[1024];
while (1)
{
memset (recvbuf, 0, sizeof (recvbuf));
int ret = read(conn, recvbuf, sizeof (recvbuf));
if (ret == 0) //客户端关闭了
{
printf ( "client closen" );
break ;
}
else if (ret == -1)
ERR_EXIT( "read error" );
fputs (recvbuf, stdout);
write(conn, recvbuf, ret);
}
}
|
上述程序利用了一点,就是父子进程共享打开的文件描述符,因为在子进程已经用不到监听描述符,故将其关闭,而连接描述符对父进程也没价值,将其关闭。当某个客户端关闭,则read 返回0,退出循环,子进程顺便exit,但如果没有设置对SIGCHLD信号的忽略,则因为父进程还没退出,故子进程会变成僵尸进程。
现在先运行server,再打开另外两个终端,运行client(直接用回射客户/服务器程序中的客户端程序),可以看到server输出如下:
simba@ubuntu:~/Documents/code/linux_programming/UNP/socket$ ./echoser_fork
recv connect ip=127.0.0.1 port=46452
recv connect ip=127.0.0.1 port=46453
在另一个终端ps一下:
simba@ubuntu:~$ ps aux | grep echoser
simba 3300 0.0 0.0 2008 280 pts/0 S+ 22:10 0:00 ./echoser_fork
simba 3303 0.0 0.0 2008 60 pts/0 S+ 22:10 0:00 ./echoser_fork
simba 3305 0.0 0.0 2008 60 pts/0 S+ 22:10 0:00 ./echoser_fork
simba 3313 0.0 0.0 4392 836 pts/3 S+ 22:12 0:00 grep --color=auto echoser
simba@ubuntu:~$
发现共有3个进程,其中一个是父进程处于监听中,另外两个是子进程处于对客户端服务中,现在ctrl+c 掉其中一个client,由上面的分析可知对应服务的子进程也会退出,而因为我们设置了父进程对SIGCHLD信号进行忽略,故不会产生僵尸进程,输出如下:
simba@ubuntu:~$ ps aux | grep echoser
simba 3300 0.0 0.0 2008 280 pts/0 S+ 22:10 0:00 ./echoser_fork
simba 3305 0.0 0.0 2008 60 pts/0 S+ 22:10 0:00 ./echoser_fork
simba 3321 0.0 0.0 4392 836 pts/3 S+ 22:13 0:00 grep --color=auto echoser
如果把29行代码注释掉,上述的情景输出可能为:
simba@ubuntu:~$ ps aux | grep echoser
simba 3125 0.0 0.0 2004 280 pts/0 S+ 21:38 0:00 ./echoser_fork
simba 3128 0.0 0.0 0 0 pts/0 Z+ 21:38 0:00 [echoser_fork] <defunct>
simba 3130 0.0 0.0 2004 60 pts/0 S+ 21:38 0:00 ./echoser_fork
simba 3141 0.0 0.0 4392 832 pts/3 S+ 21:40 0:00 grep --color=auto echoser
即子进程退出后变成了僵尸进程。
如果不想忽略SIGCHLD信号,则必须在信号处理函数中调用wait处理,但这里需要注意的是wait只能等待第一个退出的子进程,所以这里需要使用waitpid函数,如下所示:
1
2
3
4
5
6
7
8
9
10
11
|
signal (SIGCHLD, handler);
.....................
void handler( int sig)
{
/* wait(NULL); //只能等待第一个退出的子进程 */
/* 即使因为几个连接同时断开,信号因不能排队而父进程只收到一个信号
* 直到已经waitpid到所有子进程,返回0,才退出循环 */
while (waitpid(-1, NULL, WNOHANG) > 0)
;
}
|
实际上使用 while (wait(NULL) > 0) ; 也可以达到同样的效果。
来源: csdn 作者:Simba888888
转载于:https://www.cnblogs.com/huazhen/p/3419481.html
linux网络编程之socket:使用fork并发处理多个client的请求相关推荐
- linux网络编程之socket编程(六)
经过一个国庆长假,又有一段时间没有写博文了,今天继续对linux网络编程进行学习,如今的北京又全面进入雾霾天气了,让我突然想到了一句名句:"真爱生活,珍惜生命",好了,言归正传. ...
- linux网络编程之Socket编程
(1)socket套接字 1)在linux环境下,socket用于表示进程间网络通信的特殊文件类型,其本质是内核借助缓冲区形成的伪文件(不占磁盘空间,除此之外还有二进制文件,管道,字符文件). 2)伪 ...
- linux网络编程之socket(十一):套接字I/O超时设置方法和用select实现超时
一.使用alarm 函数设置超时 C++ Code 1 2 3 4 5 6 7 8 9 10 11 12 13 void handler( int sig) { } signal(SIGALRM ...
- Linux网络编程之Socket套接字
一.Socket到底是什么 socket 这个英文单词的原意是"插口""插槽", 在网络编程中,它的意思是可以通过插口接入的方式,快速完成网络连接和数据收发.你 ...
- linux网络编程之socket(十):shutdown 与 close 函数 的区别
假设server和client 已经建立了连接,server调用了close, 发送FIN 段给client(其实不一定会发送FIN段,后面再说),此时server不能再通过socket发送和接收数据 ...
- Linux网络编程之socket文件传输示例
本文所述示例程序是基于Linux平台的socket网络编程,实现文件传输功能.该示例是基于TCP流协议实现的socket网络文件传输程序.采用C语言编写.最终能够实现传输任何格式文件的文件传输程序. ...
- linux多网卡网络编程,Linux网络编程之Socket初探
Socket由来 Socket 的英文原意就是"孔"或"插座",现在,作为 BSD UNIX 的进程通讯机制,取其后一种意义.一起看下网络编程里说的socket ...
- Linux网络编程之sockaddr与sockaddr_in,sockaddr_un结构体详细讲解
Linux网络编程之sockaddr与sockaddr_in,sockaddr_un结构体详细讲解 (1)sockaddr struct sockaddr { unsigned short sa_ ...
- 网络编程之socket
网络编程之socket 看到本篇文章的题目是不是很疑惑,what is this?,不要着急,但是记住一说网络编程,你就想socket,socket是实现网络编程的工具,那么什么是socket,什么是 ...
最新文章
- Java 源代码和 C 源代码的运行区别
- thinkphp5是不是php,我对ThinkPHP5和Laravel5的一些看法
- 13-Introduction to security
- JS正则表达式验证账号、手机号、电话和邮箱
- Vue006_事件处理
- tcp状态机-三次握手-四次挥手以及常见面试题
- python数字图像处理(5):图像的绘制
- 代码编辑框控件_某游戏控件遍历
- UVa 1620 懒惰的苏珊(逆序数)
- (二)PowerLink理论知识
- 8.4完成服务(Completion Services)
- 如何调用畅捷通接口_用友金蝶多组织多账套的不同数据如何合并?
- SQL Server 备份和还原
- 项目实训- 基于unity的2D多人乱斗闯关游戏设计与开发(综述)
- 一键实现自动化部署(灰度发布)实践
- AUTOCAD——中心缩放
- banner图片通用设置
- TLC5615 10位DA模块数模转换芯片驱动
- scrapy模拟登陆强智教务系统
- 3款移动应用数据统计分析平台对比
热门文章
- 我自己关于C语言,编译器,标准库,GUN glibc,CRT ,API之类的理解。
- 用记事本打造简易计算器
- wxpython pypi_Python iwx包_程序模块 - PyPI - Python中文网
- 女生做产品经理好吗_产品经理如何做产品架构设计
- hive表级权限控制_数据库权限管理:表、行、列级别的权限控制
- java并发编程之thread.join()方法详解
- PostgreSQL中查看版本的几种方式
- Python机器学习:线型回归法06最好的衡量线型回归法的指标RSquared
- 华为p40pro怎么用鸿蒙系统,华为p40pro鸿蒙系统正式版
- php携程语比,PHP 协程