Linux下网络socket编程——实现服务器(select)与多个客户端通信
置顶 2017年06月23日 14:44:37 阅读数:3225 标签: socket编程服务器与多个客户端通epoll多路复用C语言网络编程 更多
个人分类: socket编程
一、关于socket通信
服务器端工作流程:
- 调用 socket() 函数创建套接字 用 bind() 函数将创建的套接字与服务端IP地址绑定
- 调用listen()函数监听socket() 函数创建的套接字,等待客户端连接 当客户端请求到来之后
- 调用 accept()函数接受连接请求,返回一个对应于此连接的新的套接字,做好通信准备
- 调用 write()/read() 函数和 send()/recv()函数进行数据的读写,通过 accept() 返回的套接字和客户端进行通信 关闭socket(close)
客户端工作流程:
- 调用 socket() 函数创建套接字
- 调用 connect() 函数连接服务端
- 调用write()/read() 函数或者 send()/recv() 函数进行数据的读写
- 关闭socket(close)
二、用select实现服务器端编程:
select函数楼主在之前文章中(select函数用法)已经提及,不在多做缀述。下面贴上服务器端代码servce.c
<span style="color:#000000"><code><span style="color:#009900">#include <stdio.h></span>
<span style="color:#009900">#include <netinet/in.h> <span style="color:#009900">//for souockaddr_in</span></span>
<span style="color:#009900">#include <sys/types.h> </span>
<span style="color:#009900">#include <sys/socket.h></span>
<span style="color:#009900">#include <errno.h></span>
<span style="color:#009900">#include <stdlib.h></span>
<span style="color:#009900">#include <arpa/inet.h></span>
<span style="color:#880000">//for select</span>
<span style="color:#009900">#include <sys/time.h></span>
<span style="color:#009900">#include <sys/types.h></span>
<span style="color:#009900">#include <unistd.h></span>
<span style="color:#009900">#include <sys/select.h></span>
<span style="color:#009900">#include <strings.h> <span style="color:#009900">//for bzero</span></span>
<span style="color:#009900">#include <string.h></span>
<span style="color:#009900">#define BUFF_SIZE 1024</span>
<span style="color:#009900">#define backlog 7</span>
<span style="color:#009900">#define ser_port 11277</span>
<span style="color:#009900">#define CLI_NUM 3</span>
<span style="color:#000088">int</span> client_fds[CLI_NUM];
<span style="color:#000088">int</span> main(<span style="color:#000088">int</span> agrc,<span style="color:#000088">char</span> **argv)
{
<span style="color:#000088">int</span> ser_souck_fd;
<span style="color:#000088">int</span> i;
<span style="color:#000088">char</span> input_message[BUFF_SIZE];
<span style="color:#000088">char</span> resv_message[BUFF_SIZE];
<span style="color:#000088">struct</span> sockaddr_in ser_addr;
ser_addr.sin_family= AF_INET; <span style="color:#880000">//IPV4</span>
ser_addr.sin_port = htons(ser_port);
ser_addr.sin_addr.s_addr = INADDR_ANY; <span style="color:#880000">//指定的是所有地址</span>
<span style="color:#880000">//creat socket</span>
<span style="color:#000088">if</span>( (ser_souck_fd = socket(AF_INET,SOCK_STREAM,<span style="color:#006666">0</span>)) < <span style="color:#006666">0</span> )
{
perror(<span style="color:#009900">"creat failure"</span>);
<span style="color:#000088">return</span> -<span style="color:#006666">1</span>;
}
<span style="color:#880000">//bind soucket</span>
<span style="color:#000088">if</span>(bind(ser_souck_fd, (<span style="color:#000088">const</span> <span style="color:#000088">struct</span> sockaddr *)&ser_addr,<span style="color:#000088">sizeof</span>(ser_addr)) < <span style="color:#006666">0</span>)
{
perror(<span style="color:#009900">"bind failure"</span>);
<span style="color:#000088">return</span> -<span style="color:#006666">1</span>;
}
<span style="color:#880000">//listen</span>
<span style="color:#000088">if</span>(listen(ser_souck_fd, backlog) < <span style="color:#006666">0</span>)
{
perror(<span style="color:#009900">"listen failure"</span>);
<span style="color:#000088">return</span> -<span style="color:#006666">1</span>;
}
<span style="color:#880000">//fd_set</span>
fd_set ser_fdset;
<span style="color:#000088">int</span> max_fd=<span style="color:#006666">1</span>;
<span style="color:#000088">struct</span> timeval mytime;
<span style="color:#4f4f4f">printf</span>(<span style="color:#009900">"wait for client connnect!\n"</span>);
<span style="color:#000088">while</span>(<span style="color:#006666">1</span>)
{
mytime.tv_sec=<span style="color:#006666">27</span>;
mytime.tv_usec=<span style="color:#006666">0</span>;
FD_ZERO(&ser_fdset);
<span style="color:#880000">//add standard input</span>
FD_SET(<span style="color:#006666">0</span>,&ser_fdset);
<span style="color:#000088">if</span>(max_fd < <span style="color:#006666">0</span>)
{
max_fd=<span style="color:#006666">0</span>;
}
<span style="color:#880000">//add serverce</span>
FD_SET(ser_souck_fd,&ser_fdset);
<span style="color:#000088">if</span>(max_fd < ser_souck_fd)
{
max_fd = ser_souck_fd;
}
<span style="color:#880000">//add client </span>
<span style="color:#000088">for</span>(i=<span style="color:#006666">0</span>;i<CLI_NUM;i++) <span style="color:#880000">//用数组定义多个客户端fd</span>
{
<span style="color:#000088">if</span>(client_fds[i]!=<span style="color:#006666">0</span>)
{
FD_SET(client_fds[i],&ser_fdset);
<span style="color:#000088">if</span>(max_fd < client_fds[i])
{
max_fd = client_fds[i];
}
}
}
<span style="color:#880000">//select多路复用</span>
<span style="color:#000088">int</span> ret = select(max_fd + <span style="color:#006666">1</span>, &ser_fdset, NULL, NULL, &mytime);
<span style="color:#000088">if</span>(ret < <span style="color:#006666">0</span>)
{
perror(<span style="color:#009900">"select failure\n"</span>);
<span style="color:#000088">continue</span>;
}
<span style="color:#000088">else</span> <span style="color:#000088">if</span>(ret == <span style="color:#006666">0</span>)
{
<span style="color:#4f4f4f">printf</span>(<span style="color:#009900">"time out!"</span>);
<span style="color:#000088">continue</span>;
}
<span style="color:#000088">else</span>
{
<span style="color:#000088">if</span>(FD_ISSET(<span style="color:#006666">0</span>,&ser_fdset)) <span style="color:#880000">//标准输入是否存在于ser_fdset集合中(也就是说,检测到输入时,做如下事情)</span>
{
<span style="color:#4f4f4f">printf</span>(<span style="color:#009900">"send message to"</span>);
bzero(input_message,BUFF_SIZE);
fgets(input_message,BUFF_SIZE,stdin);
<span style="color:#000088">for</span>(i=<span style="color:#006666">0</span>;i<CLI_NUM;i++)
{
<span style="color:#000088">if</span>(client_fds[i] != <span style="color:#006666">0</span>)
{
<span style="color:#4f4f4f">printf</span>(<span style="color:#009900">"client_fds[%d]=%d\n"</span>, i, client_fds[i]);
send(client_fds[i], input_message, BUFF_SIZE, <span style="color:#006666">0</span>);
}
}
}
<span style="color:#000088">if</span>(FD_ISSET(ser_souck_fd, &ser_fdset))
{
<span style="color:#000088">struct</span> sockaddr_in client_address;
socklen_t address_len;
<span style="color:#000088">int</span> client_sock_fd = accept(ser_souck_fd,(<span style="color:#000088">struct</span> sockaddr *)&client_address, &address_len);
<span style="color:#000088">if</span>(client_sock_fd > <span style="color:#006666">0</span>)
{
<span style="color:#000088">int</span> flags=-<span style="color:#006666">1</span>;
<span style="color:#880000">//一个客户端到来分配一个fd,CLI_NUM=3,则最多只能有三个客户端,超过4以后跳出for循环,flags重新被赋值为-1</span>
<span style="color:#000088">for</span>(i=<span style="color:#006666">0</span>;i<CLI_NUM;i++)
{
<span style="color:#000088">if</span>(client_fds[i] == <span style="color:#006666">0</span>)
{
flags=i;
client_fds[i] = client_sock_fd;
<span style="color:#000088">break</span>;
}
}
<span style="color:#000088">if</span> (flags >= <span style="color:#006666">0</span>)
{
<span style="color:#4f4f4f">printf</span>(<span style="color:#009900">"new user client[%d] add sucessfully!\n"</span>,flags);
}
<span style="color:#000088">else</span> <span style="color:#880000">//flags=-1</span>
{
<span style="color:#000088">char</span> full_message[]=<span style="color:#009900">"the client is full!can't join!\n"</span>;
bzero(input_message,BUFF_SIZE);
<span style="color:#4f4f4f">strncpy</span>(input_message, full_message,<span style="color:#006666">100</span>);
send(client_sock_fd, input_message, BUFF_SIZE, <span style="color:#006666">0</span>);
}
}
}
}
<span style="color:#880000">//deal with the message</span>
<span style="color:#000088">for</span>(i=<span style="color:#006666">0</span>; i<CLI_NUM; i++)
{
<span style="color:#000088">if</span>(client_fds[i] != <span style="color:#006666">0</span>)
{
<span style="color:#000088">if</span>(FD_ISSET(client_fds[i],&ser_fdset))
{
bzero(resv_message,BUFF_SIZE);
<span style="color:#000088">int</span> byte_num=read(client_fds[i],resv_message,BUFF_SIZE);
<span style="color:#000088">if</span>(byte_num > <span style="color:#006666">0</span>)
{
<span style="color:#4f4f4f">printf</span>(<span style="color:#009900">"message form client[%d]:%s\n"</span>, i, resv_message);
}
<span style="color:#000088">else</span> <span style="color:#000088">if</span>(byte_num < <span style="color:#006666">0</span>)
{
<span style="color:#4f4f4f">printf</span>(<span style="color:#009900">"rescessed error!"</span>);
}
<span style="color:#880000">//某个客户端退出</span>
<span style="color:#000088">else</span> <span style="color:#880000">//cancel fdset and set fd=0</span>
{
<span style="color:#4f4f4f">printf</span>(<span style="color:#009900">"clien[%d] exit!\n"</span>,i);
FD_CLR(client_fds[i], &ser_fdset);
client_fds[i] = <span style="color:#006666">0</span>;
<span style="color:#880000">// printf("clien[%d] exit!\n",i);</span>
<span style="color:#000088">continue</span>; <span style="color:#880000">//这里如果用break的话一个客户端退出会造成服务器也退出。 </span>
}
}
}
}
}
<span style="color:#000088">return</span> <span style="color:#006666">0</span>;
}
</code></span>
- 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
- 206
- 207
- 208
- 209
select实现多路复用,多路复用,顾名思义,就是说各做各的事,标准输入事件到来,有相关函数处理。服务器处理服务器的事件,客户端到来时有相关函数对其进行处理,通过select遍历各fd的读写情况,就不用担心阻塞了。
三、用epoll实现客户端编程:
1、客户端程序(epoll_client.c):
<span style="color:#000000"><code><span style="color:#009900">#include<stdio.h> </span>
<span style="color:#009900">#include<stdlib.h> </span>
<span style="color:#009900">#include<netinet/in.h> </span>
<span style="color:#009900">#include<sys/socket.h> </span>
<span style="color:#009900">#include<arpa/inet.h> </span>
<span style="color:#009900">#include<string.h> </span>
<span style="color:#009900">#include<unistd.h> </span>
<span style="color:#009900">#include <sys/epoll.h></span>
<span style="color:#009900">#include <errno.h></span>
<span style="color:#009900">#include <fcntl.h></span>
<span style="color:#009900">#define BUFFER_SIZE 1024 </span>
<span style="color:#000088">int</span> main(<span style="color:#000088">int</span> argc, <span style="color:#000088">const</span> <span style="color:#000088">char</span> * argv[])
{
<span style="color:#000088">int</span> i,n;
<span style="color:#000088">int</span> connfd,sockfd;
<span style="color:#000088">struct</span> epoll_event ev,events[<span style="color:#006666">20</span>]; <span style="color:#880000">//ev用于注册事件,数组用于回传要处理的事件</span>
<span style="color:#000088">int</span> epfd=epoll_create(<span style="color:#006666">256</span>);<span style="color:#880000">//创建一个epoll的句柄,其中256为你epoll所支持的最大句柄数</span>
<span style="color:#000088">struct</span> sockaddr_in client_addr;
<span style="color:#000088">struct</span> sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(<span style="color:#006666">11277</span>);
server_addr.sin_addr.s_addr =INADDR_ANY;
bzero(&(server_addr.sin_zero), <span style="color:#006666">8</span>);
<span style="color:#000088">int</span> server_sock_fd = socket(AF_INET, SOCK_STREAM, <span style="color:#006666">0</span>);
ev.data.fd=server_sock_fd;<span style="color:#880000">//设置与要处理的事件相关的文件描述符</span>
ev.events=EPOLLIN|EPOLLET;<span style="color:#880000">//设置要处理的事件类型</span>
epoll_ctl(epfd,EPOLL_CTL_ADD,server_sock_fd,&ev);<span style="color:#880000">//注册epoll事件</span>
<span style="color:#000088">if</span>(server_sock_fd == -<span style="color:#006666">1</span>)
{
perror(<span style="color:#009900">"socket error"</span>);
<span style="color:#000088">return</span> <span style="color:#006666">1</span>;
}
<span style="color:#000088">char</span> recv_msg[BUFFER_SIZE];
<span style="color:#000088">char</span> input_msg[BUFFER_SIZE];
<span style="color:#000088">if</span>(connect(server_sock_fd, (<span style="color:#000088">struct</span> sockaddr *)&server_addr, <span style="color:#000088">sizeof</span>(<span style="color:#000088">struct</span> sockaddr_in)) == <span style="color:#006666">0</span>)
{
<span style="color:#000088">for</span>(;;)
{
<span style="color:#000088">int</span> nfds=epoll_wait(epfd,events,<span style="color:#006666">20</span>,<span style="color:#006666">500</span>);<span style="color:#880000">//等待epoll事件的发生</span>
<span style="color:#000088">for</span>(i=<span style="color:#006666">0</span>;i<nfds;++i)
{
<span style="color:#000088">if</span>(events[i].events&EPOLLOUT) <span style="color:#880000">//有数据发送,写socket</span>
{
bzero(input_msg, BUFFER_SIZE);
fgets(input_msg, BUFFER_SIZE, stdin);
sockfd = events[i].data.fd;
write(sockfd, recv_msg, n);
ev.data.fd=sockfd;
ev.events=EPOLLIN|EPOLLET;
epoll_ctl(epfd,EPOLL_CTL_ADD,sockfd,&ev);
}
<span style="color:#000088">else</span> <span style="color:#000088">if</span>(events[i].events&EPOLLIN)<span style="color:#880000">//有数据到来,读socket</span>
{
bzero(recv_msg, BUFFER_SIZE);
<span style="color:#000088">if</span>((n = read(server_sock_fd, recv_msg, BUFFER_SIZE)) <<span style="color:#006666">0</span> )
{
<span style="color:#4f4f4f">printf</span>(<span style="color:#009900">"read error!"</span>);
}
ev.data.fd=server_sock_fd;
ev.events=EPOLLOUT|EPOLLET;
<span style="color:#4f4f4f">printf</span>(<span style="color:#009900">"%s\n"</span>,recv_msg);
}
}
}
}
<span style="color:#000088">return</span> <span style="color:#006666">0</span>;
} </code></span>
- 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
2、关于epoll函数:
相比于select,epoll最大的好处在于它不会随着监听fd数目的增长而降低效率。因为在内核中的select实现中,它是采用轮询来处理的,轮询的fd数目越多,自然耗时越多。并且,在linux/posix_types.h头文件有这样的声明:
#define __FD_SETSIZE 1024
表示select最多同时监听1024个fd
一共三个函数:
1、 int epoll_create (int size);
创建一个epoll的句柄
*size用来告诉内核这个监听的数目一共有多大。这个参数不同于select()中的第一个参数,给出最大监听的fd+1的值。需要注意的是,当创建好epoll句柄后,它就是会占用一个fd值,在linux下如果查看/proc/进程id/fd/,是能够看到这个fd的,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。
2、 int epoll_ctl (int epfd , int op, int fd, struct epoll_event *event);
<span style="color:#000000"><code> epoll的事件注册函数,它不同与select()是在监听事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型。第一个参数是epoll_create()的返回值,第二个参数表示动作,用三个宏来表示:
</code></span>
- 1
- 2
- EPOLL_CTL_ADD:注册新的fd到epfd中;
- EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
- EPOLL_CTL_DEL:从epfd中删除一个fd;
第三个参数是需要监听的fd
第四个参数是告诉内核需要监听什么事,struct epoll_event结构如下:
<span style="color:#000000"><code><span style="color:#000088">struct</span> epoll_event {
__uint32_t events; <span style="color:#880000">/* Epoll events */</span>
epoll_data_t data; <span style="color:#880000">/* User data variable */</span>
};</code></span>
- 1
- 2
- 3
- 4
<span style="color:#000000"><code><span style="color:#000088">typedef</span> <span style="color:#000088">union</span> epoll_data {
<span style="color:#000088">void</span> *ptr;
<span style="color:#000088">int</span> fd;
__uint32_t u32;
__uint64_t u64;
} epoll_data_t;</code></span>
- 1
- 2
- 3
- 4
- 5
- 6
events可以是以下几个宏的集合:
- EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
- EPOLLOUT:表示对应的文件描述符可以写;
- EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
- EPOLLERR:表示对应的文件描述符发生错误;
- EPOLLHUP:表示对应的文件描述符被挂断;
- EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。
- EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里
3、 int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
- 等待事件的产生,类似于select()调用。
- 参数events用来从内核得到事件的集合,
- maxevents告之内核这个events有多大,这个 maxevents的值不能大于创建epoll_create()时的size,
- 参数timeout是超时时间(毫秒,0会立即返回,-1将不确定,也有说法说是永久阻塞)。该函数返回需要处理的事件数目,如返回0表示已超时。
使用步骤:
<span style="color:#000000"><code> <1>首先通过create_epoll(int maxfds)来创建一个epoll的句柄,其中maxfds为你epoll所支持的最大句柄数。这个函数会返回一个新的epoll句柄,之后的所有操作将通过这个句柄来进行操作。在用完之后,记得用close()来关闭这个创建出来的epoll句柄。
<2>然后每一帧的调用epoll_wait (int epfd, epoll_event events, int max events, int timeout) 来查询所有的网络接口。
<3>kdpfd为用epoll_create创建之后的句柄,events是一个epoll_event*的指针,当epoll_wait这个函数操作成功之后,epoll_events里面将储存所有的读写事件。max_events是当前需要监听的所有socket句柄数。最后一个timeout是 epoll_wait的超时,为0的时候表示马上返回,为-1的时候表示一直等下去,直到有事件范围,为任意正整数的时候表示等这么长的时间,如果一直没有事件,则返回。一般如果网络主循环是单独的线程的话,可以用-1来等,这样可以保证一些效率,如果是和主逻辑在同一个线程的话,则可以用0来保证主循环的效率。
epoll_wait返回之后应该是一个循环,遍历所有的事件。
</code></span>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
基本上都是如下的框架:
<span style="color:#000000"><code><span style="color:#000088">for</span>( ; ; )
{
nfds = epoll_wait(epfd,events,<span style="color:#006666">20</span>,<span style="color:#006666">500</span>);
<span style="color:#000088">for</span>(i=<span style="color:#006666">0</span>;i<nfds;++i)
{
<span style="color:#000088">if</span>(events[i].data.fd==listenfd) <span style="color:#880000">//有新的连接</span>
{
connfd = accept(listenfd,(sockaddr *)&clientaddr, &clilen); <span style="color:#880000">//accept这个连接</span>
ev.data.fd=connfd;
ev.events=EPOLLIN|EPOLLET;
epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev); <span style="color:#880000">//将新的fd添加到epoll的监听队列中</span>
}
<span style="color:#000088">else</span> <span style="color:#000088">if</span>( events[i].events&EPOLLIN ) <span style="color:#880000">//接收到数据,读socket</span>
{
n = read(sockfd, line, MAXLINE)) < <span style="color:#006666">0</span> <span style="color:#880000">//读</span>
ev.data.ptr = md; <span style="color:#880000">//md为自定义类型,添加数据</span>
ev.events=EPOLLOUT|EPOLLET;
epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);<span style="color:#880000">//修改标识符,等待下一个循环时发送数据,异步处理的精髓</span>
}
<span style="color:#000088">else</span> <span style="color:#000088">if</span>(events[i].events&EPOLLOUT) <span style="color:#880000">//有数据待发送,写socket</span>
{
<span style="color:#000088">struct</span> myepoll_data* md = (myepoll_data*)events[i].data.ptr; <span style="color:#880000">//取数据</span>
sockfd = md->fd;
send( sockfd, md->ptr, <span style="color:#4f4f4f">strlen</span>((<span style="color:#000088">char</span>*)md->ptr), <span style="color:#006666">0</span> ); <span style="color:#880000">//发送数据</span>
ev.data.fd=sockfd;
ev.events=EPOLLIN|EPOLLET;
epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev); <span style="color:#880000">//修改标识符,等待下一个循环时接收数据</span>
}
<span style="color:#000088">else</span>
{
<span style="color:#880000">//其他的处理</span>
}
}
}</code></span>