TCP、UDP文件传输分析

题目要求

为巩固提高网络编程能力,加深对socket套接字的使用,并进一步的理解UDP和TCP之间的不同,该题目主要需完成以下两个目标:

  • 实现跨物理主机的文件传输系统。

    • 分别使用TCP和UDP协议,利用socket库,完成一个文件传输程序。
    • 文本文件能够跨物理主机在局域网内进行传输。
    • 语言不限(python,java,C,C++,C#)。
  • 分析文件传输的时延。

    • 分析在UDP协议下,文件传输系统在传输不同大小的文件时的传输时延。
    • 分析在TCP协议下,文件传输系统在传输不同大小的文件时的传输时延。
    • 文件大小分别为100M,200M,300M,400M,500M,600M,700M,800M,900M,1G。
    • 将上述分析结果制作成折线图,并对折线图趋势进行分析。

 

跨物理主机的文件传输

局域网上服务器端等待客户端连接(TCP):

检测到192.168.43.252连接成功,做出消息相应,客户端传输文件text.txt,在服务器端接收到文件并且做出消息相应。

 

在当前项目文件夹查看是否收到了文件:

接收成功。

UDP协议同理。

 

分析文件传输的时延

首先,准备好100-1G大小的文件:

 

在python中写一个简单的生成随机文件大小的程序。

import os
import time

def GetUserName():
    nameList = os.popen('echo %username%').readlines()
    username = nameList[0].replace("\n","")

    return username

def getNowDataTime(flag = 0):
    now = time.localtime(time.time())
    if flag == 0:
        return time.strftime('%Y-%m-%d %H:%M:%S',now)

def GenerateTXTFile():
    while True:
        size = input('please enter the size that you want to create(MB):')
        if size.strip().isdigit() != True:
            print('please enter a integer!')
            continue
        else:
            fileSize = int(size)
            break
    if fileSize >= 100:
        print('creating TXT file,please waiting...')
    filename = '_'+size +'MB.txt'
    print(f'filename:{filename}')
    filepath ='E:\\ '
    f = open( 'E:\\'+  filename,'w')
    starttime = getNowDataTime()
    startclock = time.clock()
    for i in range(fileSize):
        if i>=100:
            if i%100 == 0:
                print(f'created{i//100*100}MB data.')
        for j in range(1024):
            try:
                f.write('01'*512)
            except KeyboardInterrupt:
                print('\n keyboardInterupt!')
                f.close()
                exit(-1)
    f.close()
    print(f'file has been created at '+filepath+ '!'+'size:{fileSize}MB.\n')
    print(f'DetailInfo:')
    print(f'save path:{filepath+filename}')
    print(f'start time:{starttime}')
    print(f'endtime:{getNowDataTime()}')
    print(f'use time:{(time.clock() - startclock):<.3}sec.')

if __name__ == '__main__':
    GenerateTXTFile()

运行结果如下:

 

100M-1G文件在局域网上两台主机进行传输,并且得到传输时延显示出来:

得到的表格如下:

最终得到TCP和UDP各个文件大小下的传输时延的折线图为:
 

可以发现TCP协议下,文件传输-时延与文件大小基本稳正线性相关,在文件传输过程中查看网络速度大概为2.2M/S。

在UDP协议下,传输时延与文件大小线性相关性差,波动较大,传输时延相较TCP有明显的降低,文件传输过程中网络速度大概4.4M/S。

综上,可以得出以下结论:

    1、 在连接上,TCP是基于连接的,是面向连接的运输层协议,如打电话之前要拨号建立连接,UDP是无连接的,即发送数据之前不需要建立连接。

    2、 在对于系统资源的要求上,TCP较多,UDP较少。

    3、  结构程序方面,TCP的结构较为复杂,而UDP结构较为简单。

    4、 在模式上TCP为流模式,而UDP则是数据报模式。

    5、TCP能保证数据的正确性和顺序性,而UDP可能丢失且不能保证数据的顺序不改变。

     所以,TCP得出的折线图为牺牲时间为代价来提高文件传输的准确度,而UDP为无连接连输,折线图波动大,文件越大不一定时间越长,因为在传输过程中可能丢包,表面上时间变短了。

使用TCP协议传输(代码):

1. 文件操作及相关类:

使用TCP协议传输(代码):
1. 文件操作及相关类:
 #include<stdio.h>
 #include<stdlib.h>
 #include <WINSOCK2.H>
 #include <STDIO.H>
 #pragma  comment(lib,"ws2_32.lib")

class FileHelper
{
    private:
    FILE *f;
    char path_buffer[_MAX_PATH];
    char drive[_MAX_DRIVE];
    char dir[_MAX_DIR];
    char fname[_MAX_FNAME];
    char ext[_MAX_EXT];

public:
    FILE * selectfile()
    {
        printf("请输入要传送的文件名\n");
        scanf("%s",path_buffer);

        if (f=fopen(path_buffer,"rb"))
        {
            printf("文件打开成功\n");
            return f;
        }
        else
        {
            printf("文件不存在,请重新输入\n");
            return selectfile();
        }
    }

char * getFileName()
{
    _splitpath(path_buffer, drive, dir, fname, ext);
    return strcat(fname, ext);
}
FILE * createFile(char *name)
{
    remove(name);
    if (f = fopen(name, "ab"))
    {
        printf("文件创建成功\n");

    }
    else
    {
        printf("文件创建失败\n");
    }
    return f;
}

bool createDir(char *dir)
{
    char head[MAX_PATH] = "md ";
    return system(strcat(head, dir));
} 
};

2. 客户端

#include <stdio.h>
#include <WINSOCK2.H>
#include <windows.h>
#include <STDIO.H>
#include"FileHelper.h"
#pragma comment(lib,"ws2_32.lib")
int main(int argc, char* argv[])
{
WORD sockVersion = MAKEWORD(2,2);//版本号
WSADATA data; //⽤来保存WSAStartup调⽤后返回的windows
    FileHelper fh;
if(WSAStartup(sockVersion, &data) != 0)
{
    return 0;
}
sockaddr_in serAddr;
serAddr.sin_family = AF_INET;
serAddr.sin_port = htons(8888);
serAddr.sin_addr.S_un.S_addr =
    inet_addr("169.254.24.144");
while (true)
{
    SOCKET sclient = socket(AF_INET, SOCK_STREAM,
        IPPROTO_TCP);
    if (sclient == INVALID_SOCKET)
    {
        printf("invalid socket !");
        return 0;
    }
    if (connect(sclient, (sockaddr *)&serAddr,
        sizeof(serAddr)) == SOCKET_ERROR)
    {
        printf("connect error !");
        closesocket(sclient);
        return 0;
    }
    FILE *f=fh.selectfile();
    char sendData[BUFSIZ];
    char recData[BUFSIZ];
    char over[BUFSIZ] = "Finnal";
    char * name = fh.getFileName();
    strcpy(sendData, name);
    printf("%s\n", sendData);
    int nCount;
    long long sum = 0;
    send(sclient, sendData, strlen(sendData)+1, 0);
    double start = GetTickCount();
    int ret = recv(sclient, recData, BUFSIZ, 0);
    printf(recData);
    while ((nCount=fread(sendData,1,BUFSIZ,f))>0)
    {
        printf("%db\n",sum+=nCount);
        send(sclient, sendData, nCount, 0);
        int ret = recv(sclient, recData, BUFSIZ, 0);
        if (ret >0)
        {
            //recData[ret] = 0x00;
            printf(recData);
        }
        else
        {
            printf("与服务器失去连接");
            break;
        }
    }
    send(sclient, over, BUFSIZ, 0);
    ret = recv(sclient, recData, BUFSIZ, 0);
    if (ret>0&&strcmp(recData,over)==0)
    {
        printf("传输成功!");
    }
    double  end=GetTickCount();
    printf("传输时间为:%f ms",end-start);
    fclose(f);
    closesocket(sclient);
}
WSACleanup();
return 0;
}

3. 服务端

#include <stdio.h>
#include <stdio.h>
#include <winsock2.h>
#include "FileHelper.h"
#pragma comment(lib,"ws2_32.lib")
int main(int argc, char* argv[])
{
//初始化WSA
WORD sockVersion = MAKEWORD(2, 2);
WSADATA wsaData;
FileHelper fh;
if (WSAStartup(sockVersion, &wsaData) != 0)
{
    return 0;
}
//创建套接字
SOCKET slisten = socket(AF_INET, SOCK_STREAM,
    IPPROTO_TCP);
if (slisten == INVALID_SOCKET)
{
    printf("socket error !");
    return 0;
}
//绑定IP和端⼝
sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(8888);
sin.sin_addr.S_un.S_addr = INADDR_ANY;
if (bind(slisten, (LPSOCKADDR)&sin, sizeof(sin)) ==
    SOCKET_ERROR)
{
    printf("bind error !");
}
//开始监听
if (listen(slisten, 5) == SOCKET_ERROR)
{
    printf("listen error !");
    return 0;
}
//循环接收数据
SOCKET sClient;
sockaddr_in remoteAddr;
int nAddrlen = sizeof(remoteAddr);
char revData[BUFSIZ];
while (true)
{
    printf("等待连接...\n");
    sClient = accept(slisten, (SOCKADDR *)&remoteAddr,
        &nAddrlen);
    if (sClient == INVALID_SOCKET)
    {
        printf("accept error !");
        continue;
    }
    printf("接受到⼀个连接:%s \r\n",
        inet_ntoa(remoteAddr.sin_addr));
    if(fh.createDir(inet_ntoa(remoteAddr.sin_addr)))
    printf("⽂件夹创建成功!");
    int ret = 0;
    long long count = 0;
    char sendData[BUFSIZ] = "你好,TCP客户端!\n";
    ret = recv(sClient, revData, BUFSIZ, 0);
    char fromname[BUFSIZ] = {};
    strcpy(fromname, revData);
    char mid[3] = "\\";
    char finame[MAX_PATH] = {};
    char over[BUFSIZ] = "Finnal";
    strcat(finame, inet_ntoa(remoteAddr.sin_addr));
    printf(finame);
    strcat(finame, mid);
    strcat(finame, revData);
    //printf(finame);
    FILE *f = fh.createFile(finame);
    send(sClient, sendData, BUFSIZ, 0);
    while ((ret = recv(sClient, revData, BUFSIZ, 0)) >
        0)
    {
        //printf("%d\n", ret);
        printf("%db\n", count += ret);
        if (strcmp(revData,over)==0)
        {
            printf("⽂件%s传输成功\n", fromname);
            break;
            send(sClient, over, BUFSIZ, 0);
        }
        fwrite(revData, 1, ret, f);
        send(sClient, sendData, BUFSIZ, 0);
    }
    fclose(f);
    if (strcmp(revData, over) != 0)
    {
        printf("IP:%s发来的%s传输过程中失去连接\n",
            inet_ntoa(remoteAddr.sin_addr),fromname);
        remove(finame);
    }
    closesocket(sClient);
}
closesocket(slisten);
WSACleanup();
return 0;
}

使用UDP协议传输:

1. 文件操作及相关类:

 #include<stdio.h>
 #include<stdlib.h>
 #include <WINSOCK2.H>
 #include <STDIO.H>
 #pragma  comment(lib,"ws2_32.lib")

class FileHelper
{
    private:
    FILE *f;
    char path_buffer[_MAX_PATH];
    char drive[_MAX_DRIVE];
    char dir[_MAX_DIR];
    char fname[_MAX_FNAME];
    char ext[_MAX_EXT];

public:
    FILE * selectfile()
    {
        printf("请输入要传送的文件名\n");
        scanf("%s",path_buffer);

        if (f=fopen(path_buffer,"rb"))
        {
            printf("文件打开成功\n");
            return f;
        }
        else
        {
            printf("文件不存在,请重新输入\n");
            return selectfile();
        }
    }

char * getFileName()
{
    _splitpath(path_buffer, drive, dir, fname, ext);
    return strcat(fname, ext);
}
FILE * createFile(char *name)
{
    remove(name);
    if (f = fopen(name, "ab"))
    {
        printf("文件创建成功\n");

    }
    else
    {
        printf("文件创建失败\n");
    }
    return f;
}

bool createDir(char *dir)
{
    char head[MAX_PATH] = "md ";
    return system(strcat(head, dir));
} 
};

2. 客户端

#pragma comment(lib,"Ws2_32.lib")
#include<WinSock2.h>
#include<WS2tcpip.h>
#include<stdio.h>
#include<Windows.h>
#include"FileHelper.h"

int main()
{

WORD wVersionRequested;
WSADATA wsaData;
char sendData[BUFSIZ]="ÄãºÃ£¡\n";
char beginData[BUFSIZ]="Begin\n";
char overData[BUFSIZ]="Over\n";
char Filename[BUFSIZ]={};
FileHelper fh;
int err;
wVersionRequested = MAKEWORD(2, 2);

err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0) {
    //Tell the user that we could not find a usable 
    //Winsock DLL.                                 
    printf("WSAStartup failed with error: %d\n", err);
    return 1;
}

if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
    // Tell the user that we could not find a usable 
    // WinSock DLL.                                  
    printf("Could not find a usable version of Winsock.dll\n");
    WSACleanup();
    return 1;
}

