[linux_C语言_udp的多种实现方法及网络调试中遇到的问题]

26 篇文章 3 订阅
1 篇文章 0 订阅

最基本的方式(不用组播不使能广播属性:单播)

接收端

// 1,创建UDP套接字
int fd = Socket(AF_INET, SOCK_DGRAM, 0);

// 2,准备跟AF_INET(即IPv4网络层协议)对应的特定IP+PORT地址结构体
struct sockaddr_in addr;
socklen_t len = sizeof(addr);
bzero(&addr, len);

addr.sin_family = AF_INET;
inet_aton("192.168.45.153", &addr.sin_addr); // 绑定指定的IP,并做了字节序转换
//addr.sin_addr.s_addr = htonl(INADDR_ANY); // 绑定自动获取的IP好像一般为0.0.0.0
addr.sin_port  = htons(50001);

// 3,绑定地址
Bind(fd, (struct sockaddr *)&addr, len);

// 5,静静地等待对方的信件...
char buf[100];
int flag=0;
while (1)
{
	// 4,准备接受对方的地址信息
	struct sockaddr_in peeraddr;
	len = sizeof(peeraddr);
	bzero(&peeraddr, len);
	bzero(buf, 100);
	if (recvfrom(fd, buf, 100, 0, (struct sockaddr *)&peeraddr, &len) != 0)//此函数用于UDP套接字接受数据
	{
		flag = 1;
	}

	printf("收到【%s:%hu】的信息: %s",
		inet_ntoa(peeraddr.sin_addr),
		ntohs(peeraddr.sin_port),
		buf);

	if (flag == 1)
	{
		sendto(fd, buf, 100, 0, (struct sockaddr *)&peeraddr, len);//此函数用于向UDP套接字发送数据
		flag = 0;
	}
	printf("发送完毕\n");
}

发送端

char buf[100];
// 1,创建UDP套接字
int fd = Socket(AF_INET, SOCK_DGRAM, 0);

struct sockaddr_in addr;
socklen_t len = sizeof(addr);
bzero(&addr, len);

addr.sin_family = AF_INET;
inet_aton("192.168.45.153", &addr.sin_addr); // 绑定指定的IP,并做了字节序转换(字符串转二进制)
// addr.sin_addr.s_addr = htonl(INADDR_ANY); // 绑定自动获取的IP
addr.sin_port = htons(50001);

while (1)
{
	bzero(buf, 100);
	scanf("%s", buf);
	getchar();
	sendto(fd, buf, 100, 0, (struct sockaddr *)&addr, len);
	printf("发送完毕\n");

	// 4,准备接受对方的地址信息
	struct sockaddr_in peeraddr;
	len = sizeof(peeraddr);
	bzero(&peeraddr, len);

	// 5,静静地等待对方的信件...
	buf[100];
	bzero(buf, 100);
	recvfrom(fd, buf, 100, 0, (struct sockaddr *)&peeraddr, &len);

	printf("收到【%s:%hu】的信息: %s",
		inet_ntoa(peeraddr.sin_addr),//字节序转换(二进制转)
		ntohs(peeraddr.sin_port),
		buf);
}

使用SIGIO信号的方式(使用sigio信号使用广播使能属性:广播)

服务端

注意事项:
因为客户端会使能广播属性向所有地址(255.255.255.255)进行数据发送
所以这里不能特定指定服务器为某网卡ip地址:inet_aton(“192.168.45.153”, &addr.sin_addr);
这会导致客户端那边发送服务器接受数据失败的情况

// 1,创建UDP套接字
fd = Socket(AF_INET, SOCK_DGRAM, 0);

// 2,准备跟AF_INET(即IPv4网络层协议)对应的特定IP+PORT地址结构体
struct sockaddr_in addr;
socklen_t len = sizeof(addr);
bzero(&addr, len);

