Explore UDP socket programming and udp socket programming

Source: Internet
Author: User
Tags htons

Explore UDP socket programming and udp socket programming

UDP and TCP are in the same layer network model, that is, the transport layer. There are many applications based on the two. Common TCP-based applications include HTTP and Telnet, UDP-based DNS, NFS, and SNMP are available. UDP is a connectionless and unreliable data protocol service, while TCP provides stream-oriented and reliable data services. Note that UDP and TCP are not good or bad, but they are applicable in different scenarios.

The typical UDP socket programming model is that the client does not establish a connection to the server, but only calls the sendto function to send data to the server. The server information, including IP address and port, must be specified; the server does not receive connections from the client, but only calls the recvfrom function to wait for the data of a client to arrive.

UDP Programming Model

In UDP sockets, two functions are most commonly used: sendto and recvfrom. The declaration of the two functions is as follows:

#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);

The first three parameters of recvfrom and snedto are the same as those of read/write. Flags indicates the set flag value. A simple UDP program can be directly set to 0. The last two parameters indicate the server address (for sendto) or the peer address (for recvfrom ). If you do not care about the peer address, set it to NULL. In this case, addrlen can also be set to NULL.

Note: recvfrom and sendto can also be used in TCP programming, but they are generally not used in this way.

Simple echo UDP server and client programs
/*** UDP epollc test 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 failed [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 ;}
/*** UDP client */# include <iostream> # include <sys/socket. h> # include <netinet/in. h> # include <arpa/inet. h> # include <unistd. h> using namespace std; int main (int argc, char ** argv) {int connFd =-1; struct sockaddr_in servAddr; char buff [64] = "hi, udp server "; char buffRecv [64]; connFd = 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 = inet_addr ("192.168.1.100"); for (int I = 0; I <2; I ++) {sendto (connFd, buff, sizeof (buff), 0, (struct sockaddr *) & servAddr, sizeof (servAddr); int recvLen = recvfrom (connFd, buffRecv, sizeof (buffRecv), 0, NULL, NULL ); buffRecv [recvLen] = '\ 0'; cout <buffRecv <endl;} close (connFd); return 0 ;}

Note: In the code, the return value of the function is not processed for convenience :(

UDP programming may cause data packet loss because UDP is unreliable. If a customer's data packet is lost, the client will always block the recvfrom function call. Similarly, if the customer's data arrives at the server and the response packet is lost, the customer will always block the recvfrom call. To prevent such problems, you can set a timeout value for recvfrom.

 

An interesting small problem

When the UDP server is not running, after the UDP client sends the data packet to the server, it is blocked in the recvfrom call, waiting for a server response that is never possible. However, through packet capture analysis, the server host responds to an ICMP port inaccessible message, but this ICMP error is not returned to the customer process.

The Socket Tool is used for testing. The UDP packet test result is sent to 192.168.1.150 (linux host) on 192.168.1.100 (window host.

This ICMP error is an asynchronous error. This error is caused by sendto, but sendto itself is returned successfully. The success of the UDP output operation only indicates that the data packet has been added to the output queue at the data link layer. The ICMP packet was received later. Therefore, for a UDP socket, the asynchronous error caused by it is not returned to it unless it has established a connection. So, how can this problem be solved? Please move a small bench, hold the mouse, and continue to tumble :)

 

Can UDP use the connect function?

UDP can call the connect function, but the UDP connect function is quite different from the TCP connect function call. There is no three-way handshake process here. The kernel only checks whether an error is known immediately (for example, the destination address is inaccessible), records the IP address and port number of the Peer end, and then immediately returns the call process.

You do not need to use the sendto function when using connect UDP programming. You can directly use write/read. The following code uses the connect function and then sends data to the host that does not run the UDP Service:

#include <iostream>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <cstring>#include <unistd.h>using namespace std;int main(int argc, char **argv) {    int connFd = -1;    struct sockaddr_in servAddr;    char buff[64] = "hi, udp server";    char buffRecv[64];    connFd = socket(AF_INET, SOCK_DGRAM, 0);    memset(&servAddr, 0, sizeof(servAddr));    servAddr.sin_family = AF_INET;    servAddr.sin_port = htons(60);    servAddr.sin_addr.s_addr = inet_addr("192.168.1.150");    connect(connFd, (struct sockaddr *) &servAddr, sizeof(servAddr));    write(connFd, buff, sizeof(buff));    int recvLen = read(connFd, buffRecv, sizeof(buffRecv));    if (recvLen < 0) {       perror("read error");    }    close(connFd);    return 0;}

Output result:

Here, we replace the read function in the test code with the following code to output the same result.

int recvLen = recvfrom(connFd, buffRecv, sizeof(buffRecv), 0, NULL, NULL);

 

UDP lacks Traffic Control

Each TCP interface has a sending buffer. We can use the SO_SNDBUF interface option to change the buffer size. When an application calls write, the kernel copies all data from the buffer of the application process to the sending buffer of the Set interface. If the sending buffer of the Set interface cannot accommodate all applications (or the application's buffer is greater than the sending buffer of the Set interface, or the sending buffer of the Set interface has other data), the application process will be suspended, here we assume that write is blocked. The kernel will not return data from the write system call until all data in the application process buffer is copied to the sending buffer of the Set interface. Therefore, a successful response from the write call to write a TCP set of interfaces only indicates that the sent data is sent to the buffer zone of the application process. It does not tell us that the Peer TCP or application process has received data.

The UDP set interface has the size of the sending buffer (modified by SO_SNDBUF), but it is only the maximum size of the UDP datagram written to the set interface. Note that the UDP sending buffer actually does not exist. If an application writes a datagram greater than the buffer size of the interface set, the kernel returns an EMSGSIZE error. Since UDP is not reliable, it does not need to save the data copy of the application, so it does not need a real sending buffer (the data of the application process is transferred down along the protocol stack and copied to the kernel buffer in some form, however, the data link layer discards the copy after the data is sent ).

If a successful write result is returned from a UDP set of interfaces, only the data packets written by the user or all fragments have been added to the output queue at the data link layer. If the queue does not have enough space to store the packet or a segment of it, the kernel usually returns an ENOBUFS error to the application process.

Both TCP and UDP have an interface receiving buffer. The receiving buffer of TCP sets of interfaces cannot overflow because TCP has traffic control. However, for UCP, when the received data is not loaded into the receiving buffer of the set of interfaces, the datagram is discarded. UDP has no traffic control: a fast sending end can easily overwhelm a slow receiving end, causing the UDP of the receiving end to discard the datagram.

Note: because UDP does not have traffic control, when the receiving buffer is full, it begins to discard the new data packets. The test is as follows. The UDP server program prints the number of received data packets in Real Time:

The server displays that 94 UDP packets are received, that is, 6 are lost. If the UDP data volume sent at a time is larger, packet loss is more serious.

 

References

1. "Unix Network Programming volume 1 socket programming" UDP chapter

2. UDP receiver buffer and packet loss problems

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.