参照飞鸽传书的文件传输,实现客户端与服务器端的连接

参考飞鸽传书的文件传输,实现客户端与服务器端的连接
//Server .....

// tServer.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
#include <winsock.h> 
#include <stdio.h>
#include <string>

using namespace std;

#define PORT       8080 
#define MSGSIZE    (64*1024) // 64KB
#pragma comment(lib, "ws2_32.lib")

int     g_iTotalConn = 0; 
SOCKET g_CliSocketArr[FD_SETSIZE]; 

DWORD WINAPI WorkerThread(LPVOID lpParameter); 

int main() 
{   
	WSADATA     wsaData;   
	SOCKET       sListen, sClient;   
	SOCKADDR_IN local, client;   
	int         iaddrSize = sizeof(SOCKADDR_IN);   
	DWORD       dwThreadId;   

	// Windows异步套接字)的启动命令
	if (WSAStartup(MAKEWORD(2,1),&wsaData)) //调用Windows Sockets DLL
	{ 
		printf("Winsock无法初始化!\n");
		WSACleanup();
		return 0;
	}

	// Create listening socket   
	sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);   

	// Bind           
	local.sin_addr.S_un.S_addr = htonl(INADDR_ANY); 

	local.sin_family = AF_INET; 
	local.sin_port = ::htons(PORT);   
	bind(sListen, (struct sockaddr *)&local, sizeof(SOCKADDR_IN));   

	// Listen
	::listen(sListen, 3);
	printf("Server is listening!\n");

	while (TRUE)   
	{
		// Accept a connection     
		sClient = accept(sListen, (struct sockaddr *)&client, &iaddrSize);     
		printf("Accepted client:%s:%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));

		// Create worker thread   
		CreateThread(NULL, 0, WorkerThread, NULL, 0, &dwThreadId);     

		// Add socket to g_CliSocketArr     
		g_CliSocketArr[g_iTotalConn++] = sClient;   
 	}  

	return 0; 
} 

// fd_set set;
// FD_ZERO(&set);		将set清零使集合中不含任何fd
// FD_SET(fd, &set);	将fd加入set集合  
// FD_CLR(fd, &set);	将fd从set集合中清除  
// FD_ISSET(fd, &set);  测试fd是否在set集合中

