一、引言   

    使用基本的套接字编程技术,以一对基本的TCP协议通信程序为基础,模拟比特洪流(BitTorrent)的分散传输技术完成一个文件的正确传输,使用标准C语言编程。本实验的目的并不是做一个实用的网络程序,而是更好地理解套接字编程原理和P2P技术,重点在特定条件下的实验方案的设计并予以实现。    

   尽可能模拟比特洪流协议,描述文件分散传输实验方案,实现同一个文件内容从多个PEER获取。

  

二、设计思路

 比特洪流的简要介绍

  BitTorrent(简称BT)是一个文件分发协议,每个下载者在下载的同时不断向其他下载者上传已下载的数据。而在FTP,HTTP协议中,每个下载者在下载自己所需文件的同时,各个下载者之间没有交互。当非常多的用户同时访问和下载服务器上的文件时,由于FTP服务器处理能力和带宽的限制,下载速度会急剧下降,有的用户可能访问不了服务器。BT协议与FTP协议不同,特点是下载的人越多,下载速度越快,原因在于每个下载者将已下载的数据提供给其他下载者下载,充分利用了用户的上载带宽。通过一定的策略保证上传速度越快,下载速度也越快。在很短时间内,BitTorrent协议成为一种新的变革技术。

 BitTorrent 的发展依赖于peer-to-peer技术。对等网络 (Peer – to – Peer 简称 P2P) 的研究一直是国外知名学府和知名企业以及研发机构最关注的重点。P2P是近年来互联网最热门的技术,在VoIP、下载、流媒体、协调技术等领域得到飞速发展,被财富杂志评为影响互联网的四大科技之一,甚至被认为是代表无线宽带互联网未来的关键技术。

 设计思路

首先将一个图形文件划分成指定大小的数据块。在一台主机下模拟五个应用进程,其中有四个peer,分别为peer1,peer2,peer3,peer4,还有一个tracker。将上述的图片数据块按要求分配到三个节点(peer1,peer2,peer3)上,peer4从tracker上获取三个节点的数据块信息。最后从三个节点(peer1,peer2,peer3)传输图片文件的不同块到peer4上,使得该节点获取完整的图片文件。

三、实验环境

C语言

实验结构图:

假定peer1上存放数据块1,3,5,7

peer2上存放数据块9,10,11

peer3上存放数据块2,4,6,8

四、编程实现

数据发送方(peer1 peer2 peer3):

 1 #include  2 #include  3 #include  4 using namespace std; 5 #define NO_FLAGS_SET 0 6 #define FILE_ID_START 1 7 #define FILE_ID_END 4 8 #define FILE_BLOCK_SIZE 409600 9 #define PORT (u_short) 4496610 #define DEST_IP_ADDR "127.0.0.1" //Server address11 12 INT main(VOID)13 {14   WSADATA Data;15   SOCKADDR_IN destSockAddr;16   SOCKET destSocket;17   unsigned long destAddr;18   int status;19   int numsnt;20   21   /* initialize the Windows Socket DLL */22   status=WSAStartup(MAKEWORD(1, 1), &Data);23   if (status != 0)24     cerr << "ERROR: WSAStartup unsuccessful"25       << endl;26   /* convert IP address into in_addr form */27   destAddr=inet_addr(DEST_IP_ADDR);28   /* copy destAddr into sockaddr_in structure */29   memcpy(&destSockAddr.sin_addr,30     &destAddr, sizeof(destAddr));31   /* specify the port portion of the address */32   destSockAddr.sin_port=htons(PORT);33   /* specify the address family as Internet */34   destSockAddr.sin_family=AF_INET;35 36   /* create a socket */37   destSocket=socket(AF_INET, SOCK_STREAM, 0);38   if (destSocket == INVALID_SOCKET)39   {40     cerr << "ERROR: socket unsuccessful" << endl;41     status=WSACleanup();42     if (status == SOCKET_ERROR)43       cerr << "ERROR: WSACleanup unsuccessful" 44         << endl;45     return(1);46   }47 48   cout << "Trying to connect to IP Address: "49     << DEST_IP_ADDR << endl;50 51   /* connect to the server */52   status=connect(destSocket,53     (LPSOCKADDR) &destSockAddr,54     sizeof(destSockAddr));55   if (status == SOCKET_ERROR)56   {57     cerr << "ERROR: connect unsuccessful" << endl;58     status=closesocket(destSocket);59     if (status == SOCKET_ERROR)60       cerr << "ERROR: closesocket unsuccessful"61         << endl;62     status=WSACleanup();63     if (status == SOCKET_ERROR)64       cerr << "ERROR: WSACleanup unsuccessful"65         << endl;66     return(1);67   }68 69   cout << "Connected..." << endl;70 71 72     int id = FILE_ID_START;            // 文件块编号73     FILE *fp;            // 文件块读取指针74     char fileName[100]; //文件名75     char path[100];//路径 76     char buf[FILE_BLOCK_SIZE];77     sprintf(fileName, "part_%02d", id);78     while (id <= FILE_ID_END) {    79         send(destSocket, fileName, strlen(fileName) + 1, 0);// 先发送文件名80         Sleep(50); 81         sprintf(path, "%s%s", "C:\\Users\\大梦一场\\Desktop\\peer1\\",fileName);82         fp = fopen(path, "rb+");83         int count = fread(buf, 1, FILE_BLOCK_SIZE, fp); // 读取全部元素84         fclose(fp);85         send(destSocket, buf, count, 0);                // 发送文件块86         printf("ClientPeer1 sending %s successfully\n", path);87         Sleep(3000); // 等待 3 s88         id++;89         sprintf(fileName, "part_%02d", id);90     }91 92 }

