Linux下基于TCP的简易文件传输(socket编程)

  • OSI和TCP/IP:
  • 关于TCP/IP协议
  • 关于TCP协议
    • TCP编程的一般步骤[^2]
  • TCP文件传输实现
    • 功能概述
      • 服务器编程
      • 客户端编程
      • 运行结果
  • 总结
    • 遇到的问题
      • 服务器编程

OSI和TCP/IP:

OSI 模型本身不是网络体系结构的全部内容,它并未确切地描述用于各层的协议和服务,仅提出每一层应该做什么。不过OSI 已经为各层制定了标准,但并不是参考模型的一部分,而作为单独的国际标准公布的。
TCP/IP 是一组用于实现网络互连的通信协议。Internet 网络体系结构以TCP/IP 为核心。基于TCP/IP 的参考模型将协议分成四个层次,它们分别是:网络访问层、网际互联层、传输层(主机到主机)、和应用层。

关于TCP/IP协议

TCP/IP是一个网络通信模型,以及一整个网络传输协议家族,是网际网络的基础通信架构。因为该协议家族的两个核心协议:TCP(传输控制协议)和IP(网际协议),为该家族中最早通过的标准。故此它常被通称为TCP/IP协议族(TCP/IP Protocol Suite),简称TCP/IP。

. TCP/IP模型 :TCP/IP 是一组用于实现网络互连的通信协议。Internet 网络体系结构以 TCP/IP 为核心。基于TCP/IP 的参考模型将协议分成四个层次,它们分别是:网络访问层、网际互联层、传输层(主机到主机)、和应用层。

  1. 应用层
    应用层对应于 OSI 参考模型的高层,为用户提供所需要的各种服务,例如:FTP、Telnet、DNS、SMTP 等。
  2. 传输层
    传输层对应于 OSI 参考模型的传输层,为应用层实体提供端到端的通信功能,保证了数据包的顺序传送及数据的完整性。该层定义了两个主要的协议:传输控制协议(TCP)和用户数据协议(UDP)。TCP 协议提供的是一种可靠的、通过“三次握手”来连接的数据传输服务;而 UDP 协议提供的则是不保证可靠的(并不是不可靠)、无连接的数据传输服务.
  3. 网际互联层网际互联层对应于 OSI 参考模型的网络层,主要解决主机到主机的通信问题。它所包含的协议设计数据包在整个网络上的逻辑传输。注重重新赋予主机一个 IP 地址来完成对主机的寻址,它还负责数据包在多种网络中的路由。该层有三个主要协议:网际协议(IP)、互联网组管理协议(IGMP)和互联网控制报文协议(ICMP)。IP 协议是网际互联层最重要的协议,它提供的是一个可靠、无连接的数据报传递服务。
  4. 网络接口层(即主机-网络层)
    网络接入层与 OSI 参考模型中的物理层和数据链路层相对应。它负责监视数据在主机和网络之间的交换。事实上,TCP/IP 本身并未定义该层的协议,而由参与互连的各网络使用自己的物理层和数据链路层协议,然后与 TCP/IP 的网络接入层进行连接。地址解析协议(ARP)工作在此层,即 OSI 参考模型的数据链路层;

. IP协议 :IP(Internet Protocol)协议是 TCP/IP 的核心协议。IP 协议(Internet Protocol)又称互联网协议,是支持网间互连的数据报协议。它提供网间连接的完善功能, 包括 IP 数据报规定互连网络范围内的 IP 地址格式。
目前的 IP 地址(IPv4:IP 第 4 版本)由 32 个二进制位表示,每 8 位二进制数为一个整数,中间由小数点间隔,如 159.226.41.98,整个 IP 地址空间有 4 组 8 位二进制数,由表示主机所在的网络的地址以及主机在该网络中的标识共同组成,它通常被分为A,B,C,D,E五类,其中商业应用只用到A,B,C三类1

关于TCP协议

