UDP和TCP处于同一层网络模型中,也就是运输层,基于二者之上的应用有很多,常见的基于TCP的有HTTP、Telnet等,基于UDP有DNS、NFS、SNMP等。UDP是无连接,不可靠的数据协议服务,而TCP提供面向流、提供可靠数据服务。注意,UDP和TCP没有好坏之分,只是二者的适用场景不同罢了。
典型的UDP套接字编程模型是客户端不予服务端建立连接,而只是调用sendto函数来向服务端发送数据,其中必须要指定服务端的信息,包括IP和端口等;服务端不接收来自客户端的连接,而只是调用recvfrom函数,来等待某个客户端的数据到达。
UDP编程模型
在UDP套接字中,有2个函数最常用,也就是sendto和recvfrom,二者的声明如下:
#include <sys/socket.h> ssize_t recvfrom(int sockfd, void *buff, size_t nbytes, int flags, struct sockaddr *from, socklen_t *addrlen); ssize_t sendto(int sockfd, void *buff, size_t nbytes, int flags, const struct sockaddr *to, socklen_t addrlen);
recvfrom和snedto的前3个参数和read/write的前3个参数一样。flags表示设置的标志值,简单的UDP程序可以直接设置为0,最后两个参数表示服务端地址(对于sendto来说)或者是对端地址(对于recvfrom来说)。如果不关心对端的地址,则设置为NULL,此时addrlen也可以设置为NULL了。
注意:recvfrom和sendto也可以应用于TCP编程,不过一般不这样用。
简单的echo UDP服务器和客户端程序/** * UDP epollc测试 server端 */ #include <iostream> #include <sys/socket.h> #include <sys/epoll.h> #include <cstring> #include <netinet/in.h> #include <unistd.h> #include <arpa/inet.h> using namespace std; int main(int argc, char **argv) { int listenFd = -1; int connFd = -1; int epollFd = -1; struct sockaddr_in servAddr; struct epoll_event epollEvent; struct epoll_event epollEvents[16]; int nEvent = 0; listenFd = socket(AF_INET, SOCK_DGRAM, 0); memset(&servAddr, 0, sizeof(servAddr)); servAddr.sin_family = AF_INET; servAddr.sin_port = htons(6060); servAddr.sin_addr.s_addr = INADDR_ANY; bind(listenFd, (struct sockaddr *)&servAddr, sizeof(servAddr)); listen(listenFd, 5); epollFd = epoll_create(5); memset(&epollEvent, 0, sizeof(epollEvent)); epollEvent.data.fd = listenFd; epollEvent.events = EPOLLIN; epoll_ctl(epollFd, EPOLL_CTL_ADD, listenFd, &epollEvent); for (; ;) { nEvent = epoll_wait(epollFd, epollEvents, 16, -1); if (nEvent <= 0) { continue; } for (int i = 0; i < nEvent; i++) { int fd = epollEvents[i].data.fd; int event = epollEvents[i].events; if (event & EPOLLIN) { struct sockaddr_in clientAddr; socklen_t clientLen = sizeof(clientAddr); int recvLen = 0; char buff[256]; recvLen = recvfrom(fd, buff, sizeof(buff), 0, (struct sockaddr *)&clientAddr, &clientLen); buff[recvLen] = '\0'; cout << "-------------------------" << endl; cout << ntohs(clientAddr.sin_port) << endl; cout << inet_ntoa(clientAddr.sin_addr) << endl; cout << buff << endl; cout << "-------------------------" << endl; sendto(fd, buff, strlen(buff), 0, (struct sockaddr *)&clientAddr, clientLen); } if (event & (EPOLLHUP | EPOLLERR)) { epoll_ctl(epollFd, EPOLL_CTL_DEL, fd, NULL); close(fd); cout << "client shutdown" << endl; } } } return 0; }