Linux环境下实现UDP通信

1. 一对一模式下的UDP通信
2. 一对一模式下的UDP双向通信(多线程实现)
3. 一对一模式下的UDP双向通信(I/O复用实现)

1. 一对一模式下的UDP通信
send端

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
 
int main(int argc, char * argv[])
{
	//检查命令行参数是否匹配
	if(argc != 3)
	{
		printf("请传递对方的ip和端口号");
		return -1;
	}
	
	int port = atoi(argv[2]);//从命令行获取端口号
	
	if( port < 1025 || port > 65535 )//0~1024一般给系统使用,一共可以分配到65535
	{
		printf("端口号范围应为1025~65535");
		return -1;
	}
	
	int udp_socket_fd = socket(AF_INET, SOCK_DGRAM, 0);//1 创建udp通信socket
	
	if(udp_socket_fd == -1)
	{
		perror("socket failed!\n");
		return -1;
	}
    
	//设置目的IP地址
    struct sockaddr_in dest_addr = {0};
    dest_addr.sin_family = AF_INET;//使用IPv4协议
    dest_addr.sin_port = htons(port);//设置接收方端口号
    dest_addr.sin_addr.s_addr = inet_addr(argv[1]); //设置接收方IP 
	
    char buf[1024] = {0};
	
    while (1)//2 循环发送数据
    {
		printf("send data : ");
		scanf("%s",buf);//从键盘输入获取要发送的消息
        sendto(udp_socket_fd, buf, strlen(buf), 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr)); 
        
		if(strcmp(buf, "exit") == 0)
		{
			break;//退出循环
		}
		
		memset(buf,0, sizeof(buf));//清空存留消息
    }
	
    close(udp_socket_fd);//3 关闭通信socket
	
    return 0;
}

recv端

#include <stdio.h>
#include <sys/types.h>          
#include <sys/socket.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
 
int main(int argc,char *argv[])
{
	//判断命令行参数是否满足
	if(argc != 2)
	{
		printf("请传递一个端口号\n");
		return -1;
	}
 
	int port = atoi(argv[1]);//将接收端口号并转换为int
	if( port < 1025 || port > 65535 )//0~1024一般给系统使用,一共可以分配到65535
	{
		printf("端口号范围应为1025~65535");
		return -1;
	}
	
	int udp_socket_fd = socket(AF_INET,SOCK_DGRAM,0);// 1.创建udp通信socket  
	
	if(udp_socket_fd < 0 )
	{
		perror("creat socket fail\n");
		return -1;
	}
 
	struct sockaddr_in  local_addr = {0};//2.设置UDP的地址并绑定 
	local_addr.sin_family  = AF_INET; //使用IPv4协议
	local_addr.sin_port	= htons(port);   //网络通信都使用大端格式
	local_addr.sin_addr.s_addr = INADDR_ANY;//让系统检测本地网卡,自动绑定本地IP
 
	int ret = bind(udp_socket_fd, (struct sockaddr*)&local_addr, sizeof(local_addr));
	
	if(ret < 0)
	{
		perror("bind fail:");
		close(udp_socket_fd);
		return -1;
	}
	else
	{
		printf("recv ready!!!\n");
	}
	
 
	struct sockaddr_in  src_addr = {0};  //用来存放对方(信息的发送方)的IP地址信息
	int len = sizeof(src_addr);	//地址信息的大小
	char buf[1024] = {0};//消息缓冲区
	
	//3 循环接收客户发送过来的数据  
	while(1)
	{
		ret = recvfrom(udp_socket_fd, buf, sizeof(buf), 0, (struct sockaddr *)&src_addr, &len);
		
		if(ret == -1)
		{
			break;
		}
		
		printf("[%s : %d] ",inet_ntoa(src_addr.sin_addr),ntohs(src_addr.sin_port));//打印消息发送方的ip与端口号
		printf("buf = %s\n",buf);
		
		if(strcmp(buf, "exit") == 0)
		{
			break;
		}
		memset(buf, 0, sizeof(buf));//清空存留消息
 
	}
	
	close(udp_socket_fd);//4 关闭通信socket
	
	return 0;
}

