UDP communication related and retroreflective server combined with epoll

UDP belongs to the user datagram protocol and belongs to the transport layer protocol. Provide connection-oriented, unreliable transmission, no congestion control and timeout retransmission mechanism. Compared with TCP connection-oriented, provide reliable transmission transport layer protocol, UDP also has its application scenarios. UDP has the advantages of low overhead in the first part and fast transmission speed. It is also widely used in audio and video calls, web live broadcasts, and frame synchronization in games.

Unlike streaming sockets like TCP, there is no need to deal with sticky packets for UDP. UDP is message-oriented. For messages delivered at the application layer, the protocol header is directly added and delivered to the IP layer. The messages are not merged or split, and the boundaries of the messages are preserved. Therefore, the socket buffer of the receiving end uses a chain structure to store each arriving UDP data packet, so that the receiving end can only read one data packet from the buffer at a time without dealing with the sticky packet problem.

The UDP protocol is an unreliable transmission, and problems such as packet loss and disorder may occur. When using, it is necessary to implement a data packet confirmation mechanism, a packet loss retransmission mechanism, and a data packet sequencing mechanism. In addition, try not to send data packets larger than the path MTU.

The maximum content of a single UDP transmission is 1472 bytes, because the MTU set by different network relay devices is different. On the Internet, the standard MTU value is 576, excluding the IP header and UDP header (576-20-8) is equal to 548, so the UDP datagram we send should be controlled within 548 bytes. If it exceeds, the UDP data packet needs to be sent in fragments at the sending end, and then combined at the receiving end.

The following is a retroreflective server implemented by using UDP and epoll in combination.

Server side udp_epoll.cpp

#include <stdio.h> 
#include <stdlib.h>
#include <unistd.h>
#include <errno.h> 
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <string.h>
#include <vector>

#define BUF_SIZE 1024
#define SERV_PORT 5555

#define ERR_EXIT(m) \
        do \
        { \
                perror(m); \
                exit(EXIT_FAILURE); \
        } while(0)

typedef std::vector<struct epoll_event> EventList;
int main()
{
	int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if (-1 == sockfd)
	{
		ERR_EXIT("socket");
	}

	//把socket设置为非阻塞方式   
	int fd_flags = ::fcntl(sockfd, F_GETFL, 0);
	fd_flags |= FD_CLOEXEC;
	fd_flags |= O_NONBLOCK;
	fcntl(sockfd, F_SETFD, fd_flags);

	//生成用epoll专用文件描述符
	int epfd = epoll_create(10000);

	//设置与要处理的事件相关的文件描述符 
	struct epoll_event ev;
	ev.data.fd = sockfd;
	ev.events = EPOLLIN | EPOLLET;
	epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &ev);

	//绑定地址
	struct sockaddr_in serveraddr;
	memset(&serveraddr, 0, sizeof(struct sockaddr_in));

	serveraddr.sin_family = AF_INET;
	serveraddr.sin_port = htons(SERV_PORT);
	serveraddr.sin_addr.s_addr = inet_addr("127.0.0.1");

	bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));

	EventList events(16);
	int nready;
	struct sockaddr_in clientaddr;
	socklen_t addrlen = sizeof(clientaddr);
	char sockbuf[BUF_SIZE] = { 0 };
	char szAddr[BUF_SIZE] = { 0 };
	for (; ; )
	{
		//等待epoll事件的发生
		nready =epoll_wait(epfd, &*events.begin(), static_cast<int>(events.size()),-1);
		if (nready == -1)
		{
			if (errno == EINTR)
				continue;

			ERR_EXIT("epoll_wait");
		}
		//没有事件发送
		if (nready == 0)
			continue;

		//处理所发生的所有事件      
		for (int i = 0; i < nready; ++i)
		{
			if (events[i].events&EPOLLIN)
			{
				if (sockfd != events[i].data.fd)
					continue;

				int ret = recvfrom(sockfd, sockbuf, BUF_SIZE, 0, (sockaddr*)&clientaddr, &addrlen);
				if (-1 == ret || 0 == ret)
				{
					close(sockfd);
					ERR_EXIT("recvfrom");
				}

				char* p = (char *)&clientaddr.sin_addr;
				sprintf(szAddr, "%d.%d.%d.%d,port:%u sockfd:%u", 
					*p, *(p + 1), *(p + 2), *(p + 3), ntohs(clientaddr.sin_port), sockfd);

				printf("from ip:%s.recv %s \n", szAddr, sockbuf);
				ret = sendto(sockfd, sockbuf, strlen(sockbuf), 0, (sockaddr*)&clientaddr, addrlen);
				if (-1 == ret)
				{
					close(sockfd);
					ERR_EXIT("sendto");
				}
			}
		}
	}
	return 0;
}

Client udp_client.cpp

#include <stdio.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

#define BUF_SIZE 1024
#define SERV_PORT 5555

#define ERR_EXIT(m) \
        do \
        { \
                perror(m); \
                exit(EXIT_FAILURE); \
        } while(0)

int main(int argc, char *argv[])
{
   int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (-1 == sockfd)
    {
		ERR_EXIT("socket");
    }

    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(struct sockaddr_in));

    addr.sin_family = AF_INET;
    addr.sin_port = htons(SERV_PORT);
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");

    if (connect(sockfd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) != 0)
    {
		ERR_EXIT("connect");
    }

	char sendbuf[1024] = { 0 };
	char recvbuf[1024] = { 0 };
	while (fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
	{	//sockfd中存有对方地址信息可以发送和接收数据
		if (send(sockfd, sendbuf, strlen(sendbuf), 0) <= 0)
		{
			ERR_EXIT("send");
		}
		//阻塞的接收数据然后打印
		recv(sockfd, recvbuf, BUF_SIZE, 0);
		fputs(recvbuf, stdout);
		memset(sendbuf, 0, sizeof(sendbuf));
		memset(recvbuf, 0, sizeof(recvbuf));
	}
    return 0;
}

 

Guess you like

Origin blog.csdn.net/qq_19825249/article/details/108729955