Linux学习_UDP编程

Posted Leslie X徐

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux学习_UDP编程相关的知识,希望对你有一定的参考价值。

UDP编程

UDP编程模型

  1. UDP客户端服务器编程模型
  • 服务器端调用序列
    • 调用socket函数创建套接字
    • 调用bind绑定本地地址和端口
    • 调用readfrom函数获取发送来的数据
    • 调用sendto函数向对方发送数据报文
    • 调用close关闭套接字
  • 客户端调用序列
    • 调用socket函数创建套接字
    • 调用bind绑定本地地址和端口,可不绑定
    • 调用sendto函数向对方发送数据报文
    • 调用readfrom函数获取发送来的数据
    • 调用close关闭套接字

数据传输

  • 发送数据报文函数
#include <sys/socket.h>
ssize_t send(int sockfd,           //套接字描述符
					const void* buf,  //要传送的数据
					size_t nbytes,     //数据长度
					int flag);             //默认为0
返回:成功返回发送的字节数,出错返回-1。

ssize_t sendto(int sockfd,
					const void* buf,
					size_t nbytes,
					int flag,
					const struct sockaddr* destaddr, //目的地址信息
					socklen_t destlen);	 //地址结构体长度
返回:成功返回发送的字节数,出错返回-1。

ssize_t sendmsg(int sockfd,
						const struct msghdr* msg,
						int flag);	
返回:成功返回发送的字节数,出错返回-1。

struct msghdr {
   void         *msg_name;       /* optional address */
   socklen_t     msg_namelen;    /* size of address */
   struct iovec *msg_iov;        /* scatter/gather array */
   size_t        msg_iovlen;     /* # elements in msg_iov */
   void         *msg_control;    /* ancillary data, see below */
   size_t        msg_controllen; /* ancillary data buffer len */
   int           msg_flags;      /* flags (unused) */
 };
										
  • 接收数据报文函数
#include <sys/socket.h>
ssize_t recv(int sockfd,
					void* buf,
					size_t nbytes,
					int flag);

ssize_t recvfrom(int sockfd,
						void* restrict buf,
						size_t len,
						int flag,
						struct sockaddr* restrict addr,
						socklen_t* restrict addrlen);

ssize_t recvmsg(int sockfd,
						struct msghdr* msg,
						int flag);
返回:有消息则返回消息的字节数,无消息返回0,出错返回-1

服务器和客户端编程

  • 服务器编程
/*
 * time_udp_server.c
 */


#include <stdio.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <time.h>
#include <arpa/inet.h>

int sockfd;
void sig_handler(int signo)
{
	if(signo == SIGINT){
		printf("server close\\n");
		close(sockfd);
		exit(1);
	}
}

//输出客户端的信息
void out_addr(struct sockaddr_in *clientaddr)
{
	char ip[16];
	memset(ip,0,sizeof(ip));
	inet_ntop(AF_INET, 
					&clientaddr->sin_addr.s_addr, ip, sizeof(ip)); //ip
	int port = ntohs(clientaddr->sin_port); //port
	printf("client: %s(%d)\\n",ip,port);
}

//与客户端进行通信
void do_service(void)
{
	struct sockaddr_in clientaddr;
	socklen_t len = sizeof(clientaddr);
	char buffer[1024];
	memset(buffer, 0, sizeof(buffer));
	//接收客户端的数据报文
	if(recvfrom(sockfd,buffer,sizeof(buffer), 0, //flag默认0
				(struct sockaddr*)&clientaddr, &len)<0){
		perror("recvfrom error");
		exit(1);			
	}else{
		out_addr(&clientaddr);
		printf("client send info: %s\\n",buffer);
		
		//向客户端发送数据报文
		long int t = time(0);
		char *ptr = ctime(&t);
		size_t size = strlen(ptr)*sizeof(char);
		//传送时间到客户端
		if(sendto(sockfd, ptr, size, 0, 
				(struct sockaddr*)&clientaddr, len)<0){
			perror("sendto error");
			exit(1);				
		}
	}
}

