IOC服务器发送大数据问题,在线等....

str345 2013-04-02 11:27:38
场景:
有这么一个基于IOCP模型的Server,这个Server提供一种服务……文件下载,我们再假设Server端存有一个10G的文件,客户端这时发送一个请求到服务端来,客户端要求下载这个10G的文件,假设服务器段每个用户的发送缓冲为8K,如果使用
for()
{
WSASend();
}
发送数据,不久服务器发送缓冲会耗尽,显然不行。网上说使用发送队列的方式,不知道具体应该如何做,请高手详细解疑,谢谢 ?我还知道一种方式:服务器端使用线程监视发送缓冲,发现发送缓冲区有空闲空间,就将新数据写入,之后,发送线程取出数据发送给用户,但这种方式有个潜在的问题:(该方式是在假设快服务器,慢用户端的前提下才有效的)
假设第一个8K数据传到用户端,用户端发回收到消息,这时服务器端的发送线程在该用户的缓冲区中寻找未发送的数据,然而,在服务器发送数据这段时间内,将新数据写入缓冲区的线程恰巧挂起,缓冲区内没有新数据,这时发送线程找不到新数据了,于是服务器端和用户端都在等对方回应,数据传输也停止了,如何解决这个问题?

...全文
250 16 打赏 收藏 转发到动态 举报
写回复
用AI写文章
16 条回复
切换为时间正序
请发表友善的回复…
发表回复
smwhotjay 2013-04-04
  • 打赏
  • 举报
回复
首先不能死循环发送. 要client请求一次 server发送一包. client请求包 要包含文件信息.文件开始点和结束点. 楼下的TransmitFile 是个方法.我也用过.但貌似TransmitFile 效率不够. 无法支持大量连接的文件传输.我xp 测试只5个连接就满了.
wumn29 2013-04-04
  • 打赏
  • 举报
回复
方法1:
  // 得到文件尺寸
 DWORD dwFileSizeHigh;
 __int64 qwFileSize = GetFileSize(hFile, &dwFileSizeHigh);
 qwFileSize |= (((__int64)dwFileSizeHigh) << 32);///MSDN; 
               
                __int64 persize = 2097151;//2G-1(每次发送的大小)
               LARGE_INTEGER li;
               li.QuadPart = 0;
               li.LowPart = SetFilePointer (hFile, li.LowPart,&li.HighPart, MoveMethod);
                while (true)
                {
                    if (qwFileSize > persize)//大于2G-1
                    {
                        TransmitFile(sClient, hFile,persize,1024, (LPOVERLAPPED)li.QuadPart,NULL,TF_REUSE_SOCKET|TF_DISCONNECT);
                        li.QuadPart += persize;
               li.LowPart = SetFilePointer (hFile, li.LowPart,&li.HighPart, MoveMethod);
                        size -= persize;
                    }
                    else
                    {
                        TransmitFile(sClient, hFile,size,1024,(LPOVERLAPPED)li.QuadPart,NULL,TF_REUSE_SOCKET|TF_DISCONNECT);
                        break;
                    }
                }
                 
                CloseHandle(hFile);
方法2:使用文件映射方式处理文件, 然后逐块调用WSASend: 代码省略, 我现在用的老婆的电脑, 木有编译器, 文件映射的代码可以参考我写的一个例子: http://download.csdn.net/detail/wumn29/5179452
str345 2013-04-03
  • 打赏
  • 举报
回复
缓冲区满是可以检测的 --------------------- 请教高手,如何检测,不是发送0长度检测吧,这个不准,没什么用
wumn29 2013-04-03
  • 打赏
  • 举报
回复
写完了没测试,你参考一下, 下班了,晚上回去看一下
DWORD WINAPI WorkerThread(LPVOID CompletionPortID)
{
	HANDLE                  CompletionPort=(HANDLE)CompletionPortID;
	DWORD                   dwBytesTransferred;
	SOCKET                  sClient;
	LPPER_IO_OPERATION_DATA lpPerIOData = NULL;

	while (TRUE)
	{
		GetQueuedCompletionStatus(
			CompletionPort,
			&dwBytesTransferred,
			(PULONG_PTR)&sClient,
			(LPOVERLAPPED *)&lpPerIOData,
			INFINITE);
		if (dwBytesTransferred == 0xFFFFFFFF)
		{
			return 0;
		}

		if (lpPerIOData->OperationType == RECV_POSTED)
		{
			if (dwBytesTransferred == 0)
			{
				// Connection was closed by client
				printf("Connection was closed by client\r\n");
				closesocket(sClient);
				HeapFree(GetProcessHeap(), 0, lpPerIOData);       
			}
			else
			{
				printf("IP:%s PORT:%s DATA:%s\r\n", lpPerIOData->szHeader, (char*)(lpPerIOData->szHeader)+strlen(lpPerIOData->szHeader)+1, lpPerIOData->szMessage);
				
				char* answer = "服务器收到了请求, 现在开始传送文件...";
				send(sClient, answer, strlen(answer), 0);

				HANDLE hFile = CreateFileA("C:\\Documents and Settings\\wangwei_b\\My Documents\\Visual Studio 2008\\Projects\\完成端口\\Release\\测试文件传输.txt",
					GENERIC_READ,
					FILE_SHARE_READ,
					NULL,
					OPEN_EXISTING,
					FILE_ATTRIBUTE_NORMAL,
					NULL);
				int size = GetFileSize(hFile, NULL); 
				FILE* pfile = (File*)SetFilePointer(hFile, 0, 0, FILE_BEGIN);
				__int64 persize = 2097151;//2G-1
				while (true)
				{
					if (size > persize)//大于2G-1
					{
						TransmitFile(sClient, hFile,persize,1024, (LPOVERLAPPED)pfile,NULL,TF_REUSE_SOCKET|TF_DISCONNECT);
						pfile += persize;
						size -= persize;
					}
					else
					{
						TransmitFile(sClient, hFile,size,1024,(LPOVERLAPPED)pfile,NULL,TF_REUSE_SOCKET|TF_DISCONNECT);
					}
				}
				
				CloseHandle(hFile);

				// Launch another asynchronous operation for sClient
				memset(lpPerIOData->szMessage, 0, sizeof(lpPerIOData->szMessage));
				lpPerIOData->Buffer.len = MSGSIZE;
				lpPerIOData->Buffer.buf = lpPerIOData->szMessage;
				lpPerIOData->OperationType = RECV_POSTED;
				WSARecv(sClient,
					&lpPerIOData->Buffer,
					1,
					&lpPerIOData->NumberOfBytesRecvd,
					&lpPerIOData->Flags,
					&lpPerIOData->overlap,
					NULL);
				//printf("工作线程WSARecv, lpPerIOData->Buffer=%s\r\n", lpPerIOData->Buffer);
			}
		}
	}
	return 0;
}
wumn29 2013-04-03
  • 打赏
  • 举报