2. 一对一模式下的UDP双向通信

  1. 创建udp通信socket, 发送数据
  2. 设置UDP的地址并绑定
  3. 启动接收线程
  4. 开始发送数据
  5. 关闭socket
#include <stdio.h>
#include <sys/types.h>          
#include <sys/socket.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
 
void *recv_msg(void *arg);//接收消息
 
int main(int argc, char *argv[])
{
	if(argc != 2)//判断命令行参数是否满足
	{
		printf("请传递一个 端口号\n");
		return -1;
	}
 
	//将接收端口号并转换为int
	int port = atoi(argv[1]);
	if( port < 1025 || port > 65535)//0~1024一般给系统使用,一共可以分配到65535
	{
		printf("端口号范围应为1025~65535");
		return -1;
	}
	
	// 1.创建udp通信socket, 发送数据
	int udp_socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
	if(udp_socket_fd < 0 )
	{
		perror("creat socket fail\n");
		return -1;
	}
 
	//2.设置UDP的地址并绑定 
	struct sockaddr_in  local_addr = {0};
	local_addr.sin_family  = AF_INET; //使用IPv4协议
	local_addr.sin_port	= htons(port);   //网络通信都使用大端格式
	local_addr.sin_addr.s_addr = INADDR_ANY;//让系统检测本地网卡,自动绑定本地IP
 
	int ret = bind(udp_socket_fd, (struct sockaddr*)&local_addr, sizeof(local_addr));
	if(ret < 0)
	{
		perror("bind fail:");
		close(udp_socket_fd);
		return -1;
	}
	
	//启动接收线程
	pthread_t recv_thread;
	pthread_create(&recv_thread, NULL, recv_msg, (void*)&udp_socket_fd);
	
	//设置目的IP地址
    struct sockaddr_in dest_addr = {0};
    dest_addr.sin_family = AF_INET;//使用IPv4协议
    
	int dest_port = 0;//目的端口号
	char dest_ip[32] = {0};//目的IP
	char msg[1024] = {0};
	
	//循环发送消息 
	while(1)
	{
		printf("please enter the dest [IP] [PORT] [DATA]\n");
 
		scanf("%s %d %s", dest_ip, &dest_port, msg);//输入目的ip 与 端口号
		dest_addr.sin_port = htons(dest_port);//设置接收方端口号
		dest_addr.sin_addr.s_addr = inet_addr(dest_ip); //设置接收方IP 
		
		sendto(udp_socket_fd, msg, strlen(msg), 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr)); 

        if(strcmp(msg, "exit") == 0 || strcmp(msg, "") == 0)//如果准备退出会话,则结束子进程
		{
			pthread_cancel(recv_thread);//取消子线程
			break;//退出循环
		}

		memset(msg,0,sizeof(msg));//清空存留消息
		memset(dest_ip,0,sizeof(dest_ip));
	}
	
	//4 关闭通信socket
	close(udp_socket_fd);

	return 0;
}
 
//接收线程所要执行的函数 接收消息
void * recv_msg(void *arg)
{
	int ret = 0;
	int *socket_fd = (int *)arg;//通信的socket
	struct sockaddr_in  src_addr = {0};  //用来存放对方(信息的发送方)的IP地址信息
	int len = sizeof(src_addr);	//地址信息的大小
	char msg[1024] = {0};//消息缓冲区
	
	//循环接收客户发送过来的数据  
	while(1)
	{
		ret = recvfrom(*socket_fd, msg, sizeof(msg), 0, (struct sockaddr *)&src_addr, &len);
		if(ret == -1)
		{
			break;
		}

		printf("[%s : %d] ", inet_ntoa(src_addr.sin_addr), ntohs(src_addr.sin_port));//打印消息发送方的ip与端口号
		printf("The data is %s\n", msg);
		if(strcmp(msg, "exit") == 0 || strcmp(msg, "") == 0)
		{
			//通知主线程。。。
			break;
		}
		memset(msg, 0, sizeof(msg));//清空存留消息
	}
	//关闭通信socket
	close(*socket_fd);

	return NULL;
}