数据接收方 (peer4):

#include #include #include using namespace std;#define NO_FLAGS_SET 0#define THIS_PORT (u_short) 44967#define PORT (u_short) 44966#define DEST_IP_ADDR "127.0.0.1" //Server address#define FILE_BLOCK_SIZE 409600#define FILE_BLOCK_NUM 11#define FILE_NAME_LEN 7#define DEST_PORT (u_short) 44966#define CLIENT_NUM 3char dataBuffer[FILE_BLOCK_SIZE];// 用于存储接收到的数据char fileName[100]; //文件名DWORD ThreadProc(LPVOID clientSock)  {  SOCKET NewConnection = (SOCKET)clientSock;    char dataBuffer[FILE_BLOCK_SIZE];// 用于存储接收到的数据char fileName[100]; //文件名int count;FILE *fp = NULL;char path[100];//路径 while ((count = recv(NewConnection, dataBuffer, sizeof(dataBuffer), 0)) > 0) {if (strlen(dataBuffer) == FILE_NAME_LEN) {// 发来的是文件名strcpy(fileName, dataBuffer);// 记录文件名printf("receving %s\n", fileName);}else {sprintf(path, "%s%s", "C:\\Users\\大梦一场\\Desktop\\peer4\\",fileName);fp = fopen(path, "wb");// 创建文件fwrite(dataBuffer, count, 1, fp);printf("receving data %s\n", fileName);fclose(fp);}}      return 0;  }int main(void){    int numsnt;  char *getInfo="getInfo";  WSADATA Data;  SOCKADDR_IN serverSockAddr;  SOCKADDR_IN clientSockAddr;  SOCKET serverSock;  SOCKET clientSock;  int addrLen=sizeof(SOCKADDR_IN);  int status;  int numrcv;  char buffer[1024];  /* initialize the Windows Socket DLL */  status=WSAStartup(MAKEWORD(1, 1), &Data);  /*初始化Winsock DLL*/  if (status != 0)    cerr << "ERROR: WSAStartup unsuccessful" << endl;  /* zero the sockaddr_in structure */    memset(&serverSockAddr, 0,sizeof(serverSockAddr));      /* specify the port portion of the address */  serverSockAddr.sin_port=htons(PORT);  /* specify the address family as Internet */  serverSockAddr.sin_family=AF_INET;  /* specify that the address does not matter */  /*INADDR_ANY 的具体含义是,绑定到0.0.0.0。此时,对所有的地址都将是有效的*/  serverSockAddr.sin_addr.s_addr=htonl(INADDR_ANY);  /* create a socket */  serverSock=socket(AF_INET, SOCK_STREAM, 0);  if (serverSock == INVALID_SOCKET)    cerr << "ERROR: socket unsuccessful" << endl;  /* associate the socket with the address */  status=bind(serverSock, (LPSOCKADDR) &serverSockAddr,    sizeof(serverSockAddr));  if (status == SOCKET_ERROR)    cerr << "ERROR: bind unsuccessful" << endl;  /* allow the socket to take connections */  status=listen(serverSock, 1);  if (status == SOCKET_ERROR)    cerr << "ERROR: listen unsuccessful" << endl;      int conn=0;  while(1)  {int clientAddrLen = sizeof(SOCKADDR);// 求出地址的长度// 接收请求(链接客户端),第二个参数是客户端的地址if ((clientSock = accept(serverSock, (LPSOCKADDR)&clientSockAddr, &clientAddrLen)) == INVALID_SOCKET){printf("accept failed with error %d\n", WSAGetLastError());closesocket(clientSock); // 释放客户端连接}// 连接客户端成功,打印客户端的ip地址和端口号printf("当前已连接:%s:%d\n", inet_ntoa(clientSockAddr.sin_addr), ntohs(clientSockAddr.sin_port));// 开启线程处理客户端连接CreateThread(NULL, 0, ThreadProc, (LPVOID)clientSock, 0, NULL);conn++;/* 文件的拼接 */  if (conn == CLIENT_NUM) { //  接收完3个进程发送的文件Sleep(2000);printf("Succeeded in accepting all file blocks. \n");int id = 1;// 文件块编号FILE *fpr;// 文件块读取指针FILE *fpw = fopen("C:\\Users\\大梦一场\\Desktop\\DarkSoul.jfif", "wb");char fileName[100]; //文件名while (id <= FILE_BLOCK_NUM) {sprintf(fileName, "C:\\Users\\大梦一场\\Desktop\\peer4\\part_%02d", id);fpr = fopen(fileName, "rb+");int count = fread(dataBuffer, 1, FILE_BLOCK_SIZE, fpr);fwrite(dataBuffer, count, 1, fpw);id++;fclose(fpr);}fclose(fpw);Sleep(1000);printf("DarkSoul.jfif combining succeeded!\n");}    }}

  

运行结果:

                                          

                                         数据发送

            

                                       数据接收