回复
用TransmitFile搞定, 客户端循环接收
DWORD WINAPI WorkerThread(LPVOID CompletionPortID)
{
	HANDLE                  CompletionPort=(HANDLE)CompletionPortID;
	DWORD                   dwBytesTransferred;
	SOCKET                  sClient;
	LPPER_IO_OPERATION_DATA lpPerIOData = NULL;

	while (TRUE)
	{
		GetQueuedCompletionStatus(
			CompletionPort,
			&dwBytesTransferred,
			(PULONG_PTR)&sClient,
			(LPOVERLAPPED *)&lpPerIOData,
			INFINITE);
		if (dwBytesTransferred == 0xFFFFFFFF)
		{
			return 0;
		}

		if (lpPerIOData->OperationType == RECV_POSTED)
		{
			if (dwBytesTransferred == 0)
			{
				// Connection was closed by client
				printf("Connection was closed by client\r\n");
				closesocket(sClient);
				HeapFree(GetProcessHeap(), 0, lpPerIOData);       
			}
			else
			{
				printf("IP:%s PORT:%s DATA:%s\r\n", lpPerIOData->szHeader, (char*)(lpPerIOData->szHeader)+strlen(lpPerIOData->szHeader)+1, lpPerIOData->szMessage);
				
				char* answer = "服务器收到了请求, 现在开始传送文件...";
				send(sClient, answer, strlen(answer), 0);

				HANDLE hFile = CreateFileA("C:\\Documents and Settings\\wangwei_b\\My Documents\\Visual Studio 2008\\Projects\\完成端口\\Release\\测试文件传输.txt",
					GENERIC_READ,
					FILE_SHARE_READ,
					NULL,
					OPEN_EXISTING,
					FILE_ATTRIBUTE_NORMAL,
					NULL);
				TransmitFile(sClient, hFile,0,1024,NULL,NULL,TF_REUSE_SOCKET|TF_DISCONNECT);
				CloseHandle(hFile);

				// Launch another asynchronous operation for sClient
				memset(lpPerIOData->szMessage, 0, sizeof(lpPerIOData->szMessage));
				lpPerIOData->Buffer.len = MSGSIZE;
				lpPerIOData->Buffer.buf = lpPerIOData->szMessage;
				lpPerIOData->OperationType = RECV_POSTED;
				WSARecv(sClient,
					&lpPerIOData->Buffer,
					1,
					&lpPerIOData->NumberOfBytesRecvd,
					&lpPerIOData->Flags,
					&lpPerIOData->overlap,
					NULL);
				//printf("工作线程WSARecv, lpPerIOData->Buffer=%s\r\n", lpPerIOData->Buffer);
			}
		}
	}
	return 0;
}
菜牛 2013-04-03
  • 打赏
  • 举报
回复
缓冲区满是可以检测的,满了就等,跟什么大数据、小数据没有关系。
ghost5216 2013-04-02
  • 打赏
  • 举报
回复
引用 楼主 str345 的回复:
服务器发送缓冲会耗尽
是什么意思
str345 2013-04-02
  • 打赏
  • 举报
回复
不用TransmitFile,用WSASend
这个娜戒海了 2013-04-02
  • 打赏
  • 举报
回复
我记得有个TransmitFile函数的,你搜搜 功能说明:TransmitFile 是一个扩展的 API,它允许在套接字连接上发送一个打开的文件。这使得应用程序可以避免亲自打开文件,重复地在文件执行读入操作,再将读入的那块数据写入套接字。相反,已打开的文件的句柄是各套接字连接一起给出的,在套接字上,文件数据的读入和发送都在核心模式下进行。这就避免了亲自执行文件读入时必须的多重内核变换。
str345 2013-04-02
  • 打赏
  • 举报
回复
通常服务器端发送大数据的流程是如何做的?能说说嘛?
wushuang443 2013-04-02
  • 打赏
  • 举报
回复
建议你把你的代码发出来。要改的话也好帮你看下。
wushuang443 2013-04-02
  • 打赏
  • 举报
回复
用线程来发送。你用for循环肯定不行啊。另外在加锁

18,356

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 网络编程
c++c语言开发语言 技术论坛(原bbs)
社区管理员
  • 网络编程
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