目录
什么是UDP
UDP(User Datagram Prototocol 用户数据报协议)
1.它是传输层的一个协议
2.它是一个无连接的协议(我们可以理解为发短信,不需要建立连接就可以发送数据)
3.它是一种不可靠传输(因为它面向无连接,所以必然是一种不可靠的传输)
4.面向数据报(可以理解为像冰块一样一块一块发送数据)
UDP报文头部
实现一个UDP服务器
服务器 server.c
int main(int argc, char* argv[])
{
if(argc != 3)
{
printf("Usage: [ipaddr] [port]\n");
exit(1);
}
//创建socket IPV4 UDP 不关心置0
int sockfd = socket(AF_INET,SOCK_DGRAM,0);
//创建addr_in结构体,并初始化
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(atoi(argv[2]));
//服务器端口号 atoi函数把字符串转换成整型 htons函数把端口号从主机字节序转成网络字节序
addr.sin_addr.s_addr = inet_addr(argv[1]);
//ip地址转换 将一个点分十进制的数转化为二进制 的网络字节序的IPV4地址
//将服务器ip和端口号进行绑定
if(bind(sockfd,(struct sockaddr*)&addr,sizeof(addr)) < 0)
{
perror("bind");
exit(2);
}
//申请缓存区收发数据
char buf[1024];
while(1)
{
struct sockaddr_in client; //捕获数据发送源的地址,以便sendto发送
socklen_t clientLen = sizeof(client);
//recvfrom()函数:从套接字端口上接收数据,并解析出数据携带的客户端端口地址信息 ,然后放
//到定义好的client结构体中,
//参数:sockfd 申请的套接字;buf 收到数据存放的缓存区 ;sizeof(buf)-1 最多存放的大小;
//0 操作方式,不关心置0; &clien 指向携带有数据源地址信息的缓冲区(指针); &clientLen //解析过程中clientLen大小可能会发生变化,所以传指针
ssize_t s = recvfrom(sockfd, buf ,sizeof(buf)-1,0,(struct sockaddr*)&client,&clientLen);
if(s > 0)
{
buf[s] ='/0';
//把转化的数据再反转输出
printf("IP:%sPort%d:%s\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port),buf);
//sendto()函数: 向指定目的地发送数据
//参数:同上,这里的client为装有发送数据的目的地址缓存区(指针); clientLen目的地址缓存区长度(整型)
sendto(sockfd,buf,strlen(buf),0,(struct sockaddr*)&client,clientLen);
}
}
close(sockfd);
return 0;
}
客户端client.c
int main(int argc,char* argv[])
{
if(argc != 3)
{
printf("Usage: [ipaddr] [port]\n");
exit(1);
}
//创建socket IPV4 UDP 指定协议 不关心置0
int sockfd = socket(AF_INET,SOCK_DGRAM,0);
//创建addr_in结构体并初始化
struct sockaddr_in client;
client.sin_family = AF_INET;
client.sin_port = htons(atoi(argv[2]));
client.sin_addr.s_addr = inet_addr(argv[1]);
//建立缓存区,准备向服务器发送数据
printf("Please Enter : \n");
char buf[1024];
while(1)
{
printf("#client : ");
fflush(stdout);
//从标准输入读数据进buf
ssize_t s = read(0,buf,sizeof(buf)-1);
if(s < 0)
{
perror("read");
exit(3);
}
buf[s-1] = '\0';
//sendto()函数: 向指定目的地发送数据
//参数:sockfd 申请的套接字;buf 收到数据存放的缓存区 ;strlen(buf) 已经写在缓存区的长度;0 操作方式,不关心置0;client为装有发送数据的目的地址缓存区(指针); clientLen目的地址缓存区长度(整型)
s = sendto(sockfd,buf,strlen(buf),0,(struct sockaddr*)&client,sizeof(client));
if(s < 0)
{
perror("sendto");
exit(4);
}
//sendto()函数:创建一个接收对端ip&端口信息的addr_in结构体server
struct sockaddr_in server;
socklen_t len = sizeof(server);
//recvfrom()函数:从套接字端口上接收数据,并解析出客户端的端口地址信息 ,然后放到定义好的client结构体中,
//参数:sockfd 申请的套接字;buf 收到数据存放的缓存区 ;sizeof(buf)-1 最多存放的大小;
0 操作方式,不关心置0; &clien 指向携带有数据源地址信息的缓冲区(指针); &clientLen 解析过程中
clientLen大小可能会发生变化,所以传指针
s = recvfrom(sockfd,buf,sizeof(buf)-1,0,(struct sockaddr*)&server,&len);
if(s < 0)
{
perror("recvfrom");
exit(5);
}
//
buf[s] = '/0';
//把转化的数据再反转输出
printf("IP : %s , Port : %d %s\n",inet_ntoa(server.sin_addr),ntohs(server.sin_port),buf);
}
//关闭
close(sockfd);
return 0;
}