上节回顾
上一节介绍了 UDP server 与 UDP Client 简单的双向 交互。
但是遇到多 Client 与 Server 连接时 UDP Server 就处理不了。
本节介绍 使用 epoll 来实现多连接。
使用udp 与 epoll 结合
与 TCP 不同的是 UDP 需要自己创建新的 socket 而不是 系统接口的accept 返回新的fd。
Server 端代码
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <pthread.h>
#include <assert.h>
#define SO_REUSEPORT 15
#define MAXBUF 10240
#define MAXEPOLLSIZE 100
int flag = 0;
int count = 0;
char buffer[MAXBUF];
int read_data(int sd)
{
//char recvbuf[MAXBUF + 1];
int ret;
struct sockaddr_in client_addr;
socklen_t cli_len=sizeof(client_addr);
bzero(buffer, MAXBUF);
ret = recvfrom(sd, buffer, MAXBUF, 0, (struct sockaddr *)&client_addr, &cli_len);
//fflush(stdout);
return ret;
}
int send_data(int sd)
{
// char sndbuf[MAXBUF + 1];
int ret;
struct sockaddr_in client_addr;
socklen_t cli_len=sizeof(client_addr);
ret = sendto(sd, buffer, MAXBUF, 0, (struct sockaddr *)&client_addr, cli_len);
if (ret > 0) {
printf("send: %s from %d\n", buffer, sd);
} else {
printf("send err:%s %d\n", strerror(errno), ret);
}
return ret;
}
int udp_accept(int sd, struct sockaddr_in my_addr)
{
int new_sd = -1;
int ret = 0;
int reuse = 1;
char buf[16];
struct sockaddr_in peer_addr;
socklen_t cli_len = sizeof(peer_addr);
ret = recvfrom(sd, buf, 17, 0, (struct sockaddr *)&peer_addr, &cli_len);
if (ret < 0) {
return -1;
}
buf[ret] ='\0';
printf("ret: %d, buf: %s\n", ret, buf);
if ((new_sd = socket(PF_INET, SOCK_DGRAM, 0)) == -1) {
perror("child socket");
exit(1);
} else {
printf("%d, parent:%d new:%d\n",count++, sd, new_sd); //1023
}
ret = setsockopt(new_sd, SOL_SOCKET, SO_REUSEADDR, &reuse,sizeof(reuse));
if (ret) {
exit(1);
}
ret = setsockopt(new_sd, SOL_SOCKET, SO_REUSEPORT, &reuse, sizeof(reuse));
if (ret) {
exit(1);
}
//my_addr.sin_port += count;
ret = bind(new_sd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr));
if (ret){
perror("chid bind");
exit(1);
} else {
}
peer_addr.sin_family = PF_INET;
//printf("aaa:%s\n", inet_ntoa(peer_addr.sin_addr));
if (connect(new_sd, (struct sockaddr *) &peer_addr, sizeof(struct sockaddr)) == -1) {
perror("chid connect");
exit(1);
} else {
}
char buffer[1024] = {0};
memset(buffer, 0, 1024);
sprintf(buffer, "hello from Server");
sendto(new_sd, buffer, strlen(buffer), 0, (struct sockaddr *) &peer_addr, sizeof(struct sockaddr_in));
out:
return new_sd;
}
int main(int argc, char **argv)
{
int listener, kdpfd, nfds, n, curfds,new_sd;
socklen_t len;
struct sockaddr_in my_addr, their_addr;
unsigned int port;
struct epoll_event ev;
struct epoll_event events[MAXEPOLLSIZE];
int opt = 1;
int ret = 0;
port = 1234;
if ((listener = socket(PF_INET, SOCK_DGRAM, 0)) == -1) {
perror("socket");
exit(1);
} else {
printf("socket OK:%d\n",listener);
}
ret = setsockopt(listener,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
if (ret) {
exit(1);
}
ret = setsockopt(listener, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt));
if (ret) {
exit(1);
}
int flags = fcntl(listener, F_GETFL, 0);
flags |= O_NONBLOCK;
fcntl(listener, F_SETFL, flags);
bzero(&my_addr, sizeof(my_addr));
my_addr.sin_family = PF_INET;
my_addr.sin_port = htons(port);
my_addr.sin_addr.s_addr = INADDR_ANY;
if (bind(listener, (struct sockaddr *) &my_addr, sizeof(struct sockaddr)) == -1) {
perror("bind");
exit(1);
} else {
printf("IP bind OK\n");
}
kdpfd = epoll_create(MAXEPOLLSIZE);
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = listener;
if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, listener, &ev) < 0) {
fprintf(stderr, "epoll set insertion error: fd=%dn", listener);
return -1;
} else {
printf("ep add OK\n");
}
printf("================\n");
while (1) {
nfds = epoll_wait(kdpfd, events, 10000, -1);
if (nfds == -1) {
perror("epoll_wait");
break;
}
for (n = 0; n < nfds; ++n) {
if (events[n].data.fd == listener) {
struct epoll_event child_ev;
while (1) {
new_sd = udp_accept(listener, my_addr);
if (new_sd == -1) break;
child_ev.events = EPOLLIN;
child_ev.data.fd = new_sd;
if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, new_sd, &child_ev) < 0) {
fprintf(stderr, "epoll set insertion error: fd=%dn", new_sd);
return -1;
}
}
} else if(events[n].events == EPOLLIN){
struct epoll_event child_ev;
ret = read_data(events[n].data.fd);
if (ret > 0) {
new_sd = events[n].data.fd;
child_ev.events = EPOLLOUT;
child_ev.data.fd = new_sd;
if (epoll_ctl(kdpfd, EPOLL_CTL_MOD, new_sd, &child_ev) < 0) {
fprintf(stderr, "epoll set insertion error: fd=%dn", new_sd);
return -1;
}
printf("read[%d]: %s from %d\n", ret, buffer, new_sd);
} else if(ret == 0){
printf("read err:%s %d\n", strerror(errno), ret);
close(new_sd);
}
}else if(events[n].events == EPOLLOUT){
struct epoll_event child_ev;
new_sd = events[n].data.fd;
ret = send_data(new_sd);
child_ev.events = EPOLLIN;
child_ev.data.fd = new_sd;
if (epoll_ctl(kdpfd, EPOLL_CTL_MOD, new_sd, &child_ev) < 0) {
fprintf(stderr, "epoll set insertion error: fd=%dn", new_sd);
return -1;
}
}
}
}
close(listener);
return 0;
}
client 端代码
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <sys/time.h>
#include <sys/resource.h>
int main(int argc, char * argv[])
{
int socketFd;
int Port = 1234;
int ret = 0;
struct sockaddr_in peer_Addr;
socklen_t server_len=sizeof(peer_Addr);
peer_Addr.sin_family = PF_INET;
peer_Addr.sin_port = htons(Port);
peer_Addr.sin_addr.s_addr = inet_addr("172.26.197.253");
if ((socketFd = socket(PF_INET, SOCK_DGRAM| SOCK_CLOEXEC, 0)) == -1) {
perror("child socket");
exit(1);
}
if (connect(socketFd, (struct sockaddr *) &peer_Addr, sizeof(struct sockaddr)) == -1) {
perror("chid connect");
exit(1);
}
char buffer[1024] = {0};
memset(buffer, 0, 1024);
sprintf(buffer, "hello from client");
sendto(socketFd, buffer, strlen(buffer), 0, (struct sockaddr *) &peer_Addr, sizeof(struct sockaddr_in));
ret = recvfrom(socketFd, buffer,1024, 0, (struct sockaddr *)&peer_Addr, &server_len);
if (ret > 0) {
printf("read[%d]: %s fd:%d\n", ret, buffer, socketFd);
} else {
printf("read err:%s %d\n", strerror(errno), ret);
}
memset(buffer, 0, 1024);
while(1)
{
char buffer[1024] = {0};
memset(buffer, 0, 1024);
//sprintf(buffer, "hello from client");
printf("input send data:\n");
scanf("%s",buffer);
sendto(socketFd, buffer, strlen(buffer), 0, (struct sockaddr *) &peer_Addr, sizeof(struct sockaddr_in));
memset(buffer, 0, 1024);
printf("=============\n");
ret = recvfrom(socketFd, buffer,1024, 0, (struct sockaddr *)&peer_Addr, &server_len);
if (ret > 0) {
printf("read[%d]: %s fd:%d\n", ret, buffer, socketFd);
} else {
printf("read err:%s %d\n", strerror(errno), ret);
}
}
close(socketFd);
return 0;
}
代码调试
启动 Server
qifei@QF:~/udp_server_concurrent$ ./udp_server
socket OK:3
IP bind OK
ep add OK
================
ret: 17, buf: hello from client
0, parent:3 new:5
read[3]: 122 from 5
send: 122 from 5
启动Client 发送 hello client 并接收 服务端返回的 hello server
手动输入要发送的数据,立刻收到服务端返回同样的消息
qifei@QF:~/udp_server_concurrent$ ./client
read[17]: hello from Server fd:3
input send data:
122
read[1024]: 122 fd:3
input send data:
文章参考与<零声教育>的C/C++linux服务期高级架构系统教程学习:
服务器高级架构体系:
https://ke.qq.com/course/417774?flowToken=1010783