文档库 最新最全的文档下载
当前位置:文档库 › 设计文档及使用说明

设计文档及使用说明

设计文档及使用说明
设计文档及使用说明

大作业:网络Socket编程

程序员:熊宇龙200630501407 信息安全5班

本程序实现了多客户端访问和一定大小内的文件传输(已测试bmp和txt文件),并且客户端在连接时就将得到服务器端的文件列表。

为方便解说起见,我将研发说明文档和操作说明集中在这里一起了

声明:部分源码来自参考书《WINDOWS网络编程》中的TCP连接部分,本程序也是基于其源码上进行了大幅度修改完成的。

大部分研发说明都在程序内写出来了。我特地用红字表示出来。其中主要是表示的拓展的地方、需要注意的地方,以及核心的地方。

其他说明:由于时间不够的缘故,本程序旨在实现其传输文件的功能、列表发送以及多客户端多线程传输的功能。并没有对其人性化进行太多设计,程序员可以按照我在程序中的提示通过直接修改程序达到其他目的(如扩大传输量),主要可修改的包括:

1.文件列表,保存在1.txt中可以任意修改,不过请不要对客户端造成欺骗。

2.Buff容量,通过修改DEFAULT_BUFFER 达到目的。

拓展构想:计划内却因为时间关系未完成的的部分包括

1.实现任意大小文件传输:可以通过if语句判定大小,产生多个buff文件顺序传输并顺序

写入。不难但是时间不够了。

2.实现断点续传:判定部分遇到了困难,未完成。

具体操作流程图请见最后

程序如下:

客户端:

// Client.c

// 声明:部分代码来自《WINDOWS网络编程》一书中关于建立TCP连接的部分

// 功能:连接到服务器端并下载文件

// 注意:build时请在工程设置中添加ws2_32.lib

//

// Command Line Options:

// client [-p:x] [-s:IP] [-n:x] [-o]

// -p:x Remote port to send to

// -s:IP Server's IP address or hostname

// -n:x Number of times to send message

// -o Send messages only; don't receive

//

#include

#include

#include

#include

#define DEFAULT_COUNT 20

#define DEFAULT_PORT 5150

#define DEFAULT_BUFFER 4096//希望调整最大可传输文件大小请调整此处不要忘了将server的也同样更改#define DEFAULT_MESSAGE "This is a test of the emergency \

broadcasting system"

char szServer[128],

szMessage[1024];

int iPort = DEFAULT_PORT;

DWORD dwCount = DEFAULT_COUNT;

BOOL bSendOnly = FALSE;

void usage()

{

printf("usage: client [-p:x] [-s:IP] [-n:x] [-o]\n\n");

printf(" -p:x Remote port to send to\n");

printf(" -s:IP Server's IP address or hostname\n");

printf(" -n:x Number of times to send message\n");

printf(" -o Send messages only; don't receive\n");

ExitProcess(1);

}

void V alidateArgs(int argc, char **argv)

{

int i;

for(i = 1; i < argc; i++)

{

if ((argv[i][0] == '-') || (argv[i][0] == '/'))

{

switch (tolower(argv[i][1]))

{

case 'p':

if (strlen(argv[i]) > 3)

iPort = atoi(&argv[i][3]);

break;

case 's':

if (strlen(argv[i]) > 3)

strcpy(szServer, &argv[i][3]);

break;

case 'n':

if (strlen(argv[i]) > 3)

dwCount = atol(&argv[i][3]);

break;

case 'o':

bSendOnly = TRUE;

break;

default:

usage();

break;

}

}

}

}

int main(int argc, char **argv)

