在回射客户/服务器程序中,服务器只能处理一个客户端的请求,如何同时服务多个客户端呢?在未讲到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的请求相关推荐

  1. linux网络编程之socket编程(六)

    经过一个国庆长假,又有一段时间没有写博文了,今天继续对linux网络编程进行学习,如今的北京又全面进入雾霾天气了,让我突然想到了一句名句:"真爱生活,珍惜生命",好了,言归正传. ...

  2. linux网络编程之Socket编程

    (1)socket套接字 1)在linux环境下,socket用于表示进程间网络通信的特殊文件类型,其本质是内核借助缓冲区形成的伪文件(不占磁盘空间,除此之外还有二进制文件,管道,字符文件). 2)伪 ...

  3. 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 ...

  4. Linux网络编程之Socket套接字

    一.Socket到底是什么 socket 这个英文单词的原意是"插口""插槽", 在网络编程中,它的意思是可以通过插口接入的方式,快速完成网络连接和数据收发.你 ...

  5. linux网络编程之socket(十):shutdown 与 close 函数 的区别

    假设server和client 已经建立了连接,server调用了close, 发送FIN 段给client(其实不一定会发送FIN段,后面再说),此时server不能再通过socket发送和接收数据 ...

  6. Linux网络编程之socket文件传输示例

    本文所述示例程序是基于Linux平台的socket网络编程,实现文件传输功能.该示例是基于TCP流协议实现的socket网络文件传输程序.采用C语言编写.最终能够实现传输任何格式文件的文件传输程序. ...

  7. linux多网卡网络编程,Linux网络编程之Socket初探

    Socket由来 Socket 的英文原意就是"孔"或"插座",现在,作为 BSD UNIX 的进程通讯机制,取其后一种意义.一起看下网络编程里说的socket ...

  8. Linux网络编程之sockaddr与sockaddr_in,sockaddr_un结构体详细讲解

    Linux网络编程之sockaddr与sockaddr_in,sockaddr_un结构体详细讲解 (1)sockaddr struct sockaddr { unsigned  short  sa_ ...

  9. 网络编程之socket

    网络编程之socket 看到本篇文章的题目是不是很疑惑,what is this?,不要着急,但是记住一说网络编程,你就想socket,socket是实现网络编程的工具,那么什么是socket,什么是 ...

最新文章

  1. Java 源代码和 C 源代码的运行区别
  2. thinkphp5是不是php,我对ThinkPHP5和Laravel5的一些看法
  3. 13-Introduction to security
  4. JS正则表达式验证账号、手机号、电话和邮箱
  5. Vue006_事件处理
  6. tcp状态机-三次握手-四次挥手以及常见面试题
  7. python数字图像处理(5):图像的绘制
  8. 代码编辑框控件_某游戏控件遍历
  9. UVa 1620 懒惰的苏珊(逆序数)
  10. (二)PowerLink理论知识
  11. 8.4完成服务(Completion Services)
  12. 如何调用畅捷通接口_用友金蝶多组织多账套的不同数据如何合并?
  13. SQL Server 备份和还原
  14. 项目实训- 基于unity的2D多人乱斗闯关游戏设计与开发(综述)
  15. 一键实现自动化部署(灰度发布)实践
  16. AUTOCAD——中心缩放
  17. banner图片通用设置
  18. TLC5615 10位DA模块数模转换芯片驱动
  19. scrapy模拟登陆强智教务系统
  20. 3款移动应用数据统计分析平台对比

热门文章

  1. 我自己关于C语言,编译器,标准库,GUN glibc,CRT ,API之类的理解。
  2. 用记事本打造简易计算器
  3. wxpython pypi_Python iwx包_程序模块 - PyPI - Python中文网
  4. 女生做产品经理好吗_产品经理如何做产品架构设计
  5. hive表级权限控制_数据库权限管理:表、行、列级别的权限控制
  6. java并发编程之thread.join()方法详解
  7. PostgreSQL中查看版本的几种方式
  8. Python机器学习:线型回归法06最好的衡量线型回归法的指标RSquared
  9. 华为p40pro怎么用鸿蒙系统,华为p40pro鸿蒙系统正式版
  10. php携程语比,PHP 协程