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

130 篇文章 4 订阅
92 篇文章 19 订阅
本文详细介绍了Linux中UDP套接字编程的基本步骤,涉及sendto和recvfrom函数,以及如何在实践中构建服务器和客户端。同时讨论了UDP的无序和丢包问题,提出了简单丢包处理方法和可靠传输协议的概念。
摘要由CSDN通过智能技术生成

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

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

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

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

5.2.1 sendto函数

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

#include <sys/types>
#include <sys/socket.h>
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 <sys/types>
#include <sys/socket.h>
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 <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

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 <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

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("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 <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>

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

	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 <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

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(8888);

	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协议、滑动窗口协议)。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值