1.1. 使用UDP协议的流程图
UDP通信流程图如下:
服务端:socket---bind---recvfrom---sendto---close
客户端:socket----------sendto---recvfrom---close
//bind 开辟了一个接收缓冲区,任何客户端都能往里面发送数据; 客户端必须先向服务端发数据,这样服务端才能拿到客户端的ip和端口号
//客户端发数据,系统会自动分配一个端口号,这个端口在你使用的这段时间不会关闭,除非自己close
sendto()
函数原型:
int sendto(int sockfd, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen);
该函数比 send() 函数多了两个参数,
to 表示目地机的IP地址和端口号信息;
tolen 常常被赋值为sizeof (struct sockaddr)。
sendto() 函数也返回实际发送的数据字节长度或在出现发送错误时返回-1。
|
recvfrom()
函数原型:
int recvfrom(int sockfd, void *buf, int len, unsigned int flags, struct sockaddr *from, int *fromlen);
from 是一个 struct sockaddr 类型的变量,该变量保存连接机的IP地址及端口号。
fromlen 常置为sizeof (struct sockaddr)。
//如果不想获取发送方的信息,可以最后两项直接写NULL
recvfrom() 返回时,fromlen包含实际存入from中的数据字节数。recvfrom()函数返回接收到的字节数或当出现错误时返回-1,并置相应的errno。
|
udp_server.c | udp_client.c |
#include "func.h"
int main(int argc,char** argv)
{
if(argc!=3)
{
printf("error args\n");
return -1;
}
int sfd=socket(AF_INET,SOCK_DGRAM,0);
if(-1==sfd)
{
perror("socket");
return -1;
}
printf("sfd=%d\n",sfd);
struct sockaddr_in ser;
memset(&ser,0,sizeof(ser));
ser.sin_family=AF_INET;
ser.sin_port=htons(atoi(argv[2]));
ser.sin_addr.s_addr=inet_addr(argv[1]);
int ret;
ret=bind(sfd,(struct sockaddr*)&ser,sizeof(struct sockaddr));
if(-1==ret)
{
perror("bind");
return -1;
}
struct sockaddr_in client;
memset(&client,0,sizeof(client));
int addrlen=sizeof(client);
char buf[128]={0};
ret=recvfrom(sfd,buf,sizeof(buf),0,(struct sockaddr*)&client,&addrlen);
//阻塞函数,会卡在这里,直到读到客户端,拿到客户端IP和端口;
if(-1==ret)
{
perror("recvfrom");
return -1;
}
printf("client ip =%s,port =%d\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
printf("recv buf =%s\n",buf);
ret=sendto(sfd,"I am server",11,0,(struct sockaddr*)&client,sizeof(struct sockaddr));
if(-1==ret)
{
perror("sendto");
return -1;
}
close(sfd);
return 0;
}
|
#include "func.h"
int main(int argc,char** argv)
{
if(argc!=3)
{
printf("error args\n");
return -1;
}
int sfd=socket(AF_INET,SOCK_DGRAM,0);
if(-1==sfd)
{
perror("socket");
return -1;
}
printf("sfd=%d\n",sfd);
struct sockaddr_in ser;
memset(&ser,0,sizeof(ser));
ser.sin_family=AF_INET;
ser.sin_port=htons(atoi(argv[2]));
ser.sin_addr.s_addr=inet_addr(argv[1]);
int ret;
ret=sendto(sfd,"I am client",11,0,(struct sockaddr*)&ser,sizeof(struct sockaddr));
if(-1==ret)
{
perror("sendto");
return -1;
}
struct sockaddr_in client;
memset(&client,0,sizeof(client));
int addrlen=sizeof(client);
char buf[128]={0};
ret=recvfrom(sfd,buf,sizeof(buf),0,(struct sockaddr*)&client,&addrlen);
//不想获取发送方发过来的数据就直接最后两个参数写NULL
if(-1==ret)
{
perror("recvfrom");
return -1;
}
printf("server ip =%s,port =%d\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
printf("recv buf =%s\n",buf);
close(sfd);
return 0;
}
|
func.h | |
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
|
注意:操作系统的UDP接收流程如下:收到一个UDP包后,验证没有错误后,放入一个包队列中,队列中的每一个元素就是一个完整的UDP包。当应用程序通过recvfrom()读取时,OS把相应的一个完整UDP包取出,然后拷贝到用户提供的内存中,物理用户提供的内存大小是多少,OS都会完整取出一个UDP包。如果用户提供的内存小于这个UDP包的大小,那么在填充满内存后,UDP包剩余的部分就会被丢弃,以后再也无法取回。
这与TCP接收完全不同,TCP没有完整包的概念,也没有边界,OS只会取出用户要求的大小,剩余的仍然保留在OS中,下次还可以继续取出。
|