《Linux C/C++服务器开发实践》之第5章 UDP服务器编程

《Linux C/C++服务器开发实践》之第5章 UDP服务器编程

    • 5.1 UDP套接字编程的基本步骤
    • 5.2 UDP套接字编程的相关函数
      • 5.2.1 sendto函数
      • 5.2.2 recvfrom函数
    • 5.3 实战UDP套接字
        • 5.1.server.c
        • 5.1.client.c
        • 5.2.server.c
        • 5.2.client.c
    • 5.4 UDP丢包及无序问题

5.1 UDP套接字编程的基本步骤

服务器:
一、创建套接字描述符socket
二、设置服务器的IP地址和端口号(网络字节序)
三、套接字描述符绑定到服务器地址bind
四、从套接字描述符读取来自客户端的请求并取得客户端的地址recvfrom
五、向套接字描述符写入应答并发送给客户端sendto
六、回到步骤四等待读取下一个客户端的请求

客户端:
一、创建套接字描述符socket
二、设置服务器的IP地址和端口号(网络字节序)
三、向套接字描述符写入请求并发送给服务器sendto
四、从套接字描述符读取来自服务器的应答recvfrom
五、关闭套接字描述符

5.2 UDP套接字编程的相关函数

5.2.1 sendto函数

用于有连接及无连接的socket上发送数据

#include 
#include 
ssize_t sendto(int socket, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);

成功返回发送字节数,失败返回-1,errno查看错误码

5.2.2 recvfrom函数

用于有连接及无连接的socket上接收数据

#include 
#include 
ssize_t recvfrom(int socket, const void *buf, size_t len, int flags, const struct sockaddr *src_addr, socklen_t addrlen);

成功返回接收字节数,被关闭返回0,失败返回-1,errno查看错误码

5.3 实战UDP套接字

5.1.server.c
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

int main()
{
	struct sockaddr_in saddr;
	memset(&saddr, 0, sizeof(struct sockaddr_in));
	saddr.sin_family = AF_INET;
	saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
	saddr.sin_port = htons(9999);

	int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if (sockfd < 0)
	{
		perror("socket() failed");
		return -1;
	}

	char on = 1;
	setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

	int ret = bind(sockfd, (struct sockaddr *)&saddr, sizeof(struct sockaddr));
	if (ret < 0)
	{
		perror("bind() failed");
		return -1;
	}

	puts("waiting data");
	struct sockaddr_in raddr;
	int val = sizeof(struct sockaddr);
	char rbuf[50];
	ret = recvfrom(sockfd, rbuf, 50, 0, (struct sockaddr *)&raddr, (socklen_t *)&val);
	if (ret < 0)
		perror("recvfrom failed");
	printf("recv data: %s\n", rbuf);
	
	close(sockfd);
	return 0;
}
5.1.client.c
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

int main()
{
	struct sockaddr_in saddr;
	memset(&saddr, 0, sizeof(struct sockaddr_in));
	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(9999);
	saddr.sin_addr.s_addr = inet_addr("127.0.0.1");

	int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if (sockfd < 0)
	{
		perror("failed socket");
		return -1;
	}

	char on = 1;
	setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

	puts("please enter data:");
	char wbuf[50] = "write to udpserver";
	//scanf("%s", wbuf, sizeof(wbuf));
	//sscanf(wbuf, "%s", "write to udpserver");
	int ret = sendto(sockfd, wbuf, sizeof(wbuf), 0, (struct sockaddr *)&saddr, sizeof(struct sockaddr));
	if (ret < 0)
		perror("sendto failed");
	close(sockfd);
	return 0;
}
5.2.server.c
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

int main()
{
	struct sockaddr_in saddr;
	memset(&saddr, 0, sizeof(struct sockaddr_in));
	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(8888);
	saddr.sin_addr.s_addr = htonl(INADDR_ANY);

	int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if (sockfd < 0)
	{
		puts("socket failed");
		return -1;
	}

	char on = 1;
	setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

	int ret = bind(sockfd, (struct sockaddr *)&saddr, sizeof(struct sockaddr));
	if (ret < 0)
	{
		puts("sbind failed");
		return -1;
	}

	struct sockaddr_in raddr;
	int val = sizeof(struct sockaddr);
	char rbuf[50];
	while (1)
	{
		puts("waiting data");
		memset(rbuf, 0, 50);
		ret = recvfrom(sockfd, rbuf, 50, 0, (struct sockaddr *)&raddr, (socklen_t *)&val);
		if (ret == 0){
			printf("recvfrom empty\n");
			break;
		}
		if (ret < 0)
			perror("recvfrom failed");

		printf("recv data: %s\n", rbuf);
	}
	close(sockfd);
	return 0;
}
5.2.client.c
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

int main()
{
	struct sockaddr_in saddr;
	memset(&saddr, 0, sizeof(struct sockaddr_in));
	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(8888);
	saddr.sin_addr.s_addr = inet_addr("127.0.0.1");

	int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if (sockfd < 0)
	{
		perror("failed socket");
		return -1;
	}

	char on = 1;
	setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

	int ret;
	char wbuf[50] = "write to udpserver";
	for (int i=0; i<5; i++)
	{
		puts("please enter data:");
		//memset(wbuf, 0, sizeof(wbuf));
		//scanf("%s", wbuf, sizeof(wbuf));
		ret = sendto(sockfd, wbuf, sizeof(wbuf), 0, (struct sockaddr *)&saddr, sizeof(struct sockaddr));
		if (ret < 0)
			perror("sendto failed");
	}
	
	ret = sendto(sockfd, NULL, 0, 0, (struct sockaddr *)&saddr, sizeof(struct sockaddr));
	if (ret == 0)
		printf("sendto empty\n");

	close(sockfd);
	return 0;
}

5.4 UDP丢包及无序问题

UDP是无连接、面向消息的数据传输协议,数据报容易丢失,数据报无序。
丢包通常原因是服务器端socket接收缓存满了(UDP无流量控制,发送速度快于接收速度,容易出现这种情况),就会丢弃后面接收的包,且收到包后处理这段时间不会去接收包。可单独开线程去接收UDP数据。

要实现数据的可靠传输,必须实现流控制、超时和重传机制。

简单丢包处理:数据分块,加上ID,丢包ID发回服务器端,重新发送。

常见可靠传输算法有模拟TCP和重发请求(ARQ)协议(连续ARQ协议、选择重发ARQ协议、滑动窗口协议)。

你可能感兴趣的:(C/C++,学习,整理,服务器,linux,c语言)