基于UDP协议的服务器编程

  • 注:以下代码是在观看网易云课堂,职坐标发布的Linux网络编程|人工智能物联网时,跟着视频写的,仅为学习使用。代码出处在这里:https://study.163.com/course/courseMain.htm?share=1&shareId=1020885091&courseId=1002913013&trace_c_p_k2=f61d55ffe1e84f9d8ec0dd31b371723c

1 客户端发送信息,服务器返回时间

1.1 服务器

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

int sockfd;

void sig_handler(int signo) 						//信号处理,当按下CTRL+C时
{	
	if(signo==SIGINT){			
		printf("server close");
		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));
	int port=ntohs(clientaddr->sin_port);
	printf("client: %s(%d)\n",ip,port);
}

void do_service()							//与客户端进行通讯
{
	struct sockaddr_in clientaddr;
	socklen_t len=sizeof(clientaddr);
	char buffer[1024];
	memset(buffer,0,sizeof(buffer));
	/*接收客户端的数据报文
	 *这里的recvfrom是阻塞性的读写,若没有接受到消息,则会一直堵塞;
	 *若不想要一直堵塞,可以再设置套接字选项时,将setsockopt的第三
	 *个选项设置为SO_RCVTIMEO,并设置超时时间。
	 */
	if(recvfrom(sockfd,buffer,sizeof(buffer),0,
		(struct sockaddr*)&clientaddr,&len)<0){
		perror("recvfrom error");
	}else{
		//输出客户端的信息
		out_addr(&clientaddr);
		printf("client send into: %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");
		}
	}
}

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
	 *IPv4地址,UDP协议
	 */
	sockfd=socket(AF_INET,SOCK_DGRAM,0);
	if(sockfd<0){
		perror("socket error");
		exit(1);
	}
	/*设置套接字选项,因为UDP连接可能会断掉
	 *为了使下次连接时立刻就能使用,需要设置
	 *相应的选项
	 */
	int ret;
	int opt=1;
	if((ret=setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,
		&opt,sizeof(opt)))<0){
		perror("setsockopt 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;			//所有网卡上的ip地址
	if(bind(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr))<0){
		perror("bind error");
		exit(1);
	}
	
	/* 步骤3:进行通讯
	 */
	while(1){
		do_service();

	}
	return 0;
}




1.2 客户端

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

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;								//IPv4
	serveraddr.sin_port=htons(atoi(argv[2]));					//端口
	inet_pton(AF_INET,argv[1],&serveraddr.sin_addr.s_addr);		//将主机序转化为网络序
	
	char buffer[1024]="Night00 is testing a UDP program";
	//向服务器发送数据报文
	/*可以使用sendto向指定服务器发送报文,也可以先connect再send
	 *先connect的话,内核就会记录服务器的信息,这样再调用send就
	 *会向指定的服务器发送数据报文。而且,调用connect后调用recv
	 *的话,只会接受来自指定服务器的报文。不调用connect就可能会
	 *收到其他地方发来的报文。若想断开前一个connect的服务器,可
	 *再次调用connect,并将第二个参数的sin_family设为AF_UNSPEC;
	 *若想连接新服务器,只需要再connect,并修改connect的第二个参
	 *数;
	 *补:UDP的connect不会经过三次握手,只是内核记录服务器的ip和
	 *端口,TCP的connect会经过三次握手。
	
	if(connect(sockfd,(struct sockaddr*)&serveraddr),sizeof(serveraddr)<0){
		perror("connect error");				//调用connect绑定指定服务器
		exit(1);
	}
	if(send(sockfd,buffer,sizeof(buffer),0)<0){	//直接send即可,不需要指定服务器地址
		perror("sendto error");
		exit(1);
	}

	 */

	if(sendto(sockfd,buffer,sizeof(buffer),0,
		(struct sockaddr*)&serveraddr,sizeof(serveraddr))<0){
		perror("sendto error");
		exit(1);
	}else{
		//接受服务器端发送的数据报文
		memset(buffer,0,sizeof(buffer));
		size_t size;
		if((size=recv(sockfd,buffer,sizeof(buffer),0))<0){
			perror("recv error");
			exit(1);
		}else{
			printf("%s",buffer);
		}
	}
	close(sockfd);
	return 0;
}



2 域名解析

2.1 gethostbyname

#include<netdb.h>
#include<stdio.h>
#include<stdlib.h>
#include<memory.h>
#include<arpa/inet.h>

void out_addr(struct hostent *h){
	printf("hostname: %s\n",h->h_name);
	printf("addrtype: %s\n",h->h_addrtype==AF_INET ? "IPv4":"IPv6");
	char ip[16];
	memset(ip,0,sizeof(ip));
	inet_ntop(h->h_addrtype,h->h_addr_list[0],ip,sizeof(ip));
	printf("ip address: %s\n",ip);

	int i=0;
	while(h->h_aliases[i]!=NULL){
		printf("aliase: %s\n",h->h_aliases[i]);
		++i;
	}
}


