首页 > 编程 > C > 正文

Linux网络编程之基于UDP实现可靠的文件传输示例

2020-01-26 15:24:13
字体:
来源:转载
供稿:网友

了解网络传输协议的人都知道,采用TCP实现文件传输很简单。相对于TCP,由于UDP是面向无连接、不可靠的传输协议,所以我们需要考虑丢包和后发先至(包的顺序)的问题,所以我们想要实现UDP传输文件,则需要解决这两个问题。方法就是给数据包编号,按照包的顺序接收并存储,接收端接收到数据包后发送确认信息给发送端,发送端接收确认数据以后再继续发送下一个包,如果接收端收到的数据包的编号不是期望的编号,则要求发送端重新发送。

下面展示的是基于linux下C语言实现的一个示例程序,该程序定义一个包的结构体,其中包含数据和包头,包头里包含有包的编号和数据大小,经过测试后,该程序可以成功传输一个视频文件。

具体实现代码如下:

server端代码如下:

/*************************************************************************   > File Name: server.c   > Author: SongLee  ************************************************************************/ #include<sys/types.h> #include<sys/socket.h> #include<unistd.h> #include<netinet/in.h> #include<arpa/inet.h> #include<stdio.h> #include<stdlib.h> #include<errno.h> #include<netdb.h> #include<stdarg.h> #include<string.h>  #define SERVER_PORT 8000 #define BUFFER_SIZE 1024 #define FILE_NAME_MAX_SIZE 512  /* 包头 */ typedef struct {   int id;   int buf_size; }PackInfo;  /* 接收包 */ struct SendPack {   PackInfo head;   char buf[BUFFER_SIZE]; } data;   int main() {   /* 发送id */   int send_id = 0;    /* 接收id */   int receive_id = 0;    /* 创建UDP套接口 */   struct sockaddr_in server_addr;   bzero(&server_addr, sizeof(server_addr));   server_addr.sin_family = AF_INET;   server_addr.sin_addr.s_addr = htonl(INADDR_ANY);   server_addr.sin_port = htons(SERVER_PORT);    /* 创建socket */   int server_socket_fd = socket(AF_INET, SOCK_DGRAM, 0);   if(server_socket_fd == -1)   {     perror("Create Socket Failed:");     exit(1);   }    /* 绑定套接口 */   if(-1 == (bind(server_socket_fd,(struct sockaddr*)&server_addr,sizeof(server_addr))))   {     perror("Server Bind Failed:");     exit(1);   }    /* 数据传输 */   while(1)   {       /* 定义一个地址,用于捕获客户端地址 */     struct sockaddr_in client_addr;     socklen_t client_addr_length = sizeof(client_addr);      /* 接收数据 */     char buffer[BUFFER_SIZE];     bzero(buffer, BUFFER_SIZE);     if(recvfrom(server_socket_fd, buffer, BUFFER_SIZE,0,(struct sockaddr*)&client_addr, &client_addr_length) == -1)     {       perror("Receive Data Failed:");       exit(1);     }      /* 从buffer中拷贝出file_name */     char file_name[FILE_NAME_MAX_SIZE+1];     bzero(file_name,FILE_NAME_MAX_SIZE+1);     strncpy(file_name, buffer, strlen(buffer)>FILE_NAME_MAX_SIZE?FILE_NAME_MAX_SIZE:strlen(buffer));     printf("%s/n", file_name);      /* 打开文件 */     FILE *fp = fopen(file_name, "r");     if(NULL == fp)     {       printf("File:%s Not Found./n", file_name);     }     else     {       int len = 0;       /* 每读取一段数据,便将其发给客户端 */       while(1)       {         PackInfo pack_info;          if(receive_id == send_id)         {           ++send_id;           if((len = fread(data.buf, sizeof(char), BUFFER_SIZE, fp)) > 0)           {             data.head.id = send_id; /* 发送id放进包头,用于标记顺序 */             data.head.buf_size = len; /* 记录数据长度 */             if(sendto(server_socket_fd, (char*)&data, sizeof(data), 0, (struct sockaddr*)&client_addr, client_addr_length) < 0)             {               perror("Send File Failed:");               break;             }             /* 接收确认消息 */             recvfrom(server_socket_fd, (char*)&pack_info, sizeof(pack_info), 0, (struct sockaddr*)&client_addr, &client_addr_length);             receive_id = pack_info.id;            }           else           {             break;           }         }         else         {           /* 如果接收的id和发送的id不相同,重新发送 */           if(sendto(server_socket_fd, (char*)&data, sizeof(data), 0, (struct sockaddr*)&client_addr, client_addr_length) < 0)           {             perror("Send File Failed:");             break;           }           /* 接收确认消息 */           recvfrom(server_socket_fd, (char*)&pack_info, sizeof(pack_info), 0, (struct sockaddr*)&client_addr, &client_addr_length);           receive_id = pack_info.id;          }       }       /* 关闭文件 */       fclose(fp);       printf("File:%s Transfer Successful!/n", file_name);     }   }   close(server_socket_fd);   return 0; } 

