找张无忌,求完成端口(300分)

  • 主题发起人 主题发起人 wuchunhua
  • 开始时间 开始时间
W

wuchunhua

Unregistered / Unconfirmed
GUEST, unregistred user!
找张无忌,求完成端口实例一份,实现大用户量同时再线。谢谢
 
是什么说来听听,可能帮得上你呀
 
完成端口撒,你查一下google上就晓得是啥子了,嘿嘿,热情不错!我不懂这东西!
 
类似qq游戏的处理大量用户连接的解决办法。
 
有个VC的例子,你拿去参考一下,看能不能改成DELPHI的
#include <stdio.h>
#include <winsock2.h>
#include <stdlib.h>
#pragma comment (lib,&quot;WS2_32.lib&quot;)

#define SERV_TCP_PORT 9999
#define DATA_BUFSIZE 1024

struct _OVERLAPPELUS
{
SOCKET socket;
char InBuffer[DATA_BUFSIZE]; // 输入
OVERLAPPED ovIn;
int nOutBufIndex;
char OutBuffer[DATA_BUFSIZE]; // 输出
OVERLAPPED ovOut;
DWORD dwWritten;
}OVERLAPPELUS, *LPOVERLAPPELUS;

_OVERLAPPELUS pKey;

HANDLE ghCompletionPort;

void CreateWorkerThreads();
DWORD WINAPI WorkerThread(LPVOID pvoid);
void IssueRead(struct _OVERLAPPELUS *pCntx);
void CheckOsVersion();
void FatalError(char *s);
void SendString(char *p,struct _OVERLAPPELUS *pCntx);

///////////////////////////////////////////////////////////

int main(int argc, char *argv[])
{
SOCKET listener;
SOCKET newsocket;
WSADATA WsaData;
struct sockaddr_in serverAddress;
int err;

CheckOsVersion();

err = WSAStartup (0x0202, &WsaData);
if (err == SOCKET_ERROR)
{
FatalError(&quot;网络初始化失败.&quot;);
return EXIT_FAILURE;
}

if ((listener = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0,WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)
{
printf(&quot;WSASocket() failed with error %d/n&quot;, WSAGetLastError());
return EXIT_FAILURE;
}

memset(&serverAddress, 0, sizeof(serverAddress));
serverAddress.sin_family = AF_INET;
serverAddress.sin_addr.s_addr = htonl(INADDR_ANY);
serverAddress.sin_port = htons(SERV_TCP_PORT);

err = bind(listener, (struct sockaddr *)&serverAddress, sizeof(serverAddress));
if (err < 0)
{
FatalError(&quot;bind() 错误-请检查 TCP/IP 是否正确安装?&quot;);
}

ghCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL,0,0);
if (ghCompletionPort == NULL)
{
FatalError(&quot;CreateIoCompletionPort() 错误-请检查系统是否是Windows NT version 3.51 或更高版本.&quot;);
}
CreateWorkerThreads();

listen(listener, 5);
fprintf(stderr, &quot;I/O Completion Ports 模式:端口 %d/n&quot;, SERV_TCP_PORT);
fprintf(stderr, &quot;Ctrl+C 停止服务器程序/n&quot;);
printf(&quot;written by http://www.wantsoft.com/n&quot;);
fprintf(stderr, &quot;开始监听客户端:/n&quot;);

// 无限循环,接受并处理新的连接
while (true)
{
if ((newsocket = WSAAccept(listener, NULL, NULL, NULL, 0)) == SOCKET_ERROR)
{
printf(&quot;WSAAccept() failed with error %d/n&quot;, WSAGetLastError());
return EXIT_FAILURE;
}

//建立一个 key 并初始化它.
pKey.socket = newsocket;
pKey.ovOut.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); //为写信息包过程设置(事件对象) event .
pKey.ovOut.hEvent = (HANDLE)((DWORD)pKey.ovOut.hEvent | 0x1);

CreateIoCompletionPort( //为请求绑定端口
(HANDLE)newsocket,
ghCompletionPort,
(DWORD)&pKey,
0 );

IssueRead(&pKey); // 完成第一次读操作
}

return 0;
}

void CreateWorkerThreads()
{
SYSTEM_INFO sysinfo;
DWORD dwThreadId;
DWORD dwThreads;
DWORD i;

GetSystemInfo(&sysinfo);
dwThreads = sysinfo.dwNumberOfProcessors * 2 + 2;
for (i=0; i<dwThreads; i++)
{
HANDLE hThread;
hThread = CreateThread(NULL, 0, WorkerThread, NULL, 0, &dwThreadId);
CloseHandle(hThread);
}
}


// 每一个工作线程从这里开始.
DWORD WINAPI WorkerThread(LPVOID pVoid)
{
BOOL bResult;
DWORD dwNumRead;
struct _OVERLAPPELUS *pCntx;
LPOVERLAPPED lpOverlapped = NULL;

UNREFERENCED_PARAMETER(pVoid);

// 无限循环从 I/O completion port 获取信息.
while (true)
{
bResult = GetQueuedCompletionStatus(ghCompletionPort, &dwNumRead, (DWORD*)&pCntx, &lpOverlapped, INFINITE);

if (bResult == FALSE && lpOverlapped == NULL)
{
FatalError(&quot;WorkerThread - GetQueuedCompletionStatus()错误./n&quot;);
}
else if (bResult == FALSE && lpOverlapped != NULL)
{
fprintf(stderr,&quot;用户非正常退出./n&quot;);
}
else if (dwNumRead == 0)
{
fprintf(stderr, &quot;用户已经退出./n&quot;);
fprintf(stderr, &quot;------------------./n&quot;);
}
else
{
printf(&quot;recv data from client: %s/n&quot;, pCntx->InBuffer);
SendString(pCntx->InBuffer, pCntx);
IssueRead(pCntx);
}
}
ExitThread(0);
return 0;
}


