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):