3. 一对一模式下的UDP双向通信(I/O复用实现)

#include <stdio.h>
#include <sys/types.h>          
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
 
int main(int argc, char *argv[])
{
	//判断命令行参数是否满足
	if(argc != 2)
	{
		printf("请传递一个端口号\n");
		return -1;
	}
 
	//将接收端口号并转换为int
	int port = atoi(argv[1]);
	if( port < 1025 || port > 65535 )//0~1024一般给系统使用,一共可以分配到65535
	{
		printf("端口号范围应为1025~65535");
		return -1;
	}
	
	// 1.创建udp通信socket  
	int udp_socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
	if(udp_socket_fd < 0 )
	{
		perror("creat socket fail\n");
		return -1;
	}
 
	//2.设置UDP的地址并绑定 
	struct sockaddr_in  local_addr = {0};
	local_addr.sin_family  = AF_INET; //使用IPv4协议
	local_addr.sin_port	= htons(port);   //网络通信都使用大端格式
	local_addr.sin_addr.s_addr = INADDR_ANY;//让系统检测本地网卡,自动绑定本地IP
 
	int ret = bind(udp_socket_fd, (struct sockaddr*)&local_addr, sizeof(local_addr));
	if(ret < 0)
	{
		perror("bind fail:");
		close(udp_socket_fd);
		return -1;
	}
	
	printf("please enter recv or send data!!!\n");
	//定义一个文件描述符集合
	fd_set fds;
	
	//存放地址信息
    struct sockaddr_in addr = { 0 };
    addr.sin_family = AF_INET;//使用IPv4协议
	int dest_port = 0;//目的端口号
	char dest_ip[32] = {0};//目的IP

	char msg[1024] = {0};
	int len = sizeof(addr);//地址信息大小
	
	//循环监视文件描述符集合 	
	while(1)
	{
		//清空文件描述符集合
		FD_ZERO(&fds);
		//把标准输入设备加入到集合中,即键盘输入数据的fd
		FD_SET(0, &fds);
		//把网络通信文件描述符加入到集合中 
		FD_SET(udp_socket_fd, &fds);
		
		ret = select(udp_socket_fd + 1, &fds, NULL, NULL, NULL);//阻塞等待,直到集合中有活跃的描述符
		if(ret < 0)//错误
		{
			perror("select fail:");
			close(udp_socket_fd);
			return -1;
		}
		else if(ret > 0) //有活跃的  ret为1
		{
			//判断是否是 标准输入设备活跃 假设是则发送数据 
			if(FD_ISSET(0, &fds))//标准输入的描述符是0,输入数据(数据发送方)
			{
				printf("please enter [IP] [PORT] [MSG]\n");
				scanf("%s %d %s", dest_ip, &dest_port, msg);//输入目的ip 与 端口号
				addr.sin_port = htons(dest_port);//设置接收方端口号
				addr.sin_addr.s_addr = inet_addr(dest_ip); //设置接收方IP 
				
				sendto(udp_socket_fd, msg, strlen(msg), 0, (struct sockaddr *)&addr,sizeof(addr)); 
				if(strcmp(msg, "exit") == 0 || strcmp(msg, "") == 0)
				{
					break;//退出循环
				}
				memset(dest_ip, 0, sizeof(dest_ip));
			}
			
			//判断是否是new_socket_fd活跃,(有消息收到)
			if(FD_ISSET(udp_socket_fd, &fds))
			{
				ret = recvfrom(udp_socket_fd, msg, sizeof(msg), 0, (struct sockaddr *)&addr, &len);
				if(ret == -1)
				{
					break;
				}

				printf("[%s : %d] ", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));//打印消息发送方的ip与端口号
				printf("msg = %s\n", msg);
				if(strcmp(msg, "exit") == 0 || strcmp(msg, "") == 0)
				{
					//通知主线程。。。
					break;
				}
			}
			
			memset(msg, 0, sizeof(msg));//清空存留消息			
		}
	}
	
	//4 关闭通信socket
	close(udp_socket_fd);

	return 0;
}
 

