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 不同,所以即使他们使用同一个端口号,他们也不会冲突。