6.1 基于UDP的服务器端/客户端

1.UDP套接字原理

  UDP的工作原理可以类比于信件:寄件前先写上寄件人和收件人地址,之后贴上邮票放入邮筒。我们无法确认对方是否收到,邮寄过程中也有可能丢失信件。信件是一种不可靠的传输方式。

  TCP类比于打电话, UDP类比于信件,只是从工作方式进行类比。但是速率上,TCP无法超越UDP,只能接近。应用中可以这样,传递压缩文件需要使用TCP,使数据接收可靠;如果是视频、语音等,丢失一两包数据是没问题的,速度指标更加重要。另外,如果收发的数据量小但是需要频繁连接,UDP也比TCP高效。

  可靠性方面来说,TCP的确比UDP好,但是UDP的结构比TCP简洁,不会发送类似ACK应答消息,也不会有SEQ序号,性能有时比TCP高出很多。同时区分TCP和UDP的重要标志是流控制:TCP的生命在于流控制。

  图中,IP的作用是让离开主机B的UDP数据包准确传送到主机A,但是最终交给主机A的某一UDP套接字的过程则有UDP完成,它会根据端口号进行区分。

2.基于UDP的服务器端/客户端

  在TCP中,套接字之间是一一对应的,若为10个客户端提供服务,则除了守门的服务器套接字外,还需要10个服务器端套接字。UDP中,服务器客户端都只要一个套接字就OK。

 

  UDP的数据IO函数:

#include <sys/socket.h>

ssize_t sendto(int sock, void *buff,size_t nbytes,int flags,struct sockaddr *to, socklen_t addrlen);

    成功时返回传输的字节数,失败返回-1;
    
    sock:传输数据的UDP套接字文件描述符;
    buff:保存待传输数据的缓冲地址值;
    nbytes:待传输的数据长度,字节为单位
    flags:可选参数,没有则0;
    to:目标地址信息
    addrlen:to地址值结构体长度


#include <sys/socket.h>

ssize_t recvfrom(int sock, void *buff, size_t nbytes, int flags, struct sockaddr *from, socklen_t *addrlen);

    成功时返回接收的字节数,失败时返回-1;
 
    sock:用于接收数据的UDP套接字文件描述符
    buff:保存接收数据的缓冲地址
    nbytes:可接受的最大字节数,不能超过buff的缓冲大小
    flags:可选参数,没有则0
    from:发送端地址信息
    addrlen:from地址变量大小的地址值


服务器端代码uecho_server.c

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

#define BUF_SIZE 	30

void error_handling(char *message);

int main(int argc, char *argv[]){
	int serv_sock;
	char message[BUF_SIZE];
	int str_len;
	struct sockaddr_in serv_adr;
	struct sockaddr_in clnt_adr;
	socklen_t clnt_addr_sz;
	
	if(argc != 2){
		printf("Usage : %s <port>\n",argv[0]);
		exit(1);
	}

	serv_sock = socket(PF_INET,SOCK_DGRAM,0);
	if(serv_sock == -1){
		error_handling("socket() error");
	}

	memset(&serv_adr,0,sizeof(serv_adr));
	serv_adr.sin_family=AF_INET;
	serv_adr.sin_addr.s_addr=htonl(INADDR_ANY);
	serv_adr.sin_port=htons(atoi(argv[1]));

	if(bind(serv_sock,(struct sockaddr*)&serv_adr,sizeof(serv_adr)) == -1){
		error_handling("bind() error");
	}

	while(1){
	
		clnt_addr_sz = sizeof(clnt_adr);
		str_len = recvfrom(serv_sock,message,BUF_SIZE,0,(struct sockaddr*)&clnt_adr,&clnt_addr_sz);
		sendto(serv_sock,message,str_len,0,(struct sockaddr*)&clnt_adr,clnt_addr_sz);

	}

	close(serv_sock);

	return 0;

}

void error_handling(char *message){

	fputs(message,stderr);
	fputs("\n",stderr);
	exit(1);
}

客户端代码uecho_client.c

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

#define BUF_SIZE 	30

void error_handling(char *message);

int main(int argc, char *argv[]){
	int sock;
	char message[BUF_SIZE];
	int str_len;
	socklen_t adr_sz;
	struct sockaddr_in serv_adr, from_adr;

	if(argc != 3){
		printf("Usage : %s <IP> <port>\n",argv[0]);
		exit(1);
	}

	sock = socket(PF_INET,SOCK_DGRAM,0);
	if(sock == -1){
		error_handling("socket() error");
	}

	memset(&serv_adr,0,sizeof(serv_adr));
	serv_adr.sin_family=AF_INET;
	serv_adr.sin_addr.s_addr=inet_addr(argv[1]);
	serv_adr.sin_port=htons(atoi(argv[2]));

	while(1){
	
		fputs("Insert message(q to quit):",stdout);
		fgets(message,sizeof(message),stdin);
		if(!strcmp(message,"q\n") || !strcmp(message,"Q\n"))
			break;
		sendto(sock,message,strlen(message),0,(struct sockaddr *)&serv_adr,sizeof(serv_adr));
		adr_sz = sizeof(from_adr);
		str_len = recvfrom(sock,message,BUF_SIZE,0,(struct sockaddr *)&from_adr,&adr_sz);
		message[str_len] = 0;

		printf("Message from server: %s",message);
	}

	close(sock);

	return 0;

}

void error_handling(char *message){

	fputs(message,stderr);
	fputs("\n",stderr);
	exit(1);
}

运行结果:

Server:

alex@alex-VirtualBox:~/Share/Test/tcpip$ ./uecho_server 9190



Client :

alex@alex-VirtualBox:~/Share/Test/tcpip$ ./uecho_client  127.0.0.1 9190
Insert message(q to quit):hello
Message from server: hello
Insert message(q to quit):


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值