参考:
https://blog.csdn.net/nanfeibuyi/article/details/88540233

### 回答1: 在Linux下,可以使用socket实现UDP通信UDP是一种无连接的协议,它不保证数据传输的可靠性,但是传输速度快,适用于实时性要求较高的场景。 使用socket实现UDP通信的步骤如下: 1. 创建socket:使用socket函数创建一个UDP套接字。 2. 绑定端口:使用bind函数将套接字绑定到一个本地端口上。 3. 发送数据:使用sendto函数向指定的目标地址发送数据。 4. 接收数据:使用recvfrom函数从指定的源地址接收数据。 需要注意的是,UDP是无连接的协议,因此在发送数据时需要指定目标地址和端口号,而在接收数据时需要指定源地址和端口号。 下面是一个简单的UDP通信示例代码: ``` #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #define PORT 8888 int main() { int sockfd; struct sockaddr_in addr; char buf[1024]; // 创建UDP套接字 sockfd = socket(AF_INET, SOCK_DGRAM, ); if (sockfd < ) { perror("socket"); exit(1); } // 绑定端口 memset(&addr, , sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(PORT); addr.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < ) { perror("bind"); exit(1); } // 接收数据 while (1) { memset(buf, , sizeof(buf)); if (recvfrom(sockfd, buf, sizeof(buf), , NULL, NULL) < ) { perror("recvfrom"); exit(1); } printf("Received: %s\n", buf); } // 关闭套接字 close(sockfd); return ; } ``` 在上面的代码中,首先创建了一个UDP套接字,然后将其绑定到本地的8888端口上。接着进入一个循环,不断接收数据并打印出来。在接收数据时,由于UDP是无连接的协议,因此源地址和端口号都设置为NULL。 发送数据的代码如下: ``` #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #define PORT 8888 int main() { int sockfd; struct sockaddr_in addr; char buf[1024]; // 创建UDP套接字 sockfd = socket(AF_INET, SOCK_DGRAM, ); if (sockfd < ) { perror("socket"); exit(1); } // 设置目标地址 memset(&addr, , sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(PORT); addr.sin_addr.s_addr = inet_addr("127...1"); // 发送数据 strcpy(buf, "Hello, UDP!"); if (sendto(sockfd, buf, strlen(buf), , (struct sockaddr *)&addr, sizeof(addr)) < ) { perror("sendto"); exit(1); } // 关闭套接字 close(sockfd); return ; } ``` 在上面的代码中,首先创建了一个UDP套接字,然后设置目标地址为127...1:8888。接着将要发送的数据拷贝到buf中,使用sendto函数向目标地址发送数据。发送数据时需要指定目标地址和端口号。 以上就是在Linux下使用socket实现UDP通信的基本步骤和示例代码。 ### 回答2: UDP(User Datagram Protocol)是一种面向无连接的传输协议,它不像TCP协议一样需要建立连接、保证可靠传输。因此UDP具有简单、高效、灵活的特点,适合需要快速通信、可容忍少量数据丢失的场景。 在Linux下,使用C语言实现UDP通信需要使用socket编程。Socket是应用层与传输层之间的接口,用于实现进程之间的通讯。 1. 创建socket 在Linux下创建socket需要使用socket函数,示例代码如下: ``` int socket(int domain, int type, int protocol); ``` 其中,domain参数指定通信域,可以是AF_INET(IPv4)或AF_INET6(IPv6);type参数指定socket类型,可以是SOCK_DGRAM(无连接的数据报文套接字,即UDP)或SOCK_STREAM(面向连接的流套接字,即TCP);protocol参数一般为0,在指定type时已经确定了具体的协议。函数返回创建的套接字描述符,失败时返回-1。 2. 绑定地址 在UDP通信中,不需要像TCP一样主动连接,可以通过bind将本机的指定端口绑定到socket上。示例代码如下: ``` int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); ``` 其中,sockfd参数为创建的socket描述符,addr参数为指向本机IP地址和端口的sockaddr结构体指针,addrlen参数为sockaddr结构体大小。函数返回0表示成功,-1表示失败。 3. 发送数据 UDP协议支持一次发送一个数据报文,不需要像TCP一样分多次发送。示例代码如下: ``` ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen); ``` 其中,sockfd参数为socket描述符,buf参数为指向缓冲区的指针,len参数为数据长度,dest_addr参数为目标地址的sockaddr结构体指针,addrlen参数为sockaddr结构体大小。flags参数可以选择为0或者MSG_DONTWAIT(非阻塞发送)。函数返回成功发送的字节数,失败时返回-1。 4. 接收数据 UDP协议支持一次接收一个数据报文,同样不需要像TCP一样分多次接收。示例代码如下: ``` ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen); ``` 其中,sockfd参数为socket描述符,buf参数为指向缓冲区的指针,len参数为缓冲区大小,src_addr参数为源地址的sockaddr结构体指针,addrlen参数为sockaddr结构体大小。flags参数可以选择为0或者MSG_DONTWAIT(非阻塞接收)。函数返回成功接收的字节数,失败时返回-1。 使用以上四个函数,可以实现简单的UDP通信,示例代码如下: ``` #include <stdio.h> #include <string.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #define PORT 8080 #define BUFFER_SIZE 1024 int main() { int sockfd; char buffer[BUFFER_SIZE]; struct sockaddr_in servaddr, cliaddr; // create socket sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd == -1) { perror("socket creation failed"); return -1; } // bind local address to socket memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = INADDR_ANY; servaddr.sin_port = htons(PORT); if (bind(sockfd, (const struct sockaddr *) &servaddr, sizeof(servaddr)) == -1) { perror("bind failed"); return -1; } // receive data from client and send response int len, n; len = sizeof(cliaddr); n = recvfrom(sockfd, (char *) buffer, BUFFER_SIZE, 0, (struct sockaddr *) &cliaddr, &len); buffer[n] = '\0'; printf("Client : %s\n", buffer); sendto(sockfd, (const char *) "Hello Client!", strlen("Hello Client!"), 0, (const struct sockaddr *) &cliaddr, len); // close socket close(sockfd); return 0; } ``` 以上代码为服务器端代码,它首先创建一个UDP socket,并绑定到本地IP地址和指定端口。之后通过recvfrom函数接收客户端发送的数据,并通过sendto函数发送响应数据给客户端。客户端代码类似,只需要将recvfrom和sendto的顺序调换即可。 ### 回答3: 在Linux系统下,socket实现UDP通信非常常见,通常使用C语言编写。以下将介绍一下如何使用socket实现UDP通信。 首先,需要引入Linux socket库: ```c #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> ``` 然后,创建一个UDP socket: ```c int sockfd = socket(AF_INET, SOCK_DGRAM, 0); ``` 其中参数AF_INET表示IPv4地址,SOCK_DGRAM表示使用UDP协议,0表示默认选择协议。 接下来,需要指定目标地址和端口号: ```c struct sockaddr_in dest_addr; dest_addr.sin_family = AF_INET; dest_addr.sin_port = htons(port); inet_pton(AF_INET, ip, &dest_addr.sin_addr); ``` 其中port为目标端口号,ip为目标IP地址。 然后可以开始发送数据了,使用sendto函数: ```c int sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen); ``` 其中,sockfd为已创建的socket文件描述符,buf为要发送的数据,len为数据长度,dest_addr为目标地址。发送成功时,函数将返回发送的字节数。 接收数据需要用recvfrom函数: ```c int recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen); ``` 其中,sockfd为已创建的socket文件描述符,buf为接收到的数据缓冲区,len为缓冲区长度,src_addr为发送方地址,在成功接收到数据时,函数将返回接收的字节数。 在使用完socket后,需要使用close函数关闭socket: ```c int close(int sockfd); ``` 这样就可以在Linux系统下使用socket实现UDP通信了。当然,还有一些细节需要注意,例如,发送方必须先进行bind操作,接收方则需要进行listen操作。另外,由于UDP协议不提供可靠性保证,因此需要对数据进行校验和重传等处理,以确保数据传输的正确性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值