DWORD WINAPI WorkerThread(LPVOID lpParam) 
{   
	printf("WorkerThread\n");

	int             i;   
	fd_set         fdreadset;   
	int             ret;   
	struct timeval tv = {1, 0};   
	char           szMessage[MSGSIZE];

	__int64 sumRev = 0;

	string newPath = "./new.avi";
	HANDLE hOut = ::CreateFile(newPath.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
	if ( INVALID_HANDLE_VALUE == hOut )
	{
		perror("open out file");
		return 0;
	}

	while (TRUE)   
	{
		// printf(". ");

		FD_ZERO(&fdreadset);     
		for (i = 0; i < g_iTotalConn; i++) 
		{ 
			FD_SET(g_CliSocketArr[i], &fdreadset); 
		}

		// We only care read event 
		// 非阻塞...
		ret = select(0, &fdreadset, NULL, NULL, &tv); 
		if (ret == 0) 
		{
			// Time expired 
			continue; 
		}

		for (i = 0; i < g_iTotalConn; i++) 
		{ 
			if (FD_ISSET(g_CliSocketArr[i], &fdreadset)) 
			{
				// A read event happened on g_CliSocketArr 
				memset(szMessage,0,MSGSIZE);

				ret = recv(g_CliSocketArr[i], szMessage, MSGSIZE, 0); 
				if (ret == 0 || (ret == SOCKET_ERROR && WSAGetLastError() == WSAECONNRESET)) 
				{ 
					// Client socket closed           
					printf("Client socket %d closed.\n\n", g_CliSocketArr); 
					closesocket(g_CliSocketArr[i]); 

					FD_CLR(g_CliSocketArr[i], &fdreadset);

					if (i < g_iTotalConn - 1) 
					{ 
						g_CliSocketArr[i--] = g_CliSocketArr[--g_iTotalConn]; 
					}
					else
					{
						--g_iTotalConn;
					}

					cout<<"rev = "<<sumRev/1024/1024<<"MB"<<endl;
					CloseHandle(hOut);
					::ExitThread(0);
				} 
				else if(ret > 0)
				{ 
					sumRev += ret;
					//cout<<"r = "<<ret/1024<<"KB"<<endl;

					DWORD	wsize;
					//char tmpMes[]
					if (::WriteFile(hOut, szMessage, ret, &wsize, 0) != TRUE )
					{
						perror("write");
						break;
					}

					//cout<<"w = "<<wsize/1024<<"KB"<<endl;

					//printf("write over.\n");
				} 
			} //if 
		}//for 
	}//while

	return 0; 
}

// Client....

#include <stdio.h>
#include <stdlib.h>

#include <string.h>
#include <sys/types.h>

#include <WinSock2.h>
#include <iostream>

#include <WinBase.h>
#include <string>

using namespace std;

#define IPMSG_DEFAULT_VIEWMAX		(8 * 1024 * 1024)	// 8MB
#define IPMSG_DEFAULT_TCPBUFMAX		(256 * 1024)		// 256KB

// 网络链接库
#pragma comment( lib , "ws2_32.lib")

DWORD mlastTick = 0;

bool SendFile(int sockfd)
{
	// 打开文件
	const string orgPath = "./abcd.avi";
	HANDLE hIn = ::CreateFile(orgPath.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
	if ( INVALID_HANDLE_VALUE == hIn )
	{
		perror("open in file");
		return false;
	}

	// 获取文件大小
	DWORD dwInFileSizeHigh = 0;
	__int64 qwInFileSize = ::GetFileSize(hIn, &dwInFileSizeHigh);
	qwInFileSize |= (((__int64)dwInFileSizeHigh) << 32);

	// 创建文件映射
	HANDLE hInMapFile = ::CreateFileMapping(hIn, NULL, PAGE_READONLY, 0, 0, NULL);
	if ( INVALID_HANDLE_VALUE == hInMapFile )
	{
		::CloseHandle(hIn);
		return false;
	}

	__int64 remain = qwInFileSize;
	__int64 offset = 0;

	// 调整映射
	while(remain > 0)
	{
		printf("next block\n");
		cout<<"offset = "<<offset<<endl;
		
		// 映射下一块,一次8M
		char* pInMapAddress = (char *)::MapViewOfFile(hInMapFile, FILE_MAP_READ, (int)(offset >> 32), (int)offset, 
			(int)(remain > IPMSG_DEFAULT_VIEWMAX ? IPMSG_DEFAULT_VIEWMAX : remain));

		if(NULL == pInMapAddress)
		{
			perror("MapViewOfFile!");

			::CloseHandle(hIn);
			return false;
		}
		
		int size = ::send(sockfd, pInMapAddress, (DWORD)(remain > IPMSG_DEFAULT_VIEWMAX ? IPMSG_DEFAULT_VIEWMAX : remain), 0);
		if(size == -1)
		{
			perror("send error!");
			return false;
		}

		offset += remain > IPMSG_DEFAULT_VIEWMAX ? IPMSG_DEFAULT_VIEWMAX : remain;

		remain -= remain > IPMSG_DEFAULT_VIEWMAX ? IPMSG_DEFAULT_VIEWMAX : remain;	// 计算新的剩余量
		cout<<"offset = "<<offset<<"  size = "<<size<<"  remain = "<<remain<<endl;

		::UnmapViewOfFile(pInMapAddress);	// 删除旧的映射

		// 更新总消耗时间
		DWORD lastTick = ::GetTickCount();
		DWORD t = lastTick - mlastTick;
		mlastTick = lastTick;

		cout<<"t = "<<t<<endl<<endl;
	}

	::CloseHandle(hIn);

	return	TRUE;
}

int main(int argc,char *argv[])
{
	// Windows异步套接字)的启动命令
	WSADATA wsaData;
	if (WSAStartup(MAKEWORD(2,1),&wsaData)) //调用Windows Sockets DLL
	{ 
		printf("Winsock无法初始化!\n");
		WSACleanup();
		return 0;
	}

	int sockfd,numbytes;
	char buf[100];

	struct sockaddr_in their_addr;

	//建立一个TCP套接口
	if((sockfd = ::socket(AF_INET,SOCK_STREAM,0))==-1)
	{
		perror("socket");
		printf("create socket error.建立一个TCP套接口失败");
		exit(1);
	}

	// 初始化socket Buffer
	int TcpbufMax = IPMSG_DEFAULT_TCPBUFMAX;
	for (int buf_size = TcpbufMax; buf_size > 0; buf_size /= 2)
	{
		if (::setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (char *)&buf_size, sizeof(buf_size)) == 0)
		{
			break;
		}
	}

	//初始化结构体,连接到服务器的8080端口
	their_addr.sin_family = AF_INET;
	their_addr.sin_port = htons(8080);

	hostent	*pEnt;
	char hostname[256];
	::gethostname(hostname, sizeof(hostname));
	if ((pEnt = ::gethostbyname(hostname)) != NULL)
	{
		ULONG ad = *(ULONG *)pEnt->h_addr_list[0];
		their_addr.sin_addr.s_addr = inet_addr("10.1.37.232");
		//std::cout<<ad<<std::endl;
	}

	memset(their_addr.sin_zero,0,8);

	//和服务器建立连接
	if(::connect(sockfd,(struct sockaddr *)&their_addr,sizeof(struct sockaddr))==-1)
	{
		perror("connect");
		exit(1);
	}

	SendFile(sockfd);

	//关闭socket
	closesocket(sockfd);

	getchar();
}


飞鸽的传输过程中, 客户端发送文件, 使用了文件的内存映射,  可以加快文件的读入.

本人实验了下, 局域网内每秒1.2M左右., 没有飞鸽传的快. 不太理想,

 

服务器端,使用了::select()函数, 实现非阻塞式连接..

代码还有待改进, 望高手指点...