文章目錄

这是之前写的用epoll提供telnet服务的代码。

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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <stdlib.h>
#include <netinet/tcp.h>
#include <ctype.h>
#include <assert.h>
#define MAX_EVENTS 10
#define PORT 9999
//从buf中得到命令
void _get_command(char *buf, char *cmd) {
int i = 0;
int j = 0;
while (!isalpha(buf[i]))
i++;
while (buf[i] != '\0' && buf[i] != ' ' && buf[i] != '\r' && buf[i] != '\n') {
cmd[j++] = buf[i];
i++;
}
cmd[j] = '\0';
}
// 返回成功发送的字节数
int Send(int sock, void *buffer, int size)
{

int nsend = 0, total = 0;
int err;
if(NULL == buffer || 0 == size) {
return 0;
}
while(size > 0) {
nsend = write(sock, (char*)buffer + total, size);
if(nsend == -1) {
err = errno;
if(EINTR == err) {
printf("send data to socket[%d], error is %s[%d]\n",
sock, strerror(err), err);
} else {
printf("Fail to send data to socket[%d], error is %s[%d]",
sock, strerror(err), err);
return -1;
}
} else {
total += nsend;
size -= nsend;
}
}
return total;
}
//处理重置命令
void process_reset(int sock){
char mess[] = "reset successful\r\n";
Send(sock, mess, strlen(mess));
return;
}
//处理错误命令
void process_error(int sock) {
char error[] = "ERROR\r\n";
Send(sock, error, strlen(error));
return;
}
void process_stats(int sock){
char mess[] = "stats successful\r\n";
Send(sock, mess, strlen(mess));
return;
}

//处理退出命令
void process_quit(int sock) {
close(sock);
}

int process_command(int sock, char *buf) {
assert(buf != NULL);

/*char cmd[BUFSIZ];
_get_command(buf, cmd);
printf("command: %s\n", cmd);

if (strcmp("stats",cmd) == 0) {
process_stats(sock);
} else if (strcmp("reset", cmd) == 0) {
process_reset(sock);
} else if (strcmp("quit", cmd) == 0){
process_quit(sock);
} else {
process_error(sock);
}*/

Send(sock, buf, strlen(buf));

return 0;
}

//设置socket为非阻塞
void setnonblocking(int sockfd) {
int opts;
opts = fcntl(sockfd, F_GETFL);
if (opts < 0) {
perror("fcntl(F_GETFL)\n");
exit(1);
}
opts = (opts | O_NONBLOCK);
if (fcntl(sockfd, F_SETFL, opts) < 0) {
perror("fcntl(F_SETFL)\n");
exit(1);
}
}
int main() {
int listenfd, conn_sock, epfd;
char buf[BUFSIZ];
socklen_t clilen;
struct sockaddr_in cliaddr, servaddr;
struct epoll_event ev, events[MAX_EVENTS];
listenfd = socket(AF_INET, SOCK_STREAM, 0);
if (listenfd < 0) {
printf("create socket error\n");
return -1;
}
int on = 1;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
setnonblocking(listenfd);
struct linger {
int l_onoff; /* 0 = off, nozero = on */
int l_linger; /* linger time */
}lin;
lin.l_onoff = 1;
lin.l_linger = 0;
setsockopt(listenfd, SOL_SOCKET, SO_LINGER, (char*)&lin, sizeof(lin));
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(PORT);

if(bind(listenfd,(struct sockaddr*)&servaddr,sizeof(struct sockaddr))<0){
printf("bind error\n");
close(listenfd);
return -1;
}

if(listen(listenfd, 5) < 0) {
printf("listen error\n");
close(listenfd);
return -1;
}
epfd = epoll_create(MAX_EVENTS); //生成epoll专用的文件描述符
if (epfd == -1) {
printf("epoll_create\n");
return -1;
}
ev.events = EPOLLIN | EPOLLET; //设置处理的事件类型,设置为边沿触发
ev.data.fd = listenfd;
if (epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev) == -1) {
printf("epoll_ctl add listen_sock fail\n");
close(listenfd);
return -1;
}
while (1) {
int timeout = 1000;
int nfds = epoll_wait(epfd, events, MAX_EVENTS, timeout);
if (nfds == -1) {
printf("epoll_wait\n");
return -1;
}
for (int i = 0;i < nfds; ++i) {
int fd = events[i].data.fd;
if (fd == listenfd) { //监听事件
while ((conn_sock = accept(listenfd, NULL, NULL)) > 0) {//循环处理accept,这样可以处理多个连接在就绪队列中的情况
printf("accept %d\n", conn_sock);
setnonblocking(conn_sock);
ev.events = EPOLLIN | EPOLLET; //设置为边沿触发
ev.data.fd = conn_sock;
if (epoll_ctl(epfd, EPOLL_CTL_ADD, conn_sock, &ev) == -1) {
printf("epoll_ctl: add fail\n");
close(conn_sock);
return -1;
}
}
} else if (events[i].events & EPOLLIN) {//读事件,说明有数据从客户端发来
int n = 0;
int nread = 0;
while ((nread = read(fd, buf + n, BUFSIZ - n)) > 0) {
n += nread;
}
if (nread == -1 && errno != EAGAIN) {//读数据错误,关闭描述符
printf("read error\n");
close(fd); //关闭一个描述符,它会从epoll描述符集合中自动删除
continue;
}
if(nread == 0) { //客户端关闭连接,关闭相应的描述符
close(fd);
continue;
}
if( n > 0) {
process_command(fd, buf);
memset(buf, 0, sizeof(buf));
}
}
}
}
}

打赏作者

文章目錄