题目要求
为巩固提高网络编程能力,加深对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;
}