addr.sin_family = AF_INET;
//inet_aton("x.x.x.x", &addr.sin_addr); // 绑定指定的IP,并做了字节序转换
addr.sin_addr.s_addr = htonl(INADDR_ANY); // 绑定一个本机的任意可用IP
// inet_aton("192.168.45.153", &addr.sin_addr);//这里需要注意使能广播属性后不能指定IP,不然客户端对255.255.255.255广播地址进行数据发送会接受不到统一用INADDR_ANY
addr.sin_port  = htons(50002);

// 3,绑定地址
Bind(fd, (struct sockaddr *)&addr, len);

// 4,用信号的方式来异步地接收各个客户端发来的UDP信息...

// a. 捕捉信号SIGIO
signal(SIGIO, f);

// b. 设置套接字为异步工作模式(即使之收到数据是产生信号SIGIO)
long flag = fcntl(fd, F_GETFL);
flag |= O_ASYNC;
fcntl(fd, F_SETFL, flag);

// c. 指定本进程为信号的属主
fcntl(fd, F_SETOWN, getpid());


// 服务器忙别的事情
int i=0;
while(1)
{
	i++;
	printf("%d\n",i);
	sleep(1);
}
// 准备接受对方的地址信息
struct sockaddr_in peeraddr;
socklen_t len = sizeof(peeraddr);
bzero(&peeraddr, len);

char buf[100];
bzero(buf, 100);
recvfrom(fd, buf, 100, 0, (struct sockaddr *)&peeraddr, &len);	

printf("收到【%s:%hu】的信息: %s",
		inet_ntoa(peeraddr.sin_addr),
		ntohs(peeraddr.sin_port),
		buf);
// 2,准备跟AF_INET(即IPv4网络层协议)对应的特定IP+PORT地址结构体
struct sockaddr_in addr;
len = sizeof(addr);
bzero(&addr, len);

addr.sin_family = AF_INET;
inet_aton("255.255.255.255", &addr.sin_addr); // 准备好客户端的IP
addr.sin_port = htons(ntohs(peeraddr.sin_port)); // 准备好客户端的PORT
// 3,使能广播属性
int on = 1;
Setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));

sendto(fd, buf, 100, 0, (struct sockaddr *)&addr, len);

客户端

// 1,创建UDP套接字
int fd = Socket(AF_INET, SOCK_DGRAM, 0);
int ret;
// 2,准备跟AF_INET(即IPv4网络层协议)对应的特定IP+PORT地址结构体
struct sockaddr_in addr;
socklen_t len = sizeof(addr);

// 4,广播消息
char buf[100];

while(1)
{
	bzero(&addr, len);

	addr.sin_family = AF_INET;
	inet_aton("255.255.255.255", &addr.sin_addr); // 准备好服务器的IP
	// inet_aton("192.168.45.153", &addr.sin_addr);
	addr.sin_port = htons(50002); // 准备好服务器的PORT

	// 3,使能广播属性
	int on = 1;
	Setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));

	bzero(buf, sizeof(buf));
	fgets(buf,100,stdin);
	ret = sendto(fd, buf, 100, 0, (struct sockaddr *)&addr, len);
	printf("RETURN %d\n",ret);
	// 准备接受对方的地址信息
	struct sockaddr_in peeraddr;
	socklen_t len = sizeof(peeraddr);
	bzero(&peeraddr, len);

	char buf[100];
	bzero(buf, 100);
	recvfrom(fd, buf, 100, 0, (struct sockaddr *)&peeraddr, &len);

	printf("收到【%s:%hu】的信息: %s",
		inet_ntoa(peeraddr.sin_addr),
		ntohs(peeraddr.sin_port),
		buf);
}

使用组播模式

服务端

// 1,创建UDP套接字
int fd = socket(AF_INET, SOCK_DGRAM, 0);

// 2,准备地址结构体
struct sockaddr_in addr;
socklen_t len = sizeof(addr);
bzero(&addr, len);

addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port  = htons(atoi(argv[1]));

// 3,绑定地址
bind(fd, (struct sockaddr *)&addr, len);

// 4,使能广播属性
int on = 1;
setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));

// 5,准备好组播地址结构体
struct sockaddr_in addr2;
socklen_t len2 = sizeof(addr2);
bzero(&addr2, len2);

