Udp 服务的 sendto, recvfrom的参数(CentOS)

本文深入解析UDP通信机制,探讨sendto和recvfrom函数的使用方法,包括sockfd和addr的关联方式,以及通过connect关联sockfd和addr的具体实现。提供两种有效的UDP数据收发策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

两个函数的定义:

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

 ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
 

两个都有sockfd,还有addr和地址长度。

对于做过tcp的同学来说,有sockfd就够了啊。而udp是无连接,那么光有addr也应该可以了啊。

今天我搞清了用法,结论是sockfd和addr确实不必同时有,但两个必须被联系起来。

首先搞清:

recvfrom的addr是来源地址:即谁发给我的。即使用在客户端接收、只会是服务器端,它的意义也是数据来源。

而sendto的addr是目的地址:即发给谁。如果有一个正确的(被关联了addr)的sockfd,sendto可以传null的addr。

sendto的sockfd必须传一个正确的,不能传0

对于服务器端的sendto,因为是发送给不同的客户端,因此服务器端的sendto的sockfd和addr都必须有。

所以,有两种sento, recvfrom的组合用法:

首先把sockfd和addr正确的初始化。接收传入sockfd和用于保存来源地址的 src_addr 。

然后,

用法1:

用connect把sockdf和addr联系起来

然后发送时传入sockfd,addr传null

用法2:

发送时传入sockfd和addr

 

两种方法都可以正确的接收和发送消息

/*
 * t_udp_cleint.c
 *
 *  Created on: Feb 18, 2019
 *      Author: lein
 */


#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>

#include "base.h"
#include "100MClient.h"
static int addr_len = sizeof(struct sockaddr_in);
char SERVER_IP[16];
int SERVER_PORT;
int running = 1;
struct timeval rcvto,sndto;
int sockfd=0;
int tolen = sizeof(struct timeval);
struct timeval now_sec;
void logi(char* s, char*s1){
	printf("%s", s);
}

///
///
///
/*  方案1 通过sendTo  */
///
///
///
int initUdpParam(struct sockaddr_in* addr, char* server_ip, int server_port) {
	int sockfd;

	/* 建立socket,注意必须是SOCK_DGRAM */
	if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
		perror("socket");
		return 0;
	}


	rcvto.tv_sec = 2;
	rcvto.tv_usec = 500000;

	sndto.tv_sec = 1;
	sndto.tv_usec = 500000;
	//发送时限
	setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &sndto, tolen);
	//接收时限
	setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &rcvto, tolen);

	/* 填写sockaddr_in*/
	bzero(addr, sizeof(struct sockaddr_in));
	addr->sin_family = AF_INET;
	addr->sin_port = htons(server_port);
	addr->sin_addr.s_addr = inet_addr(server_ip);

	return sockfd;
}
int sendUdpDataByAddr(struct sockaddr_in* addr, char * data, int data_len) {
	//这里传入了sockfd和addr,起到把接收和发送关联起来的作用
	int rtn = sendto(sockfd, data, data_len, 0, (struct sockaddr *)addr, tolen);

	if (rtn == -1) {
		LOG_ERROR("Send msg to fd=%d failed: %s", sockfd, strerror(errno));
		return 0;
	} else if (rtn < data_len) {
		LOG_WARNING("Send msg to fd=%d may got errors, sent/total = %d/%d", sockfd, rtn, data_len);
		return 0;
	}

	return 1;
}

void recvUdpData1() {
	char buff_rtn[UDP_MSG_BUFFER_SIZE];

	struct sockaddr_in addr;
	int rtn,i;

	int error_count = 0;

	char break_flag = 0;
	MARK("Go %s:%d...", SERVER_IP, SERVER_PORT);
	while(running) {
		while(1){
			sockfd = initUdpParam(&addr, SERVER_IP, SERVER_PORT);
			if(!sockfd){
				printf("Connect to %s:%d failed\n", SERVER_IP, SERVER_PORT);
				sleep(3);
			}else break;
		}

		error_count = 0;
		while (running) {
			struct sockaddr_in tmp;
			int len = sizeof(tmp);

			rtn = recvfrom(sockfd, buff_rtn, sizeof(buff_rtn), 0, (struct sockaddr *)&tmp, &len);
			if (rtn < 1) {
				gettimeofday(&now_sec, NULL);
				MARK("[%d.%06d]FAILED recv, rtn=%d, sockfd=%d\n", now_sec.tv_sec, now_sec.tv_usec, rtn, sockfd);
				if(error_count++>5){
					printf("Connection lost try reconnect\n");
					break;
				}
				sleep(1);
				sendUdpDataByAddr(&addr, (char*)&now_sec, sizeof(now_sec));
				continue;
			} else {
				buff_rtn[rtn] = 0;
				printf("[--=A=--]Got data from server: %s\n", buff_rtn);
				break_flag = 1;
				break;
			}
		}

		if(break_flag) break;
	}
}

///
///
///
/*    */
///
///
///

