UDP通信

转载自:
https://blog.csdn.net/fanle93/article/details/90666484

UDP通信

  • UDP简介
  • UDP协议特性
  • UDP协议与TCP协议的主要区别
    • TCP协议简介
    • TCP协议特性
    • 主要区别
  • UDP协议应用场景
  • UDP通信代码
    • Linux系统下的UDP通信代码
      • 发送端
      • 接收端(阻塞模式)
      • 接收端(非阻塞模式)
    • Windows系统下的UDP通信代码
      • 配置UDP
      • 发送函数
      • 接收函数
      • 关闭函数

UDP简介

UDP(User Datagram Protocol)即用户数据报协议,是OSI(Open System Interconnection,即开放式系统互联)参考模型中的一种传输层协议。
OSI参考模型
该协议提供了一种面向无连接模式的通信。由于这种特性,使用UDP协议进行传输时的开销更小,但同时并不能保证被传输的数据能够到达目的地。

UDP协议特性

总结一下UDP协议的一些特性:

  • 需要资源少
  • 不保证接收
  • 无连接

UDP协议与TCP协议的主要区别

TCP协议简介

TCP(Transmission Control Protocol)即传输控制协议,与UDP协议一样,是OSI参考模型中的一种传输层协议。

TCP协议特性

为了明晰UDP协议与TCP协议两者之间的区别,有必要对TCP的一些特性进行简要的了解。

  • 面向有连接模式
    在建立通信的时候需要三次握手,同时在断开通信的时候需要四次挥手
  • 顺序传输与累计确认
    每个数据包均有一个ID,按照ID顺序发送,同时为了保证不丢包,需要对发送的包进行应答,称为累计确认。
  • 确认与重传机制
    如果数据包没有收到,会要求发送端重发
  • 流量控制
    控制发送速度让接收端来得及接收
  • 拥塞控制
    在不堵塞的情况下尽量发挥带宽

主要区别

UDP TCP
面向无连接 面向有连接
支持一对一、一对多、多对一、和多对多的通信 只能有两个端点,实现一对一的通信
不保证数据传输的可靠性 传输数据无差错,不丢失,不重复,且按时序到达
占用资源较少 占用资源较多

UDP协议应用场景

基于以上特性,UDP协议更适合应用于:

  • 系统资源较少的嵌入式系统
  • 网络条件稳定的内部局域网
  • IP与端口号都固定的情况
  • 对实时性要求较高的情况

UDP通信代码

本文提供了两个操作系统下,UDP通信的代码。

Linux系统下的UDP通信代码

发送端

#include 
#include 
#include 
#include 
#include 

//第一个传参,目的ip地址
//第二个传参,目的端口号