int main(int argc,char *argv[])
{
	if(argc<2){
		printf("usage: %s host\n",argv[0]);
		exit(1);
	}

	struct hostent *h;
	/*gethostbyname只能解析IPv4形式的地址,而且当host不存在时,
	 *它会随机生成一个输出(不存在的),而且这个函数在多线程的
	 *环境下不太稳定
	 */

	h=gethostbyname(argv[1]);
	if(h!=NULL){
		out_addr(h);
	}else{
		printf("no %s exists\n",argv[1]);
	}
	endhostent();						//释放hostent

	return 0;
}


2.2 gethostent

#include<netdb.h>
#include<stdio.h>
#include<stdlib.h>
#include<memory.h>
#include<arpa/inet.h>

void out_addr(struct hostent *h){
	printf("hostname: %s\n",h->h_name);
	printf("addrtype: %s\n",h->h_addrtype==AF_INET ? "IPv4":"IPv6");
	char ip[16];
	memset(ip,0,sizeof(ip));
	inet_ntop(h->h_addrtype,h->h_addr_list[0],ip,sizeof(ip));
	printf("ip address: %s\n",ip);

	int i=0;
	while(h->h_aliases[i]!=NULL){
		printf("aliase: %s\n",h->h_aliases[i]);
		++i;
	}
}
int main(int argc,char *argv[])
{	
	if(argc<2){
		fprintf(stderr,"usage: %s host\n",argv[0]);
		exit(1);
	}

	struct hostent *h;
	while((h=gethostent())!=NULL){
		if(!strcmp(argv[1],h->h_name)){
			out_addr(h);
			exit(0);
		}else{
			int i=0;
			while(h->h_aliases[i]!=NULL){
				if(!strcmp(argv[1],h->h_aliases[i])){
					out_addr(h);
					exit(0);
				}
				++i;
			}
		}
	}
	endhostent();				//释放hostent
	printf("no %s exist\n",argv[1]);

	return 0;
}

3 广播

3.1 接收方

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

int sockfd;

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

int main(int argc,char *argv[])
{
	if(argc<2){
		fprintf(stderr,"usage: %s port\n",argv[0]);
		exit(1);
	}
	if(signal(SIGINT,sig_handler)==SIG_ERR){
		perror("signal sigint error");
		exit(1);
	}

	sockfd=socket(AF_INET,SOCK_DGRAM,0);
	if(sockfd<0){
		perror("socket error");
		exit(1);
	}
	
	struct sockaddr_in serveraddr;
	memset(&serveraddr,0,sizeof(serveraddr));
	serveraddr.sin_family=AF_INET;
	serveraddr.sin_port=htons(atoi(argv[1]));
	serveraddr.sin_addr.s_addr=INADDR_ANY;
	if(bind(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr))<0){
		perror("bind error");
		exit(1);
	}
	
	char buffer[1024];
	struct sockaddr_in clientaddr;
	socklen_t len=sizeof(clientaddr);
	while(1){
		memset(buffer,0,sizeof(buffer));
		memset(&clientaddr,0,sizeof(clientaddr));
		if(recvfrom(sockfd,buffer,sizeof(buffer),0,
			(struct sockaddr*)&clientaddr,&len)<0){
			perror("recvfrom error");
			exit(1);
		}else{
			char ip[16];
			inet_ntop(AF_INET,&clientaddr.sin_addr.s_addr,
				ip,sizeof(ip));
			int port=ntohs(clientaddr.sin_port);
			printf("%s(%d): %s\n",ip,port,buffer);
		}
	}
	
	return 0;
}

3.2 发送方

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

int main(int argc,char *argv[])
{
	if(argc<3){
		fprintf(stderr,"usage: %s ip port\n",argv[0]);
		exit(1);
	}
	
	int sockfd=socket(AF_INET,SOCK_DGRAM,0);
	if(sockfd<0){
		perror("perror error");
		exit(1);
	}
	int opt=1;										//设置套接字选项
	setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,
		&opt,sizeof(opt));							//采用广播的形式发送
	
	struct sockaddr_in serveraddr;
	memset(&serveraddr,0,sizeof(serveraddr));
	serveraddr.sin_family=AF_INET;
	serveraddr.sin_port=htons(atoi(argv[2]));
	inet_pton(AF_INET,argv[1],&serveraddr.sin_addr.s_addr);
	
	printf("I will broadcast...\n");
	char *info="Night00";
	size_t size=strlen(info)*sizeof(char);
	if(sendto(sockfd,info,size,0,
		(struct sockaddr*)&serveraddr,sizeof(serveraddr))<0){
		perror("sendto error");
		exit(1);
	}else{
		printf("broadcast success\n");
	}
	close(sockfd);

	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值