一、引言   

    使用基本的套接字编程技术,以一对基本的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 <windows.h>
 2 #include <iostream>
 3 #include <winsock.h>
 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) 44966
10 #define DEST_IP_ADDR "127.0.0.1" //Server address
11 
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 s
88         id++;
89         sprintf(fileName, "part_%02d", id);
90     }
91 
92 }

数据接收方 (peer4):

#include <windows.h>
#include <iostream>
#include <winsock.h>
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 3
char 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");
		}
    }
}

  

运行结果:

                                          

                                          数据发送

            

                                       数据接收