{

WSADATA wsd;

SOCKET sClient;

char szBuffer[DEFAULT_BUFFER];

int ret;

struct sockaddr_in server;

struct hostent *host = NULL;

cout<<"input szServer"<>szServer;//如果在本机测试,可使用127.0.0.1本机循环地址作为这个数值

ValidateArgs(argc, argv);

if (WSAStartup(MAKEWORD(2,2), &wsd) != 0)

{

printf("Failed to load Winsock library!\n");

return 1;

}

strcpy(szMessage, DEFAULT_MESSAGE);

sClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

if (sClient == INV ALID_SOCKET)

{

printf("socket() failed: %d\n", WSAGetLastError());

return 1;

}

server.sin_family = AF_INET;

server.sin_port = htons(iPort);

server.sin_addr.s_addr = inet_addr(szServer);//连接到目标地址

if (server.sin_addr.s_addr == INADDR_NONE)

{

host = gethostbyname(szServer);

if (host == NULL)

{

printf("Unable to resolve server: %s\n", szServer);

return 1;

}

CopyMemory(&server.sin_addr, host->h_addr_list[0],

host->h_length);

}

if (connect(sClient, (struct sockaddr *)&server,

sizeof(server)) == SOCKET_ERROR)

{

printf("connect() failed: %d\n", WSAGetLastError());

return 1;

}

ret = recv(sClient, szBuffer, DEFAULT_BUFFER, 0);//等待接收文件列表

if (ret == SOCKET_ERROR)

{

printf("recv() failed: %d\n", WSAGetLastError());

}

szBuffer[ret] = '\0';

printf("FileList:\n");

printf("%s\n", szBuffer);

FILE *fp;

while(1)

{

cout<<"input szMessage"<>szMessage;

ret = send(sClient, szMessage, strlen(szMessage), 0);

if (ret == 0)

{

break;

}

else if (ret == SOCKET_ERROR)

{

printf("send() failed: %d\n", WSAGetLastError());

break;

}

printf("Send %d bytes\n", ret);

if (!bSendOnly)

{

ret = recv(sClient, szBuffer, DEFAULT_BUFFER, 0);//接收文件的szBuff并开始准备转换回文件

if (ret == 0)

break;

else if (ret == SOCKET_ERROR)

{

printf("recv() failed: %d\n", WSAGetLastError());

break;

}

szBuffer[ret] = '\0';

printf("RECV [%d bytes]: '%s'\n", ret, szBuffer);

char s[10]="RECV"; //对接受的文件进行重命名在前面加上RECV 后几句都是此作用

strncat(s,szMessage,strlen(szMessage));

fp=fopen(s,"a+");

fwrite(szBuffer,1,ret,fp);

fclose(fp);

}

}

closesocket(sClient);

WSACleanup();

return 0;

}

服务器端:

// Server.c

// 声明:部分代码来自《WINDOWS网络编程》一书中关于建立TCP连接的部分

// 功能:建立一个全自动服务器,当收到连接申请则连接并发送文件列表,并等待客户端发送申请下载请求

// 注意:build时请在工程设置中加入ws2_32.lib

#include

#include

#include

#define DEFAULT_PORT 5150

#define DEFAULT_BUFFER 4096

int iPort = DEFAULT_PORT;

BOOL bInterface = FALSE,

bRecvOnly = FALSE;

char szAddress[128];

//

// Function: usage

//

// Description:

// Print usage information and exit

//

void usage()

{

printf("usage: server [-p:x] [-i:IP] [-o]\n\n");

printf(" -p:x Port number to listen on\n");

printf(" -i:str Interface to listen on\n");

printf(" -o Don't echo the data back\n\n");

ExitProcess(1);

}

void V alidateArgs(int argc, char **argv)

{

int i;

for(i = 1; i < argc; i++)

{

if ((argv[i][0] == '-') || (argv[i][0] == '/'))

{

switch (tolower(argv[i][1]))

{

case 'p':

iPort = atoi(&argv[i][3]);

break;

case 'i':

bInterface = TRUE;

if (strlen(argv[i]) > 3)

strcpy(szAddress, &argv[i][3]);

break;

case 'o':

bRecvOnly = TRUE;

break;

default:

usage();

break;

}

}

}

}

DWORD WINAPI ClientThread(LPVOID lpParam)//多线程控制通过给不同的客户不同的线