(1)TCP(传输控制协议)
TCP 是一种可靠的面向连接的传送服务。主机交换数据必须首先建立连接,传输完毕后断开连
接。它用位流通信,即数据被作为无结构的字节流。它提供反馈重发机制,从而保证数据的可靠传
输。
(2)三次握手
TCP 连接建立是一个三次握手过程,三次握手的目的是使数据的发送和接收同步。TCP 连接过程
如下图所示:
TCP 连接过程如下:

  1. 服务器必须准备好接受外来的连接。通过调用 socket, bind, listen 函数完成。称为被动打开。
  2. 客户通过调用 connect 进行主动打开。这引起客户 TCP 发送一个 SYN 分节,告诉服务器客户将在
    连接中发送的数据的初始序列号。
  3. 服务器必须确认客户的 SYN,同时自己也得发送一个 SYN 分节。服务器以单个分节向客户发送
    SYN 和对客户的 SYN 的 ACK。
  4. 客户必须确认服务器的 SYN。

TCP编程的一般步骤2

TCP编程的服务器端一般步骤是:
  1、创建一个socket,用函数socket();
  2、设置socket属性,用函数setsockopt(); * 可选
  3、绑定IP地址、端口等信息到socket上,用函数bind();
  4、开启监听,用函数listen();
  5、接收客户端上来的连接,用函数accept();
  6、收发数据,用函数send()和recv(),或者read()和write();
  7、关闭网络连接;
  8、关闭监听;

TCP编程的客户端一般步骤是:
  1、创建一个socket,用函数socket();
  2、设置socket属性,用函数setsockopt();* 可选
  3、绑定IP地址、端口等信息到socket上,用函数bind();* 可选
  4、设置要连接的对方的IP地址和端口等属性;
  5、连接服务器,用函数connect();
  6、收发数据,用函数send()和recv(),或者read()和write();
  7、关闭网络连接;

TCP文件传输实现

功能概述

利用socket编程基础实现一个基础的文件传输程序,可以在不同设备之间传输文件,视频,图片,音乐等内容。

服务器编程