client端代码如下:

/*************************************************************************   > File Name: client.c   > Author: SongLee  ************************************************************************/ #include<sys/types.h> #include<sys/socket.h> #include<unistd.h> #include<netinet/in.h> #include<arpa/inet.h> #include<stdio.h> #include<stdlib.h> #include<errno.h> #include<netdb.h> #include<stdarg.h> #include<string.h>  #define SERVER_PORT 8000 #define BUFFER_SIZE 1024 #define FILE_NAME_MAX_SIZE 512  /* 包头 */ typedef struct  {   int id;   int buf_size; }PackInfo;  /* 接收包 */ struct RecvPack {   PackInfo head;   char buf[BUFFER_SIZE]; } data;   int main() {   int id = 1;    /* 服务端地址 */   struct sockaddr_in server_addr;   bzero(&server_addr, sizeof(server_addr));   server_addr.sin_family = AF_INET;   server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");   server_addr.sin_port = htons(SERVER_PORT);   socklen_t server_addr_length = sizeof(server_addr);    /* 创建socket */   int client_socket_fd = socket(AF_INET, SOCK_DGRAM, 0);   if(client_socket_fd < 0)   {     perror("Create Socket Failed:");     exit(1);   }    /* 输入文件名到缓冲区 */   char file_name[FILE_NAME_MAX_SIZE+1];   bzero(file_name, FILE_NAME_MAX_SIZE+1);   printf("Please Input File Name On Server: ");   scanf("%s", file_name);    char buffer[BUFFER_SIZE];   bzero(buffer, BUFFER_SIZE);   strncpy(buffer, file_name, strlen(file_name)>BUFFER_SIZE?BUFFER_SIZE:strlen(file_name));    /* 发送文件名 */   if(sendto(client_socket_fd, buffer, BUFFER_SIZE,0,(struct sockaddr*)&server_addr,server_addr_length) < 0)   {     perror("Send File Name Failed:");     exit(1);   }    /* 打开文件,准备写入 */   FILE *fp = fopen(file_name, "w");   if(NULL == fp)   {     printf("File:/t%s Can Not Open To Write/n", file_name);      exit(1);   }    /* 从服务器接收数据,并写入文件 */   int len = 0;   while(1)   {     PackInfo pack_info;      if((len = recvfrom(client_socket_fd, (char*)&data, sizeof(data), 0, (struct sockaddr*)&server_addr,&server_addr_length)) > 0)     {       if(data.head.id == id)       {         pack_info.id = data.head.id;         pack_info.buf_size = data.head.buf_size;         ++id;         /* 发送数据包确认信息 */         if(sendto(client_socket_fd, (char*)&pack_info, sizeof(pack_info), 0, (struct sockaddr*)&server_addr, server_addr_length) < 0)         {           printf("Send confirm information failed!");         }         /* 写入文件 */         if(fwrite(data.buf, sizeof(char), data.head.buf_size, fp) < data.head.buf_size)         {           printf("File:/t%s Write Failed/n", file_name);           break;         }       }       else if(data.head.id < id) /* 如果是重发的包 */       {         pack_info.id = data.head.id;         pack_info.buf_size = data.head.buf_size;         /* 重发数据包确认信息 */         if(sendto(client_socket_fd, (char*)&pack_info, sizeof(pack_info), 0, (struct sockaddr*)&server_addr, server_addr_length) < 0)         {           printf("Send confirm information failed!");         }       }       else       {        }     }     else     {       break;     }   }    printf("Receive File:/t%s From Server IP Successful!/n", file_name);   fclose(fp);   close(client_socket_fd);   return 0; }

感兴趣的朋友可以动手测试一下该程序,相信会对大家的Linux下C语言网络编程带来一定的帮助。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表

图片精选