Bootstrap

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

;