addr2.sin_family = AF_INET;
addr2.sin_addr.s_addr = inet_addr("224.0.0.100");
addr2.sin_port  = htons(50003);

// 6,静静地等待客户端的数据
char buf[100];
while(1)
{
	struct sockaddr_in peeraddr;
	socklen_t len = sizeof(peeraddr);
	bzero(&peeraddr, len);
	bzero(buf, 100);
	recvfrom(fd, buf, 100, 0, (struct sockaddr *)&peeraddr, &len);

	printf("收到【%s:%hu】的信息: %s",
			inet_ntoa(peeraddr.sin_addr),
			ntohs(peeraddr.sin_port),
			buf);
	// 转发到组播中
	if(sendto(fd, buf, strlen(buf), 0,
			(struct sockaddr *)&addr2, len2) == -1)
	{
		perror("sendto failed");
	}
}

客户端

// 1,创建UDP套接字
fd = socket(AF_INET, SOCK_DGRAM, 0);


// 2,准备存放自身地址的结构体
struct sockaddr_in myaddr;
socklen_t mylen = sizeof(myaddr);
bzero(&myaddr, mylen);

myaddr.sin_family = AF_INET;
myaddr.sin_addr.s_addr = htonl(INADDR_ANY);
myaddr.sin_port  = htons(50003);

// 3,绑定固定的地址,方便服务器主动给我发数据
bind(fd, (struct sockaddr *)&myaddr, mylen);


// 4,加入指定的多播组
struct ip_mreq m;
bzero(&m, sizeof(m));

m.imr_multiaddr.s_addr =  inet_addr("224.0.0.100");
m.imr_interface.s_addr =  htonl(INADDR_ANY);

setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &m, sizeof(m));


// a. 捕捉信号SIGIO
signal(SIGIO, f);

// b. 设置套接字为异步工作模式(即使之收到数据是产生信号SIGIO)
long flag = fcntl(fd, F_GETFL);
flag |= O_ASYNC;
fcntl(fd, F_SETFL, flag);

// c. 指定本进程为信号的属主
fcntl(fd, F_SETOWN, getpid());


// 5,准备对端服务器的地址结构体
struct sockaddr_in addr;
socklen_t len = sizeof(addr);
bzero(&addr, len);

addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(argv[1]);
addr.sin_port  = htons(atoi(argv[2]));

// 6,不断给服务器发送数据
//    当收到服务器发来的组播消息时,会触发SIGIO
//    继而会使得 fgets() 出错返回
char buf[100];
while(1)
{
	bzero(buf, 100);
	if(fgets(buf, 100, stdin) == NULL)
	{
		perror("fgets failed");
		continue;
	}

	sendto(fd, buf, strlen(buf), 0,
			(struct sockaddr *)&addr, len);
}

tcp和udp的使用区别

tcp具有可靠性所以一般用于cmd命令的一些重要信息收发
udp则速度比较快,一般用于数据的发送比如音视频数据

调试中遇到的问题

  1. 客户端发送数据服务器能接受,服务器(运行在设备端)发送数据客户端(运行在ubuntu)无响应
    网络环境:ubuntu ip:192.168.1.17 gw:192.168.1.1 设备 ip:192.168.1.2
    解决方法是:设备端添加对应网关即可对应解决。
    route add default gw 192.168.1.1
    网关:网关就像一个大门,当没设网关时就等于别人发送给你的报文没门可进
    所以当tcp和udp服务器和客户端在不同网段开发时更要记得设置网关啦
  2. tcp开发客户端和服务器不同网段的时候connect不上问题,服务器:(ip:192.168.1.17、gw:192.168.1.1),客户端(ip:192.190.1.2、gw:192.190.1.1)
    最后发现是掩码问题导致,解决方法两边各设置一下:
    在这里插入图片描述
    在这里插入图片描述
    如果同网段下是这样设置的:(举个例子)
    ifconfig ens33 192.168.1.17 netmask 255.255.255.0
    route add -net 255.255.255.0 netmask 255.255.255.0 dev ens33

所有源码下载点这~~

一些比较常用的网络命令

netstat -anp

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

I&You

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值