int connectToUdpServer(struct sockaddr_in* addr, char* server_ip, int server_port) {
	int sockfd;

	/* 建立socket,注意必须是SOCK_DGRAM */
	if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
		perror("socket");
		return 0;
	}


	rcvto.tv_sec = 2;
	rcvto.tv_usec = 500000;

	sndto.tv_sec = 1;
	sndto.tv_usec = 500000;
	//发送时限
	setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &sndto, tolen);
	//接收时限
	setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &rcvto, tolen);

	/* 填写sockaddr_in*/
	bzero(addr, sizeof(struct sockaddr_in));
	addr->sin_family = AF_INET;
	addr->sin_port = htons(server_port);
	addr->sin_addr.s_addr = inet_addr(server_ip);

	//通过connect把sockfd和addr关联起来
	int rs =  connect(sockfd, (struct sockaddr *) addr, (socklen_t) sizeof(struct sockaddr_in));
	if(rs==0)
		return sockfd;
	else {
		LOG_ERROR("Connect to server %s:%d failed: %s\n", server_ip, server_port, strerror(errno));
		return 0;
	}
}
/**
 * 执行发送udp消息已经接收的响应
 */
int sendUdpData(char * data, int data_len) {
	//这里没有传addr,但之前已经把addr和sockfd关联(通过connect),因此即便没有addr可以发送成功
	int rtn = sendto(sockfd, data, data_len, 0, NULL, 0);

	if (rtn == -1) {
		LOG_ERROR("Send msg to fd=%d failed: %s", sockfd, strerror(errno));
		return 0;
	} else if (rtn < data_len) {
		LOG_WARNING("Send msg to fd=%d may got errors, sent/total = %d/%d", sockfd, rtn, data_len);
		return 0;
	}

	return 1;
}


void recvUdpData2() {
	char buff_rtn[UDP_MSG_BUFFER_SIZE];

	struct sockaddr_in addr;
	int rtn,i;

	int error_count = 0;
	char break_flag = 0;
	//struct timeval ts;
	MARK("[B] Go %s:%d...", SERVER_IP, SERVER_PORT);
	while(running) {
		while(1){
			sockfd = connectToUdpServer(&addr, SERVER_IP, SERVER_PORT);
			if(!sockfd){
				printf("Connect to %s:%d failed\n", SERVER_IP, SERVER_PORT);
				sleep(3);
			}else break;
		}

		error_count = 0;
		while (running) {
			struct sockaddr_in tmp;
			int len = sizeof(tmp);

			rtn = recvfrom(sockfd, buff_rtn, sizeof(buff_rtn), 0, (struct sockaddr *)& tmp, &len);
			if (rtn < 1) {
				gettimeofday(&now_sec, NULL);
				MARK("[%d.%06d]FAILED recv, rtn=%d, sockfd=%d\n", now_sec.tv_sec, now_sec.tv_usec, rtn, sockfd);
				if(error_count++>5){
					printf("Connection lost try reconnect\n");
					break;
				}
				sleep(1);
				sendUdpData((char*)&now_sec, sizeof(now_sec));
				continue;
			} else {
				buff_rtn[rtn] = 0;
				printf("[--=B=--]Got data from server: %s\n", buff_rtn);
				break_flag = 1;
				break;
			}
		}
		if(break_flag) break;
	}
}

#ifdef EXE
void main(char** argv, int argc){
	if(argc>1) {
		memset(SERVER_IP, 0, sizeof(SERVER_IP));
		strncpy(SERVER_IP, argv[1], sizeof(SERVER_IP)-1);
	}else strcpy(SERVER_IP, "127.0.0.1");

	if(argc>2){
		SERVER_PORT = atoi(argv[2]);
	}else SERVER_PORT = 50001;
	recvUdpData1();
	recvUdpData2();
}
#endif

base.h是定义LOG_ERROR等的,非必须 。

gcc -g -o tudp_client t_udp_client.c -DEXE

 

### TCP 和 UDP 数据传输函数解析 #### TCP 协议中的 `send` 和 `recv` 在TCP协议中,用于发送和接收数据的主要函数分别是`send`和`recv`。 对于`send`函数而言,在向连接套接字写入数据时会调用此方法。其定义如下: ```c int send(SOCKET s, const char* buf, int len, int flags); ``` - 参数`s`表示已建立连接的套接口描述符; - `buf`指向要发送的数据缓冲区首地址; - `len`指定待发送数据长度; - `flags`通常设置为0,保留特殊用途[^2]; 而`recv`则负责读取来自对方发来的消息: ```c int recv(SOCKET s, char* buf, int len, int flags); ``` - 同样地,`s`代表目标套接口句柄; - `buf`用来存储收到的信息; - `len`表明最大可接受的消息大小; - `flags`一般设为零; #### UDP 协议下的 `sendto` 及 `recvfrom` 不同于TCP的是,由于UDP属于无连接型服务模型,因此需要额外提供目的端信息来完成一次完整的通信过程。这也就意味着当通过UDP方式传递数据包时,除了常规参数外还需附加远程主机的相关属性作为输入项之一。 具体来说就是利用`sendto`来进行数据分组投递操作: ```c int sendto( SOCKET s, const char *buf, int len, int flags, struct sockaddr *to, int tolen ); ``` 其中新增加的部分有: - `to`: 描述远端节点位置的对象实例(即IP地址与端口号组合),类型应为`sockaddr_in`; - `tolen`: 上述结构体变量所占空间尺寸[^1]; 至于接收方面,则借助于`recvfrom`实现: ```c int recvfrom( SOCKET s, char *buf, int len, int flags, struct sockaddr *from, int *fromlen ); ``` 这里同样引入了一个新的成员——`from`以及对应的`fromlen`, 它们的作用是用来记录当前正在处理的那个客户端的身份特征.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值