int main(int argc, char *argv[])
{
//输出参数数量校验
if (argc < 3)
{
printf(“输入的参数数量错误!\n”);
printf(“请检查并重新执行此程序!\n");
return -1;
}

char buf[1000] = { "hello world" };									//声明发送数据缓存区
struct sockaddr_in sockaddr_dest;									//声明接收服务器地址

int fd = socket(AF_INET, SOCK_DGRAM, 0);							//建立socket
																	//AF_INET		IPV4
																	//SOCK_DGRAM	UDP
																	//0				传输层通信

//socket建立测试
if (client_fd < 0)
{
	printf("socket建立失败!\n");
	return -1;
}

//清空接收服务器地址
memset(&client_addr, 0, sizeof(sockaddr_dest));

//配置接收端服务器
sockaddr_dest.sin_family = AF_INET;									//ipv4
sockaddr_dest.sin_addr.s_addr = inet_addr(argv[1]);					//第一个传参,ip地址
sockaddr_dest.sin_port = htons(atoi(argv[2]));						//第二个传参,端口号

//发送数据
sendto(fd, buf, sizeof(buf), 0, (struct sockaddr *)&sockaddr_dest, sizeof(sockaddr_dest));

//关闭socket
close(fd);

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48

接收端(阻塞模式)

#include 
#include 
#include 

//第一个传参,本机ip地址
//第二个传参,本机端口号

int main(int argc, char *argv[])
{
//输出参数数量校验
if (argc < 3)
{
printf(“输入的参数数量错误!\n”);
printf(“请检查并重新执行此程序!\n");
return -1;
}

int len = sizeof(struct sockaddr_in);
char buf[100] = { 0 };

struct sockaddr_in sockaddr_recv;									//接收端通信地址结构
struct sockaddr_in sockaddr_send;									//发送端通信地址结构

int fd = socket(AF_INET, SOCK_DGRAM, 0);

sockaddr_recv.sin_family = AF_INET;
sockaddr_recv.sin_port = htons(33333);
sockaddr_recv.sin_addr.s_addr =  htonl(INADDR_ANY);

//绑定socket
int ret = bind(fd, (struct sockaddr *)&sockaddr_recv, sizeof(sockaddr_recv));

//判断是否绑定成功
if (ret == 0)
{
	printf("绑定成功!\n");
}
else
{
	printf("绑定失败!\n");
}

//以阻塞端方式接收数据
recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&sockaddr_send, &len);

//打印接收到的数据
printf("recv is %s\n", buf);

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48

接收端(非阻塞模式)

#include 
#include 
#include 
#include 
#include 
#include 

//第一个传参,本机ip地址
//第二个传参,本机端口号
//第三个船餐,等待时间,单位秒

int main(int argc, char *argv[])
{
//输出参数数量校验
if (argc < 4)
{
printf(“输入的参数数量错误!\n”);
printf(“请检查并重新执行此程序!\n");
return -1;
}

int len = sizeof(struct sockaddr_in);
char buf[100] = { 0 };

struct sockaddr_in sockaddr_recv;									//接收端通信地址结构
struct sockaddr_in sockaddr_send;									//发送端通信地址结构

int fd = socket(AF_INET, SOCK_DGRAM, 0);

sockaddr_recv.sin_family = AF_INET;
sockaddr_recv.sin_port = htons(33333);
sockaddr_recv.sin_addr.s_addr =  htonl(INADDR_ANY);

//绑定socket
int ret = bind(fd, (struct sockaddr *)&sockaddr_recv, sizeof(sockaddr_recv));

//判断是否绑定成功
if (ret == 0)
{
	printf("绑定成功!\n");
}
else
{
	printf("绑定失败!\n");
}

//使用select完成非阻塞
fd_read read;														//声明一个fd_set集合来保存被检测的句柄
struct timeval timeout;												//声明一个时间结构来保存阻塞的时间

//循环接收
while(1)
{
	FD_ZERO(&read);													//使用select函数之前先将集合清零
	FD_SET(fd, &read);												//将所要检测端socket句柄加入到集合中
	timeout.tv_sec = atoi(argv[3]);		           			     	//设置select等待的最大时间
	timeout.tv_usec = 0;
	
	int ret_select = select(4, &read, NULL, NULL, &timeout);		//检测集合read中的句柄是否有可读信息
	
	//如果返回值小于0,则select函数出错
	if (ret_select < 0)
	{
		printf("select函数出错!\n");
		return -1;
	}
	//如果返回值为0,则在计时周期内socket的状态没有发生改变
	else if (ret_select == 0)
	{
		printf("此周期内无数据接收!\n");
		sleep(1);													//无数据接收,等待1秒
	}
	//ret_select记录了发生变化的句柄的个数
	else
	{
		if (FD_ISSET(fd, &read))									//如果这个被监视端句柄真的变为可读了
		{
			memset(buf, 0, 100);									//先将接收缓存区清零
			len = sizeof(sockaddr_send);							//获取发送端通信地址结构长
			
			//在这一时间周期内以阻塞的方式接收数据
			int recv = recvfrom(server_fd, buf, BUF_LEN, 0, (struct sockaddr *)&sockaddr_send, &len);
			//判断是否接收到了数据
			if (recv == -1)
			{
				printf("接收数据失败!\n");
				return -1;
			}
			printf("recv is %s\n", buf);
		}
	}
}

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93

Windows系统下的UDP通信代码

配置UDP

int Deploy_UDP(int &skt, char *IP_Address, UINT port)
{
	skt = socket(AF_INET, SOCK_DGRAM, 0);
	struct sockaddr_in svrsockaddr;
	svrsockaddr.sin_family = AF_INET;
	svrsockaddr.sin_port = htons(port);
	svrsockaddr.sin_addr.S_un.S_addr = inet_addr(IP_Address);
	int result_bind = bind(skt, (struct sockaddr FAR*)&svrsockaddr, sizeof(struct sockaddr_in));
	//设置UDP的模式为非阻塞接收
	u_long unblock=100;
	int result_unblock = ioctlsocket(skt, FIONBIO, &unblock);
	return 0;
}

 
 
   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

发送函数

int shareSend(int skt, char *buffer, int len, char *IP_Address, UINT port)
{
	sockaddr_in Comsockaddr;
	Comsockaddr.sin_family = AF_INET;
	Comsockaddr.sin_port = htons(port);
	Comsockaddr.sin_addr.S_un.S_addr = inet_addr(IP_Address);
	return sendto(skt, buffer, len, 0, (struct sockaddr FAR*)&Comsockaddr, sizeof(sockaddr_in));
}

 
 
   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

接收函数

int UDPRecv(int skt, BYTE *buffer, int len, struct sockaddr_in &recvsock)
{
	int len_recvsock = sizeof(struct sockaddr_in);
	if (skt > 0)
	{
		return recvfrom(skt, (char *)buffer, len, 0, (struct sockaddr FAR*)&recvsock, &len_recvsock);
	}
	return -1;
}

 
 
   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

关闭函数

int UDPClose(int skt)
{
	closesocket(skt);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
                                

你可能感兴趣的:(socket,udp)