1. Typical UDP client/server communication process
Following the communication process, we will implement a UDP redirection Client/Server.
# Include <sys/types. h>
# Include <sys/socket. h>
Ssize_t send (INT sockfd, const void * Buf, size_t Len, int flags );
Ssize_t sendto (INT sockfd, const void * Buf, size_t Len, int flags, const struct sockaddr * dest_addr, socklen_t addrlen );
Send can be used only when the socket is in the "connected" status. When flags = 0, send is consistent with write.
And send (sockfd, Buf, Len, flags); that is, sendto (sockfd, Buf, Len, flags, null, 0 );
Ssize_t Recv (INT sockfd, void * Buf, size_t Len, int flags );
Ssize_t recvfrom (INT sockfd, void * Buf, size_t Len, int flags, struct sockaddr * src_addr, socklen_t * addrlen );
The relationship between Recv and recvfrom is the same as that between send and sendto.
C ++ code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
|
/*************************************** ********************************** > File name: echoser_udp.c > Author: Simba > Mail: dameng34@163.com > Created time: Sun 03 Mar 2013 06:13:55 CST **************************************** ********************************/# Include <stdio. h> # Include <stdlib. h> # Include <unistd. h> # Include <errno. h> # Include <sys/types. h> # Include <sys/socket. h> # Include <netinet/in. h> # Include <string. h> # Define err_exit (m )\ Do {\ Perror (m );\ Exit (exit_failure );\ } While (0) Void echo_ser (INT sock) { Char recvbuf [1024] = {0 }; Struct sockaddr_in peeraddr; Socklen_t peerlen; Int N; While (1) { Peerlen = sizeof (peeraddr ); Memset (recvbuf, 0, sizeof (recvbuf )); N = recvfrom (sock, recvbuf, sizeof (recvbuf), 0, (Struct sockaddr *) & peeraddr, & peerlen ); If (n =-1) { If (errno = eintr) Continue; Err_exit ("recvfrom error "); } Else if (n> 0) { Fputs (recvbuf, stdout ); Sendto (sock, recvbuf, N, 0, (Struct sockaddr *) & peeraddr, peerlen ); } } Close (sock ); } Int main (void) { Int sock; If (sock = socket (pf_inet, sock_dgram, 0) <0) Err_exit ("socket error "); Struct sockaddr_in servaddr; Memset (& servaddr, 0, sizeof (servaddr )); Servaddr. sin_family = af_inet; Servaddr. sin_port = htons (5188 ); Servaddr. sin_addr.s_addr = htonl (inaddr_any ); If (BIND (sock, (struct sockaddr *) & servaddr, sizeof (servaddr) <0) Err_exit ("BIND error "); Echo_ser (sock ); Return 0; } |
C ++ code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
|
/*************************************** ********************************** > File name: echocli_udp.c > Author: Simba > Mail: dameng34@163.com > Created time: Sun 03 Mar 2013 06:13:55 CST **************************************** ********************************/# Include <unistd. h> # Include <sys/types. h> # Include <sys/socket. h> # Include <netinet/in. h> # Include <ARPA/inet. h> # Include <stdlib. h> # Include <stdio. h> # Include <errno. h> # Include <string. h> # Define err_exit (m )\ Do \ {\ Perror (m );\ Exit (exit_failure );\ } While (0) Void echo_cli (INT sock) { Struct sockaddr_in servaddr; Memset (& servaddr, 0, sizeof (servaddr )); Servaddr. sin_family = af_inet; Servaddr. sin_port = htons (5188 ); Servaddr. sin_addr.s_addr = inet_addr ("127.0.0.1 "); Int ret; Char sendbuf [1024] = {0 }; Char recvbuf [1024] = {0 }; While (fgets (sendbuf, sizeof (sendbuf), stdin )! = NULL) { Sendto (sock, sendbuf, strlen (sendbuf), 0, (struct sockaddr *) & servaddr, sizeof (servaddr )); Ret = recvfrom (sock, recvbuf, sizeof (recvbuf), 0, null, null ); If (ret =-1) { If (errno = eintr) Continue; Err_exit ("recvfrom "); } Fputs (recvbuf, stdout ); Memset (sendbuf, 0, sizeof (sendbuf )); Memset (recvbuf, 0, sizeof (recvbuf )); } Close (sock ); } Int main (void) { Int sock; If (sock = socket (pf_inet, sock_dgram, 0) <0) Err_exit ("socket "); Echo_cli (sock ); Return 0; } |
Compile and run the server, and open a client in each of the two terminals to interact with the server. You can see that the server has the capability of concurrent services. Use Ctrl + C to shut down the server and then run the server. At this time, the client can contact the server. Compared with the running result of the preceding TCP program, we can understand the meaning of no connection.
Ii. UDP programming considerations
1. UDP packets may be lost or duplicated
2. UDP packets may be out of order
3. UDP lacks Traffic Control
4. UDP data packet Truncation
5. If recvfrom returns 0, the connection is closed because UDP is not connected.
6. ICMP asynchronous Error
7. UDP connect
8. Determine the UDP outbound Interface
Because UDP does not need to maintain connections, the program logic is much simpler, but the UDP protocol is unreliable. In fact, there are many mechanisms to ensure communication reliability at the application layer, that is, as mentioned.
For the 4th point, you can write a small program to test it:
C ++ code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
|
# Include <unistd. h> # Include <sys/types. h> # Include <sys/socket. h> # Include <netinet/in. h> # Include <stdlib. h> # Include <stdio. h> # Include <errno. h> # Include <string. h># Define err_exit (m )\ Do \ {\ Perror (m );\ Exit (exit_failure );\ } While (0) Int main (void) { Int sock; If (sock = socket (pf_inet, sock_dgram, 0) <0) Err_exit ("socket "); Struct sockaddr_in servaddr; Memset (& servaddr, 0, sizeof (servaddr )); Servaddr. sin_family = af_inet; Servaddr. sin_port = htons (5188 ); Servaddr. sin_addr.s_addr = htonl (inaddr_any ); If (BIND (sock, (struct sockaddr *) & servaddr, sizeof (servaddr) <0) Err_exit ("bind "); Sendto (sock, "ABCD", 4, 0, (struct sockaddr *) & servaddr, sizeof (servaddr )); Char recvbuf [1]; Int N; Int I; For (I = 0; I <4; I ++) { /* UDP is a protocol, that is, if the space received at one time is smaller than the data sent, packet truncation may occur, * But there must be no TCP packet Sticking Problem */ N = recvfrom (sock, recvbuf, sizeof (recvbuf), 0, null, null ); If (n =-1) { If (errno = eintr) Continue; Err_exit ("recvfrom "); } Else if (n> 0) Printf ("n = % d % C \ n", N, recvbuf [0]); } Return 0; } |
The above program sends data to itself and sends four bytes, but we only provide one byte buffer recvbuf. The first recvfrom reads one byte, however, in the next loop, the rest of the data is not read, because UDP is a protocol. If the buffer received at one time is smaller than the sent data, packet truncation may occur. In contrast, TCP Streaming Protocol is used, one part of a data packet can be read at a time, or multiple data packets can be read at a time, but this is the source of the packet sticking problem. Therefore, UDP does not have a packet sticking problem, because one message is received at a time. The output is as follows:
Simba @ Ubuntu :~ /Documents/code/linux_programming/UNP/socket $./trunc
N = 1
............
After receiving a character, recvfrom is blocked again.
For, if the data size sent using sendto is 0, the data frame containing only the header of each layer of the Protocol will be sent to the other party, and the recvfrom will return 0, but it does not mean that the other party closes the connection, because UDP itself does not have the concept of connection.
We can see that our client program does not call connect, does not run the server program, and runs the client program directly:
Simba @ Ubuntu :~ /Documents/code/linux_programming/UNP/socket $./echocli_udp
Dfsaf
................
When several characters are typed on the keyboard, sendto only copies the Buf data to the corresponding sock buffer. If the server is not enabled, the protocol stack returns an ICMP asynchronous error, however, because the previous call to connect "creates" a connection, this error cannot be received during recvfrom and is always blocked. Now let's add a sentence outside the while loop: connect (sock, (struct sockaddr *) & servaddr, sizeof (servaddr); test again:
Simba @ Ubuntu :~ /Documents/code/linux_programming/UNP/socket $./echocli_udp
Dfsaf
Recvfrom: Connection refused
In this case, recvfrom can receive the error and return it, and print the error message.
In fact, connect does not actually establish a connection, that is, there is no three handshakes, But it maintains a state and is bound to a remote address, because the remote address can be left unspecified when sendto is called, such as sendto (sock, sendbuf, strlen (sendbuf), 0, null, 0). You can even use the send function.
Send (sock, sendbuf, strlen (sendbuf), 0 );
Assume that the client has multiple IP addresses, and the parameter of the remote address provided by the connect or sendto function, the system selects an appropriate exit. For example, the remote IP address is 192.168.2.10, if the client's current IP addresses are 192.168.1.32 and 192.168.2.75, the IP address 192.168.2.75 is automatically selected.
Refer:
Linux C Programming one-stop learning
Chapter 1 TCP/IP details
UNP