两个函数的定义:
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
#include
#include
#include
#include
#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