SOCKET编程实现Client/ Server程序:比特洪流协议模拟
一、引言
使用基本的套接字编程技术,以一对基本的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"); } } }
运行结果:
数据发送
数据接收
热门相关:邪王独宠:纨绔异能妃 惊世第一妃:魔帝,宠上身! 重生之我的书记人生 一念路向北(刘恺威主演电视剧原著) 我寄人间