#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/stat.h>#pragma comment (lib, "ws2_32.lib") //加载 ws2_32.dll
#define SERVER_PORT 8000 //监听本机8000端口
#define MAX 4096
#define BUF_SIZE 10240void empty_stdin() {int c;do {c = getchar();} while (c != '\n' && c != EOF);
}int main(void)
{struct sockaddr_in serveraddr,clientaddr;int sockfd,addrlen,confd,len;char ipstr[128];char buf[16];pid_t pid;//1.socketsockfd = socket(AF_INET,SOCK_STREAM,0);//2.bindbzero(&serveraddr,sizeof(serveraddr));//地址族协议ipv4serveraddr.sin_family = AF_INET;//ip地址serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);serveraddr.sin_port = htons(SERVER_PORT);bind(sockfd,(struct sockaddr *)&serveraddr,sizeof(serveraddr));//3.listenlisten(sockfd,20);//128作为可同时链接的数量上线//4. accept阻塞监听客户端的链接请求addrlen = sizeof(clientaddr);confd = accept(sockfd,(struct sockaddr *)&clientaddr,&addrlen);//如果有客户端连接上服务器,就输出客户端的ip地址和端口号printf("client ip %s\tport %d\n",inet_ntop(AF_INET,(struct sockaddr *)&clientaddr.sin_addr.s_addr,ipstr,sizeof(ipstr)),ntohs(clientaddr.sin_port));int flag=1;while(flag){char fdownload[100] = {0};recv(confd,fdownload,100,0);//接收客户端的下载请求获得文件名if(!strcmp(fdownload,"quit")) break;//客户端输入quit退出程序  else{FILE *fp = fopen(fdownload, "rb"); //以二进制方式打开文件if(fp == NULL){printf("Cannot open file, press any key to exit!\n");break;}struct stat statbuf; //这三行代码获得文件的大小,得到文件字节数stat(fdownload,&statbuf);int size=statbuf.st_size;char t[20];printf("%d \n",size);//文件大小sprintf(t,"%d",size);//转换整数到字符型数据//printf("start transfer\n");send(confd,t,20,0);//把文件大小发给客户端char buffer[BUF_SIZE] = {0}; //缓冲区long nCount,mc=0;long si,sj,m,c;recv(confd,t,4,0);//获得开始传输指令if(t[0]=='o'){printf("start transfer\n");
/*一下内容开始发送文件内容 ,因为文件大小会超出发送缓冲区大小,因此在这里循环调用send()函数进行发送,每一次发送nCount个字节,传输缓冲区这里设置为10240(并不合理,但不想花脑子想这个问题)*/while( (nCount = fread(buffer, 1, BUF_SIZE, fp)) > 0 ){mc=mc+nCount;si=mc*30/size;//这里开始计算传输比例,式字计算顺序不能更改,否则出现数据溢出,而产生错误。m=si-sj;printf("%*s|%d%%",30-si,"",(mc*100/size));//在固定位置打印百分数printf("\r\033["); //退格for(int t=0;t<si+1;t++){printf(">");setbuf(stdout, NULL);}send(confd, buffer, nCount, 0);sj=si;}printf("transfer success!\n");fclose(fp);}}}close(confd);//close(sockfd);return 0;
}

客户端编程

#include <netinet/in.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/stat.h>
#include <ctype.h>
#include <stdlib.h>#define HELLO_WORLD_SERVER_PORT       6666
#define BUFFER_SIZE                   1024
#define FILE_NAME_MAX_SIZE            512
#define BUF_SIZE 10240  #define SERVER_PORT 8000
#define MAXLINE 4096int main(void)
{struct sockaddr_in serveraddr;int confd,len;char ipstr[] = "192.168.1.100";//这是服务器的地址,使用ifconfig来查看char buf[MAXLINE];//1.创建一个socketconfd = socket(AF_INET,SOCK_STREAM,0);//2.初始化服务器地址,指明我要连接哪个服务器bzero(&serveraddr,sizeof(serveraddr));serveraddr.sin_family = AF_INET;inet_pton(AF_INET,ipstr,&serveraddr.sin_addr.s_addr);serveraddr.sin_port = htons(SERVER_PORT);//3.链接服务器connect(confd,(struct sockaddr *)&serveraddr,sizeof(serveraddr));int flag=1,size;char t[20];memset(&t,0,sizeof(t));while(flag){char fdownload[100] = {0};printf("Input filename to download: ");gets(fdownload);if(!strcmp(fdownload,"quit")) break;send(confd,fdownload,100,0);char filename[100] = {0}; //文件名len=recv(confd,t,20,0);size=atoi(t);double sizel;sizel=size/1048576.0;printf("filesize; %.2f MB\n ",sizel);printf("Input filename to save: ");gets(filename);if(!strcmp(filename,"quit")) break;else{FILE *fp = fopen(filename, "wb"); //以二进制方式打开(创建)文件if(fp == NULL){printf("Cannot open file, press any key to exit!\n");break;}send(confd,"o",4,0);char buffer[BUF_SIZE] = {0}; //文件缓冲区long nCount,mc=0;long si,sj,m,c;printf("Start receive!\n");while( (nCount = recv(confd, buffer, BUF_SIZE, 0)) > 0 ){mc=mc+nCount;si=mc*30/size;m=si-sj;printf("%*s|%d%%",30-si,"",(mc*100/size));printf("\r\033["); //退格for(int t=0;t<si+1;t++){printf(">");setbuf(stdout, NULL);}fwrite(buffer, nCount, 1, fp);if(mc==size) break;}printf("Transfer success!\n");fclose(fp);}}
close(confd);
return 0;
}

运行结果


手机客户端传输截图,手机端安装"手机CAPP"app即可运行客户端程序:

总结

遇到的问题

服务器编程

  1. 进度条不在传输过程中打印:在客户端发送数据时进度条不打印,只在传输结束以后才打印一串进度条符号
    原因:C语言的标准输入输出函数都具有一个输入输出缓冲区,一般来说,只有在遇到“\n”或者缓冲区满的条件下才输出缓冲区的数据,在打印进度条的时候没有进行回车,所以再这一过程printf是不会输出数据的,只有在结束以后遇到回车才输出
    解决:把输入缓冲区大小设置为零,这么一来可以加快打印效率。如果在windows下面可以使用fflush来清空缓冲区使得数据及时输出。

  1. 关于IP地址的更多信息 ↩︎

  2. 这部分来自这位博主的博客 ↩︎

Linux下基于TCP的简易文件传输(socket编程)相关推荐

  1. linux tcp文件分包_在Linux下基于TCP协议的文件传输程序.

    [设计目的] 通过 Linux C 编程,设计一个基于 TCP/IP 的文件传输系统,实现网络文件的收发 [设计环境] Ubuntu 12.04 [设计方案] ( 1 )文件读写 任意文件都可以二进制 ...

  2. linux端口扫描nc,Linux下nc命来实现文件传输、端口扫描

    今天在饮水思源上闲逛,看到了一个贴子关于Linux下nc命来实现文件传输,进行学习了解了一下. 发送端: cat test.txt | nc -l -p 6666 或者nc -l  -p 6666 & ...

  3. linux nc 传送文件,Linux下nc命来实现文件传输

    发送端: cat test.txt | nc -l -p 6666 或者nc -l -p 6666 < test.txt 有些版本不要在 -p [监听6666端口,等待连接](设发送端IP为10 ...

  4. 利用tcp完成文件传输linux,Linux下基于TCP的文件传输

    服务器: #include #include #include #include #include #include #include #define SERVER_PORT 6666 #define ...

  5. 基于TCP的大文件传输c语言项目

    文章目录 前言:功能实现 tcp文件传输的基本过程: 1.用户登录 1.1创建数据库 2.文件普通下载和上传的实现: 2.1 普通下载 2.2 普通上传 2.3 文件秒上传的实现 2.断点下载和断点上 ...

  6. Qt网络编程小项目-基于Tcp协议的文件传输项目

    目录 项目描述 Qt下Tcp服务器端和客户端流程: 具体流程: 客户端: 服务器端: 源码: 服务器端: 服务器头文件: 服务器源文件: 服务器端ui 客户端: 客户端头文件: 客户端源文件: 客户端 ...

  7. java socket 通信协议_java网络通信(基于TCP协议可靠通信的socket编程)

    package cn.wang; import java.io.*; import java.net.*; public class Server { static int num = 1;//客户端 ...

  8. Linux系统下实现基于TCP/IP协议的简单Socket通信

    Linux系统下实现基于TCP/IP协议的简单Socket通信 网络套接字Socket Socket概念 主机字节序和网络字节序 Sockaddr地址结构 Socket实现客户端.服务端通信 服务端程 ...

  9. linux 监控微信通知,一个 Linux 下基于 Bash 的文件和数据库监控及备份工具,可发送微信报警通知...

    shellMonitor 一个 Linux 下基于 bash 的文件和数据库监控及备份工具. 写这个工具的原因,在于一个朋友的一个小电商网站,因为未明原因被黑了,总是会被增加超级管理员,并将收款账号改 ...

最新文章

  1. linux防火墙添加端口并开闭防火墙
  2. mysql中shift h_MySQL复制技术对比与容器化探究
  3. 深度学习目标检测系列:faster RCNN实现|附python源码
  4. MySQL高级 —— 查询性能优化
  5. Ionic--再次打开自动填充用户名和密码的问题解决方法
  6. 利用CSS使元素在水平方向或水平,竖直同时居中
  7. HashMap在Jdk1.7和1.8中的实现
  8. 杨建:网站加速--系统架构篇
  9. armbian格式化磁盘命令,甜糖格式化磁盘
  10. 译文 [ROM][多国语言][2015.06.11] Lenovo S750 (MTK6589) - andrea_d86-lenovos750-4.2.2
  11. IDEA启动报错:Error launching IDEA if you already have a 64-bit JDK installed,define a JAVA_HOME
  12. 看相识人的顶级学问--《冰鉴》
  13. PQ分区出错用Ghost来进行补救
  14. IDEA配置文件保存位置修改
  15. 历年软考网络规划师考点总结
  16. 小瓜讲matplotlib高级篇——坐标轴设置(坐标轴居中、坐标轴箭头、刻度设置、标识设置)
  17. Rancher hosted Kubernetes AKS
  18. 【160312 18:00】四则运算 2
  19. 随笔---XXXIII
  20. 电视剧神话剧情介绍-电视剧神话剧情简介

热门文章

  1. R语言:读取中文数据乱码的解决方案
  2. SpringBoot+Vue
  3. 21世纪非常成功心法(一)
  4. [理论] GUI原理1 - 色彩王国
  5. html泳道连线图插件,mxGraph之在流程图上显示图片及泳道
  6. 线性回归理论说明及公式推导
  7. RK3588S Android预置apk
  8. Java基础一到五章复习笔记
  9. spring 使用注解来定义联合主键
  10. 关于Clang的编译使用