LinuxSocket 编程 -- UdpServer 与 epoll

上节回顾
上一节介绍了 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

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值