{

SOCKET sock=(SOCKET)lpParam;

char szBuff[DEFAULT_BUFFER];

int ret,

nLeft,

idx;

FILE *fp;

while(1)

{

ret = recv(sock, szBuff, DEFAULT_BUFFER, 0);//收听客户端想下载什么文件

if (ret == 0)

break;

else if (ret == SOCKET_ERROR)

{

printf("recv() failed: %d\n", WSAGetLastError());

break;

}

szBuff[ret] = '\0';

printf("RECV: '%s'\n", szBuff);

fp=fopen(szBuff,"rb");//打开目标文件将目标文件内容存入szBuff

memset(szBuff,0,DEFAULT_BUFFER);//初始化szBuff 不然传送的数据后面会自动添加很多东西

ret=fread(szBuff,1,DEFAULT_BUFFER-1,fp);//将目标文件中的东西读入szBuff

send(sock,szBuff,ret,0);//发送给客户端

fclose(fp);

}

return 0;

}

int main(int argc, char **argv)

{

WSADATA wsd;

SOCKET sListen,

sClient;

int iAddrSize;

HANDLE hThread;

DWORD dwThreadId;

struct sockaddr_in local,

client;

ValidateArgs(argc, argv);

if (WSAStartup(MAKEWORD(2,2), &wsd) != 0)

{

printf("Failed to load Winsock!\n");

return 1;

}

sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); if (sListen == SOCKET_ERROR)

{

printf("socket() failed: %d\n", WSAGetLastError());

return 1;

}

if (bInterface)

{

local.sin_addr.s_addr = inet_addr(szAddress);

if (local.sin_addr.s_addr == INADDR_NONE)

usage();

}

else

local.sin_addr.s_addr = htonl(INADDR_ANY);

local.sin_family = AF_INET;

local.sin_port = htons(iPort);

if (bind(sListen, (struct sockaddr *)&local,

sizeof(local)) == SOCKET_ERROR)

{

printf("bind() failed: %d\n", WSAGetLastError());

return 1;

}

listen(sListen, 8);

while (1)

{

iAddrSize = sizeof(client);

sClient = accept(sListen, (struct sockaddr *)&client,

&iAddrSize);

if (sClient == INV ALID_SOCKET)

{

printf("accept() failed: %d\n", WSAGetLastError());

break;

}

printf("Accepted client: %s:%d\n",

inet_ntoa(client.sin_addr), ntohs(client.sin_port));

char fileList[DEFAULT_BUFFER]="";//创建文件表地址

FILE*fp;

fp=fopen("1.txt","r");//读取文件表,文件表默认为保存在1.txt中

int flen=fread(fileList,1,DEFAULT_BUFFER,fp);

send(sClient,fileList,flen,0);

flen=0;

hThread = CreateThread(NULL, 0, ClientThread, //多线程控制

(LPVOID)sClient, 0, &dwThreadId);

if (hThread == NULL)

{

printf("CreateThread() failed: %d\n", GetLastError());

break;

}

CloseHandle(hThread);

}

closesocket(sListen);

WSACleanup();

return 0;

}

操作流程:

1.编译过程请在工程—设置—连接—工程、选项中添加ws2_3

2.lib 不然link过程会出现错误。

2.编译成功打开如图两个窗口(可打开多个客户端窗口)。

这时server已经开始服务,请在客户端输入目标IP:127.0.0.1

此时如图

可以看到我们客户端得到了列表,有2.txt 3.txt 1.jpg可下载

此时可以输入其中一个,我们测试其中传输最复杂的1.bmp。

此时可以看到文件夹多出了个名为RECV1.bmp的文件,打开后发现和1.bmp图一样,说明

完成了传输并重命名。。

如果是txt文档,那么客户端将返回txt文档内容给服务器。

测试完毕。

主要目的是完成传输,错误控制部分除了ip地址的合法性部分,其他都较为完善,所以请输入正确的IP。

关于多客户端部分,用户可以自行测试,由于多线程设计,互相之间传输基本是不会有影响

的。

相关文档