///////////////////////////////////////////////////////
//调用 WSARecv 开始一个异步请求重复获取我们的句柄状态//
///////////////////////////////////////////////////////
void IssueRead(struct _OVERLAPPELUS *pCntx)
{
DWORD RecvBytes;
DWORD Flags =0;
WSABUF DataBuff;

if (pCntx == NULL)
return;
memset(pCntx->InBuffer, 0, sizeof(pCntx->InBuffer));
ZeroMemory( &(pCntx->ovIn), sizeof(OVERLAPPED));

DataBuff.len = DATA_BUFSIZE;
DataBuff.buf = pCntx->InBuffer;

if (WSARecv(pCntx->socket, &DataBuff, 1, &RecvBytes, &Flags,
&(pCntx->ovIn), NULL) == SOCKET_ERROR)
{
if (WSAGetLastError() != ERROR_IO_PENDING)
{
printf(&quot;WSARecv() failed with error %d/n&quot;, WSAGetLastError());
return ;
}
}
}

// 确保我们运行在正确的版本下 Windows NT (3.51, 4.0, or later)
void CheckOsVersion()
{
OSVERSIONINFO ver;
BOOL bResult;

ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);

bResult = GetVersionEx((LPOSVERSIONINFO) &ver);

if ( (!bResult) ||
(ver.dwPlatformId != VER_PLATFORM_WIN32_NT) )
{
FatalError(&quot;ECHOSRV requires Windows NT 3.51 or later.&quot;);
}
}


//发送字符串子程序
void SendString(char *p, struct _OVERLAPPELUS *pCntx)
{
DWORD SendBytes;
WSABUF DataBuff;
int len;

strcpy(pCntx->OutBuffer,p);
len = strlen(pCntx->OutBuffer);

pCntx->OutBuffer[len] = '/0';
pCntx->nOutBufIndex = strlen(pCntx->OutBuffer);

DataBuff.len = len;
DataBuff.buf = pCntx->OutBuffer;


if (WSASend(pCntx->socket, &DataBuff, 1, &SendBytes, 0,
&(pCntx->ovOut), NULL) == SOCKET_ERROR)
{
if (WSAGetLastError() != ERROR_IO_PENDING)
{
printf(&quot;WSASend() failed with error %d/n&quot;, WSAGetLastError());
return ;
}
}
}

// 错误句柄
void FatalError(char *s)
{
fprintf(stdout, &quot;%s/n&quot;, s);
exit(EXIT_FAILURE);
}
 
找盖次,求WINDOWS源码一份,实现32位机操作系统。谢谢。
 
楼上的大哥 说的比较经典(NBA-牛逼带尖了)
 
几点疑问:
1、WSAAccept与Accept ,WSAConnect与Connect的区别 为什么我客户端和服务端分别采用WSAConnect、WSAAccept进行连接处理 在同一台电脑上可以连接上,而把服务器与客户端分开便连接不上, 同样的代码,把WSAConnect、WSAAccept换成Connect、Accept 进行连接,不管服务端和客户端是否在同一台电脑上都没有问题。
2、服务端如果采用完成端口,是用WSARecv,还是Recv去接收数据比较合理,WSARecv是不是异步 不需要等待吗?
3、如果客户端异常死机 服务器如何判断这个连接已经无效 ,是不是通过获取连接时间,和客户端最后一次数据通信的时间差>某个值 便Closesocket这个连接?
 
1. WSAAccept与Accept ,WSAConnect与Connect的区别 基础Winsock的版本不一样,
前者是 winsocket = 'ws2_32.dll';
后者是 winsocket = 'wsock32.dll';

所以,在使用时,对Winsock的初始也是不一样的,(就是要具体指定sock版本)
前者是 WSAStartup(MAKEWORD(2, 2), wsaData);
后者是 WSAStartup(MAKEWORD(1, 1), wsaData);

由此可见,请查看一下对的Winsock初始是否有问题

2. IOCP本身是异步I/O通信的,所以最好是用WSARecv(也是异步请求)

3. 这个有些麻烦,如果是正常情况下的断线或网络异常,Server端会收到一个异常信息,可以捕捉。
 
以后这样的贴最好不要进,不小心就得了心脏病;
 
下次一定写上dongy_143与心脏病患者不得入内!
 
张无忌大侠出国旅游了。明年回[:D]
 
完成端口 bbs上的贴子多不胜数,有一部分还是对你有帮助的,
自己多动手实践,好过于这里求别人贴代码,当你搞清楚了,完成端口也就是那么回事
和其他IO模型一样简单。
 
听说 张无忌 好象封了ID 不用这个号了嘿嘿
 
有实例也给我一份好么
fc182182@yahoo.com.cn
 
indy 的supercore包里面有对IOCP的支持,但是需要把打包文件改成对DELPHI2006支持的格式。
 
INDY是不支持IOCP的。
 
完成端口一个奇怪的问题,完成端口是会为每个连接分配一个套接字(不知道准确不准确),如果连接1的TSocket收到一个指令,关闭自己服务器关闭成功,客户端也能收到FD_Close消息, 如果连接2收到指令CloseSocket(连接1的套接字) 服务器上关闭成功,但是客户端收不到FD_Close消息, 我把每个连接的SOCKET放在一个TLIST中
 
只要有客户连结,服务端就会有一个新的套接字与这个客户端对应,最好不要用TLIST,可以用一个比较大的数组
 
后退
顶部