前言
UDP,全称 User Datagram Protocol,中文名称为用户数据报协议,主要用来支持那些需要在计算机之间传输数据的网络连接。UDP 协议从问世至今已经被使用了很多年,虽然目前 UDP 协议的应用不如 TCP 协议广泛,但 UDP 依然是一种非常实用和可行的网络传输层协议。尤其是在一些实时性很强的应用场景中,比如网络游戏、视频会议等,UDP 协议的快速能力更具有独特的魅力。
关键字:Linux socket UDP GB级 文件传输
服务器端
使用socket()函数加载套接字库,创建套接字;
使用bind()函数来绑定套接字到一个IP地址和一个端口上;
使用Listen()函数将套接字设置为监听模式,等待连接请求;
用accept()函数接受连接请求,返回一个新的对应于连接的套接字;
用send()/sendto()函数返回的套接字和客户端进行通信;
返回到第三步,等待另一个请求;
用closesocket()函数关闭套接字。
客户端
使用socket()函数加载套接字库,创建套接字;
使用connect()函数向服务器发出连接请求;
用send()/recv()函数与服务器端进行通信;
通信完成后,用closesocket()函数关闭套接字。
服务器
创建套接字
int sockfd;
if((sockfd = socket(AF_INET,SOCK_DGRAM,0))<0)
{
printf("socket build error!\n");
}
else
{
printf("socket build success!\n");
}
socket
是一个函数,那么它也有返回值,当套接字创建成功时,返回套接字,失败返回“-1”,错误代码则写入errno
中。AF_INET
表示IPv4,SOCK_DGRAM
数据传输方式 注:TCP使用的是流套接字(SOCK_STREAM
),UDP使用的是数据报套接字(SOCK_DGRAM
)
绑定套接字到一个IP地址和一个端口上
struct sockaddr_in server
memset(&server,0,sizeof(server)); //清空server结构体
server.sin_family= AF_INET;
server.sin_addr.s_addr = htonl(INADDR_ANY);
server.sin_port = htons(8888);
if((bind(sockfd,(struct sockaddr*)&server,sizeof(server)))==-1)
{
printf("bind error!\n");
}
else
{
printf("bind success!\n");
}
sockaddr_in
是一个数据结构;用做bind
、connect
、recvfrom
、sendto
等函数的参数,指明地址信息sin_family
表示地址类型,对于基于TCP/IP传输协议的通信,该值只能是AF_INET
;sin_prot
表示端口号,例如:21 或者 80 或者 27015,总之在0 ~ 65535之间;sin_addr
表示32位的IP地址,例如:192.168.1.5 或 202.96.134.133;sin_zero
表示填充字节,一般情况下该值为0;bind()
函数int bind( int sockfd , const struct sockaddr * my_addr, socklen_t addrlen);
bind()
函数通过给一个套接字接口分配一个地址来建立捆绑。
接收客户端数据
从客户端接收文件名
struct sockaddr_in client;
int addrlen=sizeof(struct sockaddr);
printf("waiting....\n");
memset(filename,'\0',sizeof(filename));
memset(filepath,'\0',sizeof(filepath));
lenfilepath = recvfrom(sockfd,filepath,100,0,(struct sockaddr *)&client,&addrlen);
printf("filepath :%s\n",filepath);
if(lenfilepath=1)
{ printf("recv finished!\n");}
从客户端接收文件
while(fileTrans =recvfrom(sockfd,buffer,BUFFER_SIZE,0,(struct sockaddr *)&client,&addrlen))
{
printf("data= %d ",data);
data++;
if(fileTrans<0)
{
printf("recv2 error!\n");
break;
}
writelength = fwrite(buffer,sizeof(char),fileTrans,fp);
if(fileTrans < BUFFER_SIZE)
{
printf("finish writing!\n");
break;
}else{
printf("write succ! %d fileTrans=%d\n",writelength,fileTrans);
printf("write successful!\n");
//break;
}
Int recvfrom( SOCKET s,char FAR *buf,int len,nt flags);
不论是客户还是服务器应用程序都用recvfrom
函数从另一端接收数据,根据返回值判断数据接收情况。s
指定接收端套接字描述符;buf
指明一个缓冲区,该缓冲区用来存放recvfrom
函数接收到的数据;len
指明buf
的长度;flags
一般为0;
客户端
创建套接字
int sockfd;
if((sockfd = socket(AF_INET,SOCK_DGRAM,0))<0)
{
printf("socket build error!\n");
}
else
{
printf("socket build success!\n");
}
向服务器发出连接请求
memset(&server,0,sizeof(server));
server.sin_family= AF_INET;
server.sin_port = htons(8888);
if(inet_pton(AF_INET,"127.0.0.1",&server.sin_addr)<0)
{
printf("bind error!\n");
}
else
{
printf("bind success!\n");
}
与服务器之间进行数据传输
lenpath = sendto(sockcd,filepath,strlen(filepath),0,(struct sockaddr *)&server,addrlen);// put file path to sever
if(lenpath<0)
{
printf("filepath send error!\n");
}
else
{
printf("filepath send success!\n");
}
printf("begin send data...\n");
int data= 1;
while((fileTrans = fread(buffer,sizeof(char),BUFFER_SIZE,fp))>0)
{
printf("data= %d ",data);
data++;
//printf("fileTrans =%d\n",fileTrans);
if(sendto(sockcd,buffer,fileTrans,0,(struct sockaddr *)&server,addrlen)<0)
{
printf("send failed!\n");
break;
}
else{
printf("send successful!\n");
}
SendTo
是一个计算机函数,指向一指定目的地发送数据,sendto()
适用于发送未建立连接的UDP数据包 (参数为SOCK_DGRAM
)。int sendto(SOCKET s, const char FAR *buf, int len, int flags);
参数s为已连接的本地套接字描述符。buf
指向存有发送数据的缓冲区的指针,其长度由len
指定。flags
指定传输控制方式,如是否发送带外数据等。 如果没有错误发生,send()
返回总共发送的字节数。否则它返回SOCKET_ERROR
。
调试
使用visual studio code进行调试,安装c/c++扩展。 先运行服务器,后运行客户端;然后在服务器中输入拟发送的数据;
参考文献
何润岸. 基于UDP进行大规模数据传输的可靠传输系统的设计与实现[D]. 2015.
陈双全. 浅论C语言在提高程序执行效率上的编程技巧[J]. 信息技术与信息化, 2019(10).
云思雨. 议Linux系统下的计算机C语言编程技巧[J]. 环球市场信息导报, 2017, 000(047):121-121.
李芙蓉. 基于Winsock流套接字的进程通信的实现[J]. 西安文理学院学报(自然科学版), 2010, 13(002):81-84.
张恺. 基于UDP的可靠文件传输协议的设计与实现[D]. 西安电子科技大学, 2014.