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