Linux网络编程:UDP实现可靠的文件传输
我们知道,用TCP实现文件传输很简单。相对于TCP,因为UDP是面向无连接、不可靠的传输协议,所以我们需要考虑丢包和后发先至(包的顺序)的问题,所以我们想要实现UDP传输文件,则需要解决这两个问题。方法就是给数据包编号,按照包的顺序接收并存储,接收端接收到数据包后发送确认信息给发送端,发送端接收确认数据以后再继续发送下一个包,如果接收端收到的数据包的编号不是期望的编号,则要求发送端重新发送。
下面是我用linux C实现的一个Demo,定义一个包的结构体,其中包含数据和包头,包头里包含包的编号和数据大小,经过测试,传输一个视频文件,传送成功。
代码:
/*************************************************************************
> File Name: server.c
> Author: SongLee
> E-mail: lisong.shine@qq.com
> Created Time: 2014年05月11日 星期日 19时55分49秒
> Personal Blog: http://songlee24.github.io
************************************************************************/
#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;
}
/*************************************************************************
> File Name: client.c
> Author: SongLee
> E-mail: lisong.shine@qq.com
> Created Time: 2014年05月11日 星期日 21时53分39秒
> Personal Blog: http://songlee24.github.io
************************************************************************/
#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网络编程:UDP实现可靠的文件传输的更多相关文章
- 网络编程 - socket通信/粘包/文件传输/udp - 总结
socket通信 1.简单的套接字通信 import socket phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM) phone.bin ...
- Linux网络编程:UDP Socket编程范例
TCP协议提供的是一种可靠的,复杂的,面向连接的数据流(SOCK_STREAM)传输服务,它通过三段式握手过程建立连接.TCP有一种"重传确认"机制,即接收端收到数据后要发出一个肯 ...
- TCP/UDP Linux网络编程详解
本文主要记录TCP/UDP网络编程的基础知识,采用TCP/UDP实现宿主机和目标机之间的网络通信. 内容目录 1. 目标2.Linux网络编程基础2.1 嵌套字2.2 端口2.3 网络地址2.3.1 ...
- linux网络编程-(socket套接字编程UDP传输)
今天我们来介绍一下在linux网络环境下使用socket套接字实现两个进程下文件的上传,下载,和退出操作! 在socket套接字编程中,我们当然可以基于TCP的传输协议来进行传输,但是在文件的传输中, ...
- Linux网络编程10——使用UDP实现五子棋对战
思路 1. 通信 为了同步双方的棋盘,每当一方在棋盘上落子之后,都需要发送给对方一个msg消息,让对方知道落子位置.msg结构体如下: /* 用于发给对方的信息 */ typedef struct t ...
- linux网络编程_1
本文属于转载,稍有改动,以利于学习. (一)Linux网络编程--网络知识介绍 Linux网络编程--网络知识介绍客户端和服务端 网络程序和普通的程序有一个最大的区别是网络程序是由两个 ...
- Linux网络编程入门 (转载)
(一)Linux网络编程--网络知识介绍 Linux网络编程--网络知识介绍客户端和服务端 网络程序和普通的程序有一个最大的区别是网络程序是由两个部分组成的--客户端和服务器端. 客户 ...
- [转] - Linux网络编程 -- 网络知识介绍
(一)Linux网络编程--网络知识介绍 Linux网络编程--网络知识介绍客户端和服务端 网络程序和普通的程序有一个最大的区别是网络程序是由两个部分组成的--客户端和服务器端. 客户 ...
- Linux C 程序 Linux网络编程(21)
Linux网络编程网络编程必备的理论基础网络模型,地址,端口,TCP/IP协议 TCP/IP协议是目前世界上使用最广泛的网络通信协议日常中的大部分应用使用该系列协议(浏览网页,收发电子邮件,QQ聊天等 ...
随机推荐
- [ NOI 2001 ] 食物链
\(\\\) Description 有三类动物 \(A,B,C\),满足\(A\) 吃 \(B\),\(B\)吃 \(C\),\(C\) 吃 \(A\). 现有 \(N\) 个动物,以 \(1 - ...
- Django基础之数据库增删改查
Django中生成多个APP,每个APP下都有自己models模块,避免了多个APP之间数据的相互影响. 1.首先在APP的models下创建一个类 class UserInfo(models.Mod ...
- 重构27-Remove God Classes(去掉神类)
在传统的代码库中,我们常常会看到一些违反了SRP原则的类.这些类通常以Utils或Manager结尾,有时也没有这么明显的特征而仅仅是普通的包含多个功能的类.这种God类还有一个特征,使用语句或注释将 ...
- ZENCART 二级 分类 展开
zencart首页默认的是只显示一级分类,很多做仿牌外贸的朋友觉得只显示一级分类不好看,也不利于产品展示.怎么让zencart首页显示二级目录?下面分享给大家: 打开文件’includes/class ...
- GAN生成图像论文总结
GAN Theory Modifyingthe Optimization of GAN 题目 内容 GAN DCGAN WGAN Least-square GAN Loss Sensi ...
- 并发编程学习笔记(15)----Executor框架的使用
Executor执行已提交的 Runnable 任务的对象.此接口提供一种将任务提交与每个任务将如何运行的机制(包括线程使用的细节.调度等)分离开来的方法.通常使用 Executor 而不是显式地创建 ...
- Vue指令7:v-model
可以用 v-model 指令在表单控件元素上创建双向数据绑定. v-model 会忽略所有表单元素的 value.checked.selected 特性的初始值. 因为它会选择 Vue 实例数据来作为 ...
- vue组件---组件注册
(1)组件名 在注册一个组件的时候,我们始终需要给它一个名字.比如在全局注册的时候我们已经看到了: Vue.component('my-component-name', { /* ... */ }) ...
- [转载] Linux Futex的设计与实现
Linux Futex的设计与实现 引子 在编译2.6内核的时候,你会在编译选项中看到[*] Enable futex support这一项,上网查,有的资料会告诉你"不选这个内核不一定能正 ...
- BigDecimal运算
BigDecimal由任意精度整数未缩放值和32位整数级别组成 . 如果为零或正数,则刻度是小数点右侧的位数. 如果是负数,则数字的非标定值乘以10,以达到等级的否定的幂. 因此,BigDecimal ...