//主函数传入监听的端口
int main(int argc, char **argv)
{
	if(argc<2){
		printf("usage: %s port\\n",argv[0]);
		exit(1);
	}
	if(signal(SIGINT,sig_handler) == SIG_ERR){
		perror("signal sigint error");
		exit(1);
	}
	
	/*步骤1:创建socket*/
	sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if(sockfd<0){perror("socket error");exit(1);}
	
	int ret;
	int opt = 1;
	//设置套接字选项,SO_REUSEADDR让停止的端口能够马上使用
	if((ret = setsockopt(sockfd, SOL_SOCKET, 
				SO_REUSEADDR, &opt, sizeof(opt)))<0){
		perror("set socket opt error");
		exit(1);			
	}
		
	/*步骤2:调用bind函数对socket和地址进行绑定*/
	struct sockaddr_in serveraddr;
	memset(&serveraddr, 0, sizeof(serveraddr));
	serveraddr.sin_family = AF_INET; //IPv4
	serveraddr.sin_port = htons(atoi(argv[1])); //port
	serveraddr.sin_addr.s_addr = INADDR_ANY; //INADDR_ANY获得所有ip地址请求
	if(bind(sockfd, (struct sockaddr*)&serveraddr,
				sizeof(serveraddr))<0){
		perror("bind error");
		exit(1);			
	}
	
	/*步骤3:和客户端进行双向的数据通信*/
	while(1){
		do_service();
	}
	
	return 0;
}


  • 客户机编程
/*
 * time_udp_client.c
 * 
 */


#include <stdio.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <memory.h>
#include <arpa/inet.h>

//判断输入是否为域名
int is_host(struct hostent* host,char* name)
{
	if(!strcmp(host->h_name,name))return 1;
	int i=0;
	while(host->h_aliases[i]!=NULL){
		if(!strcmp(host->h_aliases[i],name))return 1;
		++i;
	}
	return 0;
}

unsigned int get_ip_by_name(char *name)
{
	unsigned int ip = 0;
	struct hostent *host;
	while((host = gethostent())!=NULL){
		if(is_host(host,name)){ //判断传进来的域名是否在/etc/hosts里面
			memcpy(&ip, host->h_addr_list[0], 4);
			break;
		}
	}
	endhostent();
	return ip;
}

int main(int argc, char **argv)
{
	if(argc<3){
		printf("usage: %s ip port\\n",argv[0]);
		exit(1);
	}
	
	/*步骤1:创建socket*/
	int sockfd = socket(AF_INET, SOCK_DGRAM,0);
	if(sockfd<0){
		perror("socket error");
		exit(1);
	}
	
	/*步骤2:调用recvfrom和sendto函数
	 * 和服务器端双向通讯
	 * */
	 struct sockaddr_in serveraddr;
	 memset(&serveraddr,0,sizeof(serveraddr));
	 serveraddr.sin_family = AF_INET;
	 serveraddr.sin_port = htons(atoi(argv[2]));//port
	 //inet_pton(AF_INET,argv[1],
					//&serveraddr.sin_addr.s_addr);//ip
					
	/*使用域名解析*/
	unsigned int ip = get_ip_by_name(argv[1]);
	if(ip!=0){
		serveraddr.sin_addr.s_addr = ip;
	}else{ //如果根据域名无法找到ip
		inet_pton(AF_INET,argv[1],&serveraddr.sin_addr.s_addr);
	}
	
	if(connect(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr))<0){
		perror("connect error");
		exit(1);
	}
	
	char buffer[1024] = "hello iotek";
	//向服务器发送数据报文
	if(sendto(sockfd,buffer,sizeof(buffer),0,
		(struct sockaddr*)&serveraddr,sizeof(serveraddr))<0){
			perror("sendto error");
			exit(1);
	}
	else{
		/*成功发送后,开始接收服务器发送的数据报文(因为之前已经知道服务器端的信息,所以直接用recv函数)
		 * */
		memset(buffer,0,sizeof(buffer));
		if(recv(sockfd,buffer,sizeof(buffer),0)<0){
			perror("recv error");
			exit(1);
		}
		else{
			printf("%s",buffer);
		}
	}	
	close(sockfd);
	
	
	return 0;
}



输出:

客户端:
pi@raspberrypi:~/haitong-learning/Linux/homework/网络编程 $ ./time_udp_client xuwenhan 8888
Wed May 19 00:14:30 2021
pi@raspberrypi:~/haitong-learning/Linux/homework/网络编程 $ ./time_udp_client www.xu.com 8888
Wed May 19 00:14:43 2021
pi@raspberrypi:~/haitong-learning/Linux/homework/网络编程 $ 


服务器:
pi@raspberrypi:~/haitong-learning/Linux/homework/网络编程 $ ./time_udp_server 8888
client: 192.168.31.174(56188)
client send info: hello iotek
client: 192.168.31.174(45804)
client send info: hello iotek
^Cserver close

pi@raspberrypi:~/haitong-learning/Linux/homework/网络编程 $ 

以上是关于Linux学习_UDP编程的主要内容,如果未能解决你的问题,请参考以下文章

Linux网络编程必学!——Linux_网络编程_UDP

Python学习笔记__16.3章 UDP编程

Linux下_UDP协议网络编程函数接口

python学习_day31_基于udp协议编程

Python 之 Socket编程(TCP/UDP)

Linux:UDP Socket编程(代码实战)