Summary
I wrote about TCP in my last blog. Now that I have learned TCP, I have to mention UDP. TCP and UDP are like two inseparable partners, always mentioned and compared with each other. As for the advantages and disadvantages of the two protocols, I will not describe them here. Next, we mainly learn how to write UDP server communication.
Implementation of UDP Communication
In the last blog, we learned several related functions of socket programming, but we also know that UDP and TCP still have some differences, so here we will lead to different implementation methods in some aspects. Let me briefly mention the differences between them.
When calling socket function to create socket, TCP is based on byte stream transmission, while UDP is based on data packet transmission, so the second parameter of socket function is SOCK_DGRAM.
UDP servers do not need to shake hands three times to establish connections, so UDP does not need to listen.
After binding, it can read and write directly, but different from TCP, it is its own unique function, recvfrom and sendto.
Read-write function of UDP
Data Receiving Function recvfrom
The parameter sockfd denotes the fd of the current socket; the parameter buf denotes the received data pointer; the parameter flags defaults to 0; the parameter addr is the input-output parameter, and the output is the sender's protocol address; the parameter addrlen is the input-output parameter, and the size of addr is filled in.
Data sending function sendto
The meaning of the parameters is the same as that of the recvfrom function above, which can be directly referred to.
Socket UDP Communication
Server side:
#include<stdio.h> #include<sys/types.h> #include<sys/socket.h> #include<netinet/in.h> #include<string.h> void usage(const char* proc) { printf("Usage: %s, [udp_ip], [udp_port]\n", proc); } int main(int argc, char* argv[]) { if(argc != 3){ usage(argv[0]); return 1; } int sock = socket(AF_INET, SOCK_DGRAM, 0);//Creating sockets is SOCK_DGRAM because it is data packet transmission if(sock < 0){ perror("socket"); return 2; } struct sockaddr_in server_addr; server_addr.sin_family = AF_INET; server_addr.sin_port = htons(atoi(argv[2])); server_addr.sin_addr.s_addr = inet_addr(argv[1]); if(bind(sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0){//Binding Port Number and IP perror("bind"); return 3; } //Once the binding is successful, there is no need to listen, because it is unreliable, so there is no need to establish and save the connection. while(1){ struct sockaddr_in client_addr; socklen_t len = sizeof(client_addr); //send data char buf[1024]; if(recvfrom(sock, buf, 1024, 0, (struct sockaddr*)&client_addr, &len) < 0){ perror("recvrom"); return 4; } printf("recv is %s\n", buf); char buff[1024]; printf("server # "); scanf("%s", buff);//Reading Data from Standard Input //receive data if(sendto(sock, buff, 1024, 0, (struct sockaddr*)&client_addr, sizeof(client_addr)) < 0){ perror("sendto"); return 5; } } close(sock); return 0; }
Client
After writing the code, let's run it:#include<stdio.h> #include<sys/types.h> #include<sys/socket.h> #include<netinet/in.h> #include<string.h> void usage(char* proc) { printf("Usage: %s, [udp_ip], [udp_port]\n", proc); } int main(int argc, char* argv[]) { if(argc != 3){ usage(argv[0]); return 1; } int sock = socket(AF_INET, SOCK_DGRAM, 0); if(sock < 0){ perror("socket"); return 2; } struct sockaddr_in server_addr; server_addr.sin_family = AF_INET; server_addr.sin_port = htons(atoi(argv[2])); server_addr.sin_addr.s_addr = inet_addr(argv[1]); while(1){ char buff[1024]; printf("client # "); scanf("%s", buff);//This is equivalent to reading data from standard input. if(sendto(sock, buff, 1024, 0, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0){ perror("sendto"); return 3; } char buf[1024]; socklen_t len = sizeof(server_addr); if(recvfrom(sock, buf, 1024, 0, (struct sockaddr*)&server_addr, &len) < 0){ perror("recvfrom"); return 4; } printf("recv is %s\n", buf); } close(sock); return 0; }
This is where we can enter what we want to enter.
We can see that the two sides are communicating with each other.
At this point we can view UDP services in the system by commanding netstat-nlup
We can see that the running ip is 0 and the port number is 8080.
How to Realize the Reliability of UDP in User Space
We all know that UDP is unreliable compared to TCP, so how to make UDP more reliable?
UDP is not a connection protocol, so it has the advantages of low resource consumption and fast processing speed. Therefore, usually audio, video and ordinary data use UDP more when they are transmitted, because even if they occasionally lose one or two data packets, they will not have a great impact on the reception results.
Transport layer can not guarantee the reliable transmission of data, it can only be achieved through the application layer. The way of implementation can refer to the way of reliable transmission of tcp, but the realization is not in the transport layer, and the realization is transferred to the application layer.
Realize confirmation mechanism, retransmit mechanism and window confirmation mechanism.
If you do not use the Linux protocol stack and the upper socket mechanism to achieve reliable transmission by grabbing and sending packets, then you must achieve the following functions:
Sending: Packet Fragmentation, Packet Confirmation, Packet Re-sending
Receiving: Packet Sequencing, Packet Number Confirmation
At present, the following open source programs use udp to achieve reliable data transmission. They are RUDP, RTP and UDT.
[RUDP]
RUDP provides a set of data quality of service enhancement mechanisms, such as congestion control improvement, retransmit mechanism and desalination server algorithm, so that in the case of packet loss and network congestion, RTP client (real-time location) presents a high-quality RTP flow. While not interfering with the real-time characteristics of the protocol, reliable UDP congestion control mechanism allows flow control behavior in TCP mode.
[RTP]
Real-time Transport Protocol (RTP) provides end-to-end transmission services with real-time characteristics for data, such as interactive video, audio or analog data under multicast or unicast network services. Applications usually run RTP on UDP to use their multi-node and verification services; both protocols provide transport layer protocol functionality. But RTP can be used with other suitable underlying networks or transport protocols. If the underlying network provides multicast mode, RTP can use the multicast table to transmit data to multiple destinations.
RTP itself does not provide on-time delivery mechanism or other quality of service (QoS) guarantees, it relies on the underlying services to achieve this process. RTP does not guarantee or prevent disorderly transmission, nor does it determine the reliability of the underlying network. RTP carries out orderly transmission. The serial number in RTP allows the receiver to reorganize the sender's packet sequence. At the same time, the serial number can also be used to determine the appropriate packet location. For example, in video decoding, there is no need for sequential decoding.
[UDT]
UDP-based Data Transfer Protocol (UDT) is an Internet data transmission protocol. The main purpose of UDT is to support massive data transmission in high-speed WAN, while TCP, the standard data transmission protocol on the Internet, performs poorly in high-bandwidth and long-distance networks. As the name implies, UDT is built on UDP and introduces new congestion control and data reliability control mechanisms. UDT is a connection-oriented two-way application layer protocol. It supports both reliable data stream transmission and partially reliable data packet transmission. Because UDT is fully implemented in UDP, it can also be used in other applications besides high-speed data transmission, such as point-to-point technology (P2P), firewall penetration, multimedia data transmission and so on.