UDP是无连接的传输,在传输的过程中数据报可能会出现丢失,但是UDP传输又适合那些实时性要求比较高的周期性发送数据的情况。比如客户端周期发送固定长度的脉冲信号给服务器,偶尔丢失一两包数据也不会造成很大的问题,在这种时候是可以使用UDP传输的。有时,客户端也需要发送控制命令给服务端,但是控制不能丢失,这时就需要使用TCP协议来传输了。下面的这个示例代码是在一个进程中同时使用TCP和UDP来传输数据。
/*=============================================================================
# FileName: tcpudpselect.c
# Desc: use tcp and udp to process client request
# Author: Licaibiao
# LastChange: 2017-02-12
=============================================================================*/
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<unistd.h>
#include<stdlib.h>
#include<errno.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<string.h>
#include<signal.h>
#define MAXLINE 1024
#define LISTENLEN 10
#define SERV_PORT 6666
int max(int a, int b)
{
return a>b ? a : b;
}
void sig_chld(int signo)
{
pid_t pid;
int stat;
while ((pid = waitpid(-1,stat,WNOHANG))>0)
{
//printf("child %d terminated \n",pid);
}
return ;
}
void str_echo(int fd)
{
}
int main(int argc, char **argv)
{
int listenfd, connfd, udpfd, nready, maxfdp1;
char mesg[MAXLINE];
pid_t childpid;
fd_set rset;
ssize_t n;
socklen_t len;
const int on = 1;
struct sockaddr_in cliaddr, servaddr;
void sig_chld(int);
/* create listening TCP socket */
listenfd = socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
bind(listenfd, (struct sockaddr*) &servaddr, sizeof(servaddr));
listen(listenfd, LISTENLEN);
/* create UDP socket */
udpfd = socket(AF_INET, SOCK_DGRAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
bind(udpfd, (struct sockaddr*) &servaddr, sizeof(servaddr));
signal(SIGCHLD, sig_chld); /* must call waitpid() */
FD_ZERO(&rset);
maxfdp1 = max(listenfd, udpfd) + 1;
for ( ; ; )
{
FD_SET(listenfd, &rset);
FD_SET(udpfd, &rset);
if ( (nready = select(maxfdp1, &rset, NULL, NULL, NULL)) < 0)
{
if (errno == EINTR)
continue; /* back to for() */
else
{
printf("select error");
exit(0);
}
}
if (FD_ISSET(listenfd, &rset))
{
len = sizeof(cliaddr);
connfd = accept(listenfd, (struct sockaddr*) &cliaddr, &len);
if ( (childpid = fork()) == 0) /* child process */
{
close(listenfd); /* close listening socket */
str_echo(connfd); /* process the request */
exit(0);
}
close(connfd); /* parent closes connected socket */
}
if (FD_ISSET(udpfd, &rset))
{
len = sizeof(cliaddr);
n = recvfrom(udpfd, mesg, MAXLINE, 0, (struct sockaddr*) &cliaddr, &len);
sendto(udpfd, mesg, n, 0, (struct sockaddr*) &cliaddr, len);
}
}
}
我们调用select函数来监听TCP和UDP的可读条件,然后TCP的连接创建一个新的进程去处理。严格意义上来说其实也不是使用一个进程来处理。
在这里TCP和UDP我们使用的都是同一个端口。在TCP/UDP中使用5元组来定位一个连接,src_ip, src_port, dest_ip, dest_port, protocol_type,这里的protocol_type 不同,所以即使他们使用同一个端口号,他们也不会冲突。
===================2022.08.20:21:30更新=====================
由于各种原因,后续文章内容将更新到公众号,本平台将不再做更新。
CSDN上相关文章的测试工程代码,也统一放到了公众号上,可以免费免积分下载
可以通过主页上的二维码,也可以通过搜索微信公众号 liwen01 进入公众号
liwen01 2022.08.20
===================2022.08.20:21:30更新=====================