SOCKADDR_IN addrServ;
addrServ.sin_addr.S_un.S_addr=inet_addr("169.254.24.144");
addrServ.sin_family=AF_INET;
addrServ.sin_port=htons(4999);
while (true)
{
    SOCKET socketClient=socket(AF_INET,SOCK_DGRAM,0);
    FILE *f=fh.selectfile();
    sendto(socketClient,beginData,BUFSIZ,0,(SOCKADDR*)&addrServ,sizeof(SOCKADDR));
    strcpy(Filename,fh.getFileName());
    sendto(socketClient,Filename,BUFSIZ,0,(SOCKADDR*)&addrServ,sizeof(SOCKADDR));
    int count=0;
    int sum=0;
    double start = GetTickCount();
    while ((count=fread(sendData,1,BUFSIZ,f))>0)
    {
        //Sleep(1);
        printf("%d\n",sum+=count);
        sendto(socketClient,sendData,BUFSIZ,0,(SOCKADDR*)&addrServ,sizeof(SOCKADDR));
    }
    sendto(socketClient,overData,BUFSIZ,0,(SOCKADDR*)&addrServ,sizeof(SOCKADDR));
    double end = GetTickCount();
    printf("传输时延为:%f ms",end-start);
    closesocket(socketClient);
}
WSACleanup();
return 0;

}

