当前位置:网站首页>I/O复用的高级应用:同时处理 TCP 和 UDP 服务
I/O复用的高级应用:同时处理 TCP 和 UDP 服务
2022-04-23 14:36:00 【m0_51551385】
1、I/O复用的另一用武之地:同时处理 TCP 和 UDP 服务
同一端口(或socket地址)既能接收UDP请求也能接收TCP请求。我们使用同一个socket地址创建两个 socket 文件描述符,分别用于处理该端口上的UDP和TCP请求。然后使用I/O复用技术同时监听这两个socket 文件描述符的事件,就可以实现同时处理一个端口上的 TCP 和 UDP 请求。
2、实例程序
下面是一个服务器程序,他能同时处理 TCP 和 UDP 服务。
/* 本程序实现一个同时处理 TCP 请求和 UDP 请求的回射服务器 */
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<assert.h>
#include<unistd.h>
#include<stdio.h>
#include<fcntl.h>
#include<stdlib.h>
#include<string.h>
#include<sys/epoll.h>
#include<pthread.h>
#include<errno.h>
#define MAX_EVENT_NUMBER 1024
#define TCP_BUFFER_SIZE 512
#define UDP_BUFFER_SIZE 1024
int setnonblocking(int fd)
{
int old_option = fcntl(fd, F_GETFL);
int new_option = old_option | O_NONBLOCK;
fcntl(fd, F_SETFL, new_option);
return new_option;
}
void addfd(int epollfd, int fd)
{
epoll_event event;
event.data.fd = fd;
event.events = EPOLLIN | EPOLLET;
epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);
setnonblocking(fd);
}
int main(int argc, char* argv[])
{
if (argc <= 2)
{
return 1;
}
const char* ip = argv[1];
int port = atoi(argv[2]);
struct sockaddr_in address;
bzero(&address, sizeof(address));
address.sin_port = htons(port);
address.sin_family = AF_INET;
inet_pton(AF_INET, ip, &address.sin_addr);
/* 创建 TCP socket,并将其绑定到端口 port 上 */
int listenfd = socket(PF_INET, SOCK_STREAM, 0);
assert(listenfd >= 0);
int ret = bind(listenfd, (struct sockaddr*)&address, sizeof(address));
assert(ret != -1);
ret = listen(listenfd, 5);
assert(ret != -1);
/* 创建 UDP socket,并将其绑定到端口 port 上 */
bzero(&address, sizeof(address));
address.sin_family = AF_INET;
address.sin_port = htons(port);
inet_pton(AF_INET, ip, &address.sin_addr);
int udpfd = socket(PF_INET, SOCK_DGRAM, 0);
assert(udpfd >= 0);
ret = bind(udpfd, (struct sockaddr*)&address, sizeof(address));
assert(ret != -1);
epoll_event events[MAX_EVENT_NUMBER];
int epollfd = epoll_create(10);
assert(epollfd != -1);
/* 注册 TCP socket 和 UDP socket 上的可读事件 */
addfd(epollfd, listenfd);
addfd(epollfd, udpfd);
while (1)
{
int number = epoll_wait(epollfd, events, MAX_EVENT_NUMBER, -1);
if (number < 0)
{
printf("epoll failure\n");
break;
}
for (int i = 0; i < number; i++)
{
int sockfd = events[i].data.fd;
if (sockfd == listenfd) /* 如果是 TCP 的监听 socket */
{
struct sockaddr_in client_address;
socklen_t client_addrlength = sizeof(client_address);
int connfd = accept(listenfd, (struct sockaddr*)&client_address, &client_addrlength);
addfd(epollfd, connfd);
}
else if (sockfd == udpfd) /* 如果是 UDP 的 socket */
{
char buf[UDP_BUFFER_SIZE];
memset(buf, '\0', UDP_BUFFER_SIZE);
struct sockaddr_in client_address;
socklen_t client_addrlength = sizeof(client_address);
ret = recvfrom(udpfd, buf, UDP_BUFFER_SIZE, 0, (struct sockaddr*)&client_address, &client_addrlength);
if (ret > 0)
{
sendto(udpfd, buf, UDP_BUFFER_SIZE - 1, 0, (struct sockaddr*)&client_address, client_addrlength);
}
}
else if (events[i].events & EPOLLIN) /* 如果是 TCP 可写事件 */
{
char buf[TCP_BUFFER_SIZE];
while (1) /* 由于 TCP 传输的是字节流,所以需要用 while 确保所有数据都被读出,UDP就不用使用 while */
{
memset(buf, '\0', TCP_BUFFER_SIZE);
ret = recv(events[i].data.fd, buf, TCP_BUFFER_SIZE - 1, 0);
if (ret < 0)
{
if (errno == EAGAIN || errno == EWOULDBLOCK) /* 在Vxorks和Windows上,EAGAIN 叫做 EWOULDBLOCK */
{
break;
}
close(sockfd);
break;
}
else if (ret == 0)
{
close(sockfd);
}
else
{
send(sockfd, buf, ret, 0);
}
}
}
else
{
printf("something else happened\n");
}
}
}
close(listenfd);
return 0;
}
版权声明
本文为[m0_51551385]所创,转载请带上原文链接,感谢
https://blog.csdn.net/m0_51551385/article/details/124363330
边栏推荐
- Four ways of SSH restricting login
- Find daffodils - for loop practice
- Docker (V) MySQL installation
- Proteus simulation design of DC adjustable regulated power supply (with simulation + paper and other data)
- 51 Single Chip Microcomputer Design of traffic light system (with Proteus simulation, C program, schematic diagram, PCB, thesis and other complete data)
- 1N5408-ASEMI整流二极管1N5408
- Usage of BC
- redis 模块编程中 key value的生命周期
- 【无标题】
- flannel 原理 之 子网划分
猜你喜欢
自动化的艺术
AT89C51单片机的数字电压表开发,量程0~5V,proteus仿真,原理图PCB和C程序等
SVN详细使用教程
Mq-2 and DS18B20 fire temperature smoke alarm system design, 51 single chip microcomputer, with simulation, C code, schematic diagram, PCB, etc
TUN 设备原理
想要成为架构师?夯实基础最重要
Arduino for esp8266串口功能简介
C语言知识点精细详解——数据类型和变量【1】——进位计数制
AT89C52 MCU frequency meter (1Hz ~ 20MHz) design, LCD1602 display, including simulation, schematic diagram, PCB and code, etc
asp.net使用MailMessage发送邮件的方法
随机推荐
Four ways of SSH restricting login
全连接层的作用是什么?
tcp_diag 内核相关实现 1 调用层次
交通灯系统51单片机设计(附Proteus仿真、C程序、原理图及PCB、论文等全套资料)
Logical volume creation and expansion
Sed learning for application
Qt界面优化:鼠标双击特效
C语言知识点精细详解——初识C语言【1】
ArrayList集合基本使用
LLVM - 生成加法
Parameter stack pressing problem of C language in structure parameter transmission
Electronic perpetual calendar of DS1302_ 51 single chip microcomputer, month, day, week, hour, minute and second, lunar calendar and temperature, with alarm clock and complete set of data
8.5 循环神经网络简洁实现
Detailed explanation of SAR command
单相交交变频器的Matlab Simulink建模设计,附Matlab仿真、PPT和论文等资料
外包干了四年,废了...
Arduino for esp8266串口功能简介
Golang 对分片 append 是否会共享数据
Matlab Simulink modeling and design of single-phase AC-AC frequency converter, with MATLAB simulation, PPT and papers
【Servlet】Servlet 详解(使用+原理)