linux_C语言_udp的多种实现方法
- 最基本的方式(不用组播不用sigio信号不使能广播属性)
- 接收端
- 发送端
- 使用SIGIO信号的方式(使用sigio信号使用广播使能属性)
- 服务端
- 客户端
- 使用组播模式
- 服务端
- 客户端
- tcp和udp的使用区别
- 调试中遇到的问题
- 所有源码下载点这~~
最基本的方式(不用组播不用sigio信号不使能广播属性)
接收端
// 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则速度比较快,一般用于数据的发送比如音视频数据
调试中遇到的问题
- 客户端发送数据服务器能接受,服务器(运行在设备端)发送数据客户端(运行在ubuntu)无响应
解决方法是:设备端添加对应网关即可对应解决。
网关:网关就像一个搬运工,当没设网关时就等于没人帮你发送报文
所以当tcp和udp服务器和客户端在不同网段开发时更要记得设置网关啦 - 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