3. 服务端

#pragma comment(lib,"Ws2_32.lib")
#include<WinSock2.h>
#include<WS2tcpip.h>
#include<stdio.h>
#include<Windows.h>
#include "FileHelper.h"

int main()
{
WORD wVersionRequested;
WSADATA wsaData;

FileHelper fh;
int err;
wVersionRequested=MAKEWORD(2,2);
err=WSAStartup(wVersionRequested,&wsaData);
if (err!=0)
{
    printf("WSAStartup failed with error:%d\n",err);
    return -1;
}
if (LOBYTE(wsaData.wVersion)!=2||HIBYTE(wsaData.wVersion)!=2)
{
    printf("Could not find a usable version of Winsock.dll\n");
    WSACleanup();
    return -1;
}
SOCKET socketServer=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
SOCKADDR_IN addrServ;
addrServ.sin_addr.S_un.S_addr=htonl(INADDR_ANY);//指定0.0.0.0地址,表示任意地址
addrServ.sin_family=AF_INET;//表示IPv4的套接字类型
addrServ.sin_port=htons(4999);
bind(socketServer,(SOCKADDR*)&addrServ,sizeof(SOCKADDR));
SOCKADDR_IN addrClient;
int length=sizeof(SOCKADDR);
char recvBuf[BUFSIZ]={};
int rev=0;
while (true)
{
    DWORD TIME_OUT=10;
    char sendData[BUFSIZ]="你好!\n";
    char beginData[BUFSIZ]="Begin\n";
    char overData[BUFSIZ]="Over\n";
    char Filename[BUFSIZ]={};
    char ClientAddr[BUFSIZ]={};
    char FromName[BUFSIZ]={};
    FILE *f=NULL;
    if(err=setsockopt(socketServer,SOL_SOCKET,SO_SNDTIMEO,(char *)&TIME_OUT,sizeof(TIME_OUT)))
    {
        printf("失败!\n");
    };
    printf("%d\n",err);
    recvfrom(socketServer,recvBuf,BUFSIZ,0,(SOCKADDR*)&addrClient,&length);
    if (strcmp(recvBuf,beginData)==0)
    {

        recvfrom(socketServer,recvBuf,BUFSIZ,0,(SOCKADDR*)&addrClient,&length);
        strcpy(ClientAddr,inet_ntoa(addrClient.sin_addr));
        strcpy(FromName,recvBuf);
        fh.createDir(ClientAddr);
        strcpy(Filename,ClientAddr);
        strcat(Filename,"\\");
        strcat(Filename,recvBuf);
        f=fh.createFile(Filename);

    }
    int sum=0;
    while((rev=recvfrom(socketServer,recvBuf,BUFSIZ,0,(SOCKADDR*)&addrClient,&length))>0)
    {
        if (strcmp(overData,recvBuf)==0)
        {
            printf("文件%s传输成功!\n",FromName);
            fclose(f);
            break;
        }
        //  printf(recvBuf);
        fwrite(recvBuf,1,rev,f);
        printf("%db\n",sum+=rev);
    }

    if (rev<0||strcmp(overData,recvBuf)!=0)
    {
        printf("IP:%s发来的%s传输过程中失去连接\n",addrClient,FromName);
        fclose(f);
        remove(Filename);
    }

}

closesocket(socketServer);
WSACleanup();
return 0;

}

 

  • 3
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
TCPUDP是两种常用的传输层协议,用于在网络中传输数据。它们有一些区别和特点。 1. TCP协议: TCP(传输控制协议)是一种面向连接的协议,提供可靠的数据传输。它通过三次握手建立连接,确保数据的可靠性和顺序性。TCP使用滑动窗口和确认机制来实现可靠传输,并使用流量控制和拥塞控制来调整数据传输速率TCP适用于需要可靠传输的应用,如文件传输、电子邮件和网页浏览。 2. UDP协议: UDP(用户数据报协议)是一种无连接的协议,提供不可靠的数据传输。它不需要建立连接,直接将数据包发送给目标主机。UDP不提供可靠性和顺序性,也不进行拥塞控制。UDP适用于实时应用,如音频和视频流传输,以及需要较低延迟的应用。 以下是TCPUDP协议的一些特点和示例: . TCP协议特点: - 面向连接:通过三次握手建立连接,确保可靠传输。 - 可靠性:使用滑动窗口和确认机制,确保数据的可靠性和顺序性。 - 流量控制:通过滑动窗口调整数据传输速率,防止数据丢失和拥塞。 - 拥塞控制:根据网络状况调整数据传输速率,避免网络拥塞。 - 适用于需要可靠传输的应用,如文件传输、电子邮件和网页浏览。 2. UDP协议特点: - 无连接:直接将数据包发送给目标主机,不需要建立连接。- 不可靠性:不提供可靠性和顺序性,数据包可能丢失或乱序。 - 低延迟:不进行拥塞控制,适用于实时应用和需要较低延迟的应用。 - 适用于实时应用,如音频和视频流传输。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值