1 #include <unistd.h>
2 #include <sys/types.h>
3 #include <fcntl.h>
4 #include <sys/socket.h>
5 #include <netinet/in.h>
6 #include <arpa/inet.h>
7 #include <signal.h>
8 #include <fcntl.h>
9 #include <sys/wait.h>
10 #include <sys/epoll.h>
11
12 #include <stdlib.h>
13 #include <stdio.h>
14 #include <errno.h>
15 #include <string.h>
16
17 #include <vector>
18 #include <algorithm>
19 #include <iostream>
20
21 typedef std::vector<struct epoll_event> EventList;
22
23 #define ERR_EXIT(m) \
24 do \
25 { \
26 perror(m); \
27 exit(EXIT_FAILURE); \
28 } while(0)
29
30 int main(void)
31 {
32 // TCP是全双工的信道, 可以看作两条单工信道, TCP连接两端的两个端点各负责一条. 当对端调用close时, 虽然本意是关闭整个两条信道,
33 // 但本端只是收到FIN包. 按照TCP协议的语义, 表示对端只是关闭了其所负责的那一条单工信道, 仍然可以继续接收数据. 也就是说, 因为TCP协议的限制,
34 // 一个端点无法获知对端的socket是调用了close还是shutdown.
35 // 对一个已经收到FIN包的socket调用read方法,
36 // 如果接收缓冲已空, 则返回0, 这就是常说的表示连接关闭. 但第一次对其调用write方法时, 如果发送缓冲没问题, 会返回正确写入(发送).
37 // 但发送的报文会导致对端发送RST报文, 因为对端的socket已经调用了close, 完全关闭, 既不发送, 也不接收数据. 所以,
38 // 第二次调用write方法(假设在收到RST之后), 会生成SIGPIPE信号, 导致进程退出.
39 // 为了避免进程退出, 可以捕获SIGPIPE信号, 或者忽略它, 给它设置SIG_IGN信号处理函数:
40 // 这样, 第二次调用write方法时, 会返回-1, 同时errno置为SIGPIPE. 程序便能知道对端已经关闭.
41 signal(SIGPIPE, SIG_IGN);//防止进程退出
42 //忽略SIGCHLD信号,这常用于并发服务器的性能的一个技巧
43 //因为并发服务器常常fork很多子进程,子进程终结之后需要
44 //服务器进程去wait清理资源。如果将此信号的处理方式设为
45 //忽略,可让内核把僵尸子进程转交给init进程去处理,省去了
46 //大量僵尸进程占用系统资源。(Linux Only)
47 signal(SIGCHLD, SIG_IGN);
48
49 int idlefd = open("/dev/null", O_RDONLY | O_CLOEXEC);
50 int listenfd;
51 //if ((listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
52 if ((listenfd = socket(PF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_TCP)) < 0)
53 ERR_EXIT("socket");
54
55 struct sockaddr_in servaddr;
56 memset(&servaddr, 0, sizeof(servaddr));
57 servaddr.sin_family = AF_INET;
58 servaddr.sin_port = htons(5188);
59 servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
60
61 int on = 1;
62 if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
63 ERR_EXIT("setsockopt");
64
65 if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
66 ERR_EXIT("bind");
67 if (listen(listenfd, SOMAXCONN) < 0)
68 ERR_EXIT("listen");
69
70 //create epoll object
71 int epollfd;
72 epollfd = epoll_create1(EPOLL_CLOEXEC);
73
74 //add EPOLLIN event to epoll
75 struct epoll_event event;
76 event.data.fd = listenfd;
77 event.events = EPOLLIN/* | EPOLLET*/;
78 epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &event);
79
80 EventList events(16);
81 struct sockaddr_in peeraddr;
82 socklen_t peerlen;
83 int connfd;
84
85 std::vector<int> clients;
86
87 int nready;
88 while (1)
89 {
90 //return active event
91 nready = epoll_wait(epollfd, &*events.begin(), static_cast<int>(events.size()), -1);
92 if (nready == -1)
93 {
94 if (errno == EINTR)
95 continue;
96
97 ERR_EXIT("epoll_wait");
98 }
99 if (nready == 0) // nothing happended
100 continue;
101
102 //double capacity
103 if ((size_t)nready == events.size())
104 events.resize(events.size()*2);
105
106 //treat all active event
107 for (int i = 0; i < nready; ++i)
108 {
109 //如果是主socket的事件的话,则表示有新连接进入了,进行新连接的处理
110 if (events[i].data.fd == listenfd)
111 {
112 peerlen = sizeof(peeraddr);
113 connfd = ::accept4(listenfd, (struct sockaddr*)&peeraddr,
114 &peerlen, SOCK_NONBLOCK | SOCK_CLOEXEC);
115
116 if (connfd == -1)
117 {
118 //防止文件句柄超过最大数量
119 if (errno == EMFILE)
120 {
121 close(idlefd);
122 idlefd = accept(listenfd, NULL, NULL);
123 close(idlefd);
124 idlefd = open("/dev/null", O_RDONLY | O_CLOEXEC);
125 continue;
126 }
127 else
128 ERR_EXIT("accept4");
129 }
130
131
132 std::cout<<"ip="<<inet_ntoa(peeraddr.sin_addr)<<
133 " port="<<ntohs(peeraddr.sin_port)<<std::endl;
134
135 clients.push_back(connfd);
136
137 event.data.fd = connfd;
138 event.events = EPOLLIN/* | EPOLLET*/;
139 epoll_ctl(epollfd, EPOLL_CTL_ADD, connfd, &event);
140 }
141 else if (events[i].events & EPOLLIN)
142 {
143 connfd = events[i].data.fd;
144 if (connfd < 0)
145 continue;
146
147 char buf[1024] = {0};
148 int ret = read(connfd, buf, 1024);
149 if (ret == -1)
150 ERR_EXIT("read");
151 if (ret == 0)
152 {
153 std::cout<<"client close"<<std::endl;
154 close(connfd);
155 event = events[i];
156 epoll_ctl(epollfd, EPOLL_CTL_DEL, connfd, &event);
157 clients.erase(std::remove(clients.begin(), clients.end(), connfd), clients.end());
158 continue;
159 }
160
161 std::cout<<buf;
162 write(connfd, buf, strlen(buf));
163 }
164
165 }
166 }
167
168 return 0;
169 }