实现UDP通信的例子程序【linux】(zzzc)
UDP通信的例子程序
写一个A程序,然后再写一个B程序,让这两个程序之间使用UDP通信。
第1步:调用socket创建套接字文件
函数原型
int socket(int domain, int type, int protocol);
参数
参数如何指定?
domain:AF_INET
表示使用的是IPV4的TCP/IP协议族。
type:SOCK_DGRAM
表示使用的是协议族中的无连接、不可靠、固定长度的数据报协议。
protocol:0
在TCP/IP协议族里面只有UDP协议属于无连接、不可靠、固定长度的数据报协议这种情况,所以指定完参数之后就可以确定使用的一定是UDP协议。
第2步:bind绑定固定的ip和端口
如果只是使用UDP发送数据,可以不绑定固定的ip和端口,如果要接收数据的话,必须绑定固定的ip和端口。
疑问:为什么UDP接收数据时需要bind 固定的ip和端口?
如果不绑定,每次使用的都是自动设置的ip和端口,自动设置的话,ip和端口就是不定的,如果每次都是变化的,发送数据端在指定IP和端口时就不知所应该写什么了。所以如果要接收数据,就必须绑定固定的IP和端口。
第3步:调用sendto函数,发送数据
为什么没有listen和accept?
因为UDP没有连接的过程,listen和accept是TCP连接时才会用到函数,既然UDP没有连接,自然也就用不到了。
函数原型
#include <sys/types.h>
#include <sys/socket.h>
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
功能
发送数据,当后两个参数为NULL和0时,功能等价于send,send专门用于TCP这种面向连接的通信,但对于像UDP这种非连接的通信,必须使用sendto,因为此时必须使用最后两个参数。
返回值
调用成功返回发送的字节数,调用失败返回-1,errno被设置。
参数
sockfd
socket返回的套接字描述符,对于UDP来说,套接字描述符直接用于通信。
buf
存放数据的应用缓存
len
应用缓存的大小。
flags
一般写0,表示阻塞发送数据
其它常用的选项与send的flags一样。
dest_addr
填写目标ip和端口
前面就说过,对于UDP来说,UDP没有连接的过程,所以没有自动记录对方的ip和端口,所以每次发送数据的时候,都需要指定对方的ip和端口。
addelen
dest_addr的大小
第4步:调用recvfrom函数,接收数据
函数原型
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
功能
接收数据,最后两个参数与NULL和NULL时,功能与recv功能相同。
UDP通信时,常使用的是recvfrom函数,因为需要用到后两个参数。
返回值
成功返回接收到的字节数,失败返回-1,ernno被设置
参数
sockfd
socket返回的套接字文件描述符
buf
应用缓存,用于存放接收到的数据
len
buf的大小
flags
一般写0,表示阻塞接收数据,其它的常用设置同recv函数
src_addr
用于保存“数据发送方”的ip和端口,以便“接收方”回答对方。
如果是局域网通信,ip就是局域网ip,如果是广域网通信,ip就是对方的所在路由器的公网ip。
如果不需要回答对方数据的话,可以不用保存对方的ip和端口,这时recvfrom的最后两个参数写NULL,此时与recv函数的功能完全等价。
addrlen
src_addr的大小
代码演示
UDP_A.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <signal.h>
void print_err(char *str, int line, int err_no)
{
printf("%d, %s: %s\n", line, str, strerror(err_no));
exit(-1);
}
int sockfd = -1;
struct sockaddr_in peer_addr = {0};
void *pth_fun(void *pth_arg)
{
int ret = -1;
char buf[100] = {0};
int peer_addr_size = 0;
while(1)
{
bzero(buf, sizeof(buf));
peer_addr_size = sizeof(peer_addr);
/* 接收对方发送的数据,并保存对方的ip和端口,以便回答 */
ret = recvfrom(sockfd, buf, sizeof(buf), 0, \
(struct sockaddr *)&peer_addr, &peer_addr_size);
if(ret == -1) print_err("recvfrom fial", __LINE__, errno);
else if(ret > 0)
{
printf("peerip:%s, peerport:%d\n", \
inet_ntoa(peer_addr.sin_addr), ntohs(peer_addr.sin_port));
printf("%s\n", buf);
}
}
return NULL;
}
int main(int argc, char **argv)
{
int ret = 0;
char buf[100] = {0};
if(argc != 3)
{
printf("./a.out peer_ip peer_port\n");
exit(-1);
}
/* 创建套接字文件,指定使用UDP协议 */
sockfd = socket(PF_INET, SOCK_DGRAM, 0);
if(sockfd == -1) print_err("socket fial", __LINE__, errno);
/* 使用UDP来通信,如果要接收数据的话,必须绑
* 定固定的IP和端口,方便对方发送数据 */
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(5001);
addr.sin_addr.s_addr = inet_addr("192.168.31.162 ");
ret = bind(sockfd, (struct sockaddr *)&addr, sizeof(addr));
if(ret == -1) print_err("bind fial", __LINE__, errno);
/* 创建此线程,用于循环接收对方发送的数据 */
pthread_t tid;
ret = pthread_create(&tid, NULL, pth_fun, NULL);
if(ret != 0) print_err("pthread_create fail", __LINE__, errno);
/* 主线程发送数据 */
while(1)
{
/* 设置对方的IP和端口 */
peer_addr.sin_family = AF_INET;
peer_addr.sin_port = htons(atoi(argv[2]));
peer_addr.sin_addr.s_addr = inet_addr(argv[1]);
bzero(buf, sizeof(buf));
scanf("%s", buf);
ret = sendto(sockfd, buf, sizeof(buf), 0, \
(struct sockaddr *)&peer_addr, sizeof(peer_addr));
if(ret == -1) print_err("sendto fial", __LINE__, errno);
}
return 0;
}
UDP_B.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <signal.h>
void print_err(char *str, int line, int err_no)
{
printf("%d, %s: %s\n", line, str, strerror(err_no));
exit(-1);
}
int sockfd = -1;
struct sockaddr_in peer_addr = {0};
void *pth_fun(void *pth_arg)
{
int ret = -1;
char buf[100] = {0};
int peer_addr_size = 0;
while(1)
{
bzero(buf, sizeof(buf));
peer_addr_size = sizeof(peer_addr);
/* 接收对方发送的数据,并保存对方的ip和端口,以便回答 */
ret = recvfrom(sockfd, buf, sizeof(buf), 0, \
(struct sockaddr *)&peer_addr, &peer_addr_size);
if(ret == -1) print_err("recvfrom fial", __LINE__, errno);
else if(ret > 0)
{
printf("peerip:%s, peerport:%d\n", \
inet_ntoa(peer_addr.sin_addr), ntohs(peer_addr.sin_port));
printf("%s\n", buf);
}
}
return NULL;
}
int main(int argc, char **argv)
{
int ret = 0;
char buf[100] = {0};
if(argc != 3)
{
printf("./a.out peer_ip peer_port\n");
exit(-1);
}
/* 创建套接字文件,指定使用UDP协议 */
sockfd = socket(PF_INET, SOCK_DGRAM, 0);
if(sockfd == -1) print_err("socket fial", __LINE__, errno);
/* 使用UDP来通信,如果要接收数据的话,必须绑
* 定固定的IP和端口,方便对方发送数据 */
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(5009);
addr.sin_addr.s_addr = inet_addr("192.168.31.162 ");
ret = bind(sockfd, (struct sockaddr *)&addr, sizeof(addr));
if(ret == -1) print_err("bind fial", __LINE__, errno);
/* 创建此线程,用于循环接收对方发送的数据 */
pthread_t tid;
ret = pthread_create(&tid, NULL, pth_fun, NULL);
if(ret != 0) print_err("pthread_create fail", __LINE__, errno);
/* 主线程发送数据 */
while(1)
{
/* 设置对方的IP和端口 */
peer_addr.sin_family = AF_INET;
peer_addr.sin_port = htons(atoi(argv[2]));
peer_addr.sin_addr.s_addr = inet_addr(argv[1]);
bzero(buf, sizeof(buf));
scanf("%s", buf);
ret = sendto(sockfd, buf, sizeof(buf), 0, \
(struct sockaddr *)&peer_addr, sizeof(peer_addr));
if(ret == -1) print_err("sendto fial", __LINE__, errno);
}
return 0;
}
在不同终端窗口同时运行程序:
运行结果为:
我们可以看到两个程序之间使用UDP协议进行互相数据的发送。
UDP因为没有“连接和应答”这种确认机制,所以UDP是不可靠通信,不过我们自己可以在应用层加入确认机制,以弥补UDP的补足。
比如每次使用UDP通信发送数据给对方后,对方必须回答,如果对方没有回答或者回答数据有错,发送方就重发,以此保证UDP的通信的可靠性,这就是通过应用程序来弥补UDP的不可靠性。
上一篇: C# Socket通信
下一篇: UDP通信小程序
推荐阅读
-
使用C#实现基于TCP和UDP协议的网络通信程序的基本示例
-
网络编程概念、UDP通信程序和TCP通信程序的通信原理及实现程序
-
实现UDP通信的例子程序【linux】(zzzc)
-
利用Qt/C++在腾讯云/阿里云服务器搭建TCP/IP协议实现网络通信以及Qt在linux下的安装和程序打包踩坑(详解)
-
实现TCP通信的客户端程序 【linux】(zzz)
-
linux网络编程之用socket实现简单客户端和服务端的通信(基于UDP)
-
传输层使用udp实现客户端和服务端通信的小程序 (可以实现多个客户端与服务端通信)
-
使用C#实现基于TCP和UDP协议的网络通信程序的基本示例
-
利用Qt/C++在腾讯云/阿里云服务器搭建TCP/IP协议实现网络通信以及Qt在linux下的安装和程序打包踩坑(详解)