poll

本文介绍了一个基于Linux的网络编程示例,重点讲解如何通过非阻塞socket和I/O复用来处理大并发连接请求,有效避免SIGPIPE信号及TIME_WAIT状态带来的问题。
signal (SIGPIPE ,SIG_IGN) ;  

Linux网络编程 第12讲 tcp 11中状态

 


  1. SIGPIPE产生的原因:        

        如果客户端关闭套接字close 而服务器调用一次RST segment (TCP传输层) 如果服务器再次调用了write ,这个时候就会产生SIGPIPE信号

2 、TIMEOUT会对大并发服务器产生很大的影响

 由于服务器主动关闭连接,服务器就会进入TIME_WAIT ,所以我们要使client先关闭,服务器被动关闭

 3、如果客户端不活跃了,一些客户端不断开连接,这样子就会占用服务器的连接资源,服务器端也有要个机制来踢掉不活跃的连接

 

所以我们使用了nonblocking sokcet + I/O 复用

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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <sys/wait.h>
#include <poll.h>
 
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
 
#include <vector>
#include <iostream>
 
#define ERR_EXIT(m) \
        do \
        { \
                perror(m); \
                exit(EXIT_FAILURE); \
        } while(0)
 
typedef std::vector<struct pollfd> PollFdList;
 
int main(void)
{
    // 防止进程由于客户端的关闭产生SIG_PIPE信号而是服务器进程退出
    signal(SIGPIPE, SIG_IGN);
    //SIGCHLD 子进程状态改变后会产生此信号,父进程需要调用一个wait函数以确定发生了什么。
    //如果进程特定设置该信号的配置为SIG_IGN,则调用进程的子进程不产生僵死进程。
    //其实要想防止进程僵死,我们应该在他的父进程里面调用wait,以获取子进程的状态就不会让子进程变成僵死进程了
    signal(SIGCHLD, SIG_IGN);
 
    //int idlefd = open("/dev/null", O_RDONLY | O_CLOEXEC);
    int listenfd;
 
    //if ((listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
    //SOCK_NONBLOCK:非阻塞 ,2.6.2**才有
    //SOCK_CLOEXEC :当进程调用vfork或者fork产生进程时,继承下来的描述是关闭的状态
    //
    if ((listenfd = socket(PF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_TCP)) < 0)
        ERR_EXIT("socket");
 
    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);
 
    int on = 1;
    //SOL_SOCKET:在套接字级别上进行设置
    // SO_REUSEADDR,打开或关闭地址复用功能。
    if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
        ERR_EXIT("setsockopt");
 
    if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
        ERR_EXIT("bind");
    if (listen(listenfd, SOMAXCONN) < 0)
        ERR_EXIT("listen");
 
    struct pollfd pfd;
    pfd.fd = listenfd;
    //注册事件
    pfd.events = POLLIN;
 
    PollFdList pollfds;
    pollfds.push_back(pfd);
    //a positive number is returned
    int nready;
 
    struct sockaddr_in peeraddr;
    socklen_t peerlen;
    int connfd;
 
    while (1)
    {
        //可用POLLIN事件数
        //  -1 :无限等待,直到发送POLLIN事件
        nready = poll(&*pollfds.begin(), pollfds.size(), -1);
        if (nready == -1)
        {
            if (errno == EINTR)
                continue;
 
            ERR_EXIT("poll");
        }
        if (nready == 0)    // nothing happended
            continue;
 
        //如果POLLIN事件里面包含监听socket,那么监听socket将会
        if (pollfds[0].revents & POLLIN)
        {
            peerlen = sizeof(peeraddr);
            //SOCK_NONBLOCK : 非阻塞模式
            connfd = accept4(listenfd, (struct sockaddr*)&peeraddr,
                        &peerlen, SOCK_NONBLOCK | SOCK_CLOEXEC);
 
            if (connfd == -1)
                ERR_EXIT("accept4");
 
/*
            if (connfd == -1)
            {
                if (errno == EMFILE)
                {
                    close(idlefd);
                    idlefd = accept(listenfd, NULL, NULL);
                    close(idlefd);
                    idlefd = open("/dev/null", O_RDONLY | O_CLOEXEC);
                    continue;
                }
                else
                    ERR_EXIT("accept4");
            }
*/
 
            pfd.fd = connfd;
            pfd.events = POLLIN;
            pfd.revents = 0;
            pollfds.push_back(pfd);
            --nready;
 
            // 连接成功
            std::cout<<"ip="<<inet_ntoa(peeraddr.sin_addr)<<
                " port="<<ntohs(peeraddr.sin_port)<<std::endl;
            if (nready == 0)
                continue;
        }
 
        //std::cout<<pollfds.size()<<std::endl;
        //std::cout<<nready<<std::endl;、
        //过滤掉第一个socketfd,也就是监听sdocket
        for (PollFdList::iterator it=pollfds.begin()+1;
            it != pollfds.end() && nready >0; ++it)
        {
            //如果有POLLIN事件
                if (it->revents & POLLIN)
                {
                    --nready;
                    connfd = it->fd;
                    char buf[1024] = {0};
                    int ret = read(connfd, buf, 1024);
                    if (ret == -1)
                        ERR_EXIT("read");
                    //如果等于0 ,就是说客户端已关闭,既然是POLLIN事件,那么就表示有数据可读,但现在为0(),也就是所读到了文件的EOF
                    if (ret == 0)
                    {
                        std::cout<<"client close"<<std::endl;
                        it = pollfds.erase(it);
                        --it;
 
                        close(connfd);
                        continue;
                    }
 
                    std::cout<<buf;
                    write(connfd, buf, strlen(buf));
 
                }
        }
    }
 
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值