欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

实现UDP通信的例子程序【linux】(zzzc)

程序员文章站 2022-07-07 15:11:50
...

UDP通信的例子程序

实现UDP通信的例子程序【linux】(zzzc)
写一个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通信的例子程序【linux】(zzzc)

实现UDP通信的例子程序【linux】(zzzc)

运行结果为:

实现UDP通信的例子程序【linux】(zzzc)

实现UDP通信的例子程序【linux】(zzzc)

实现UDP通信的例子程序【linux】(zzzc)

实现UDP通信的例子程序【linux】(zzzc)

我们可以看到两个程序之间使用UDP协议进行互相数据的发送。

UDP因为没有“连接和应答”这种确认机制,所以UDP是不可靠通信,不过我们自己可以在应用层加入确认机制,以弥补UDP的补足。

比如每次使用UDP通信发送数据给对方后,对方必须回答,如果对方没有回答或者回答数据有错,发送方就重发,以此保证UDP的通信的可靠性,这就是通过应用程序来弥补UDP的不可靠性。

上一篇: C# Socket通信

下一篇: UDP通信小程序