完成端口-如何在已建立的socket上同时进行接收和发送数据请求,或者如何同时提交多个发送请求?-来者有分!

ttkttkttk 2004-09-06 09:30:52
看了很多关于完成端口的论述,本人也成功做过比较多的完成端口编程,但现在发现一个问题,就是如何在已建立的socket上同时进行接收和发送数据,或者如何同时提交多个发送请求?

因为每个socket都有一个socket context,其包括了发送和接收数据的缓冲区,还有此次操作的类型。这样若在一个操作没有完成之前再次提交其他操作,必然需要改变socket context,这样会影响前一次提交的请求。

请问各位该如何解决,还是对同一个socket来说,完成端口根本就不能同时提交多个操作请求,必须等上一个操作请求完成之后,再提交下一个请求?????
...全文
1424 52 打赏 收藏 转发到动态 举报
写回复
用AI写文章
52 条回复
切换为时间正序
请发表友善的回复…
发表回复
CompletionPort 2005-01-18
  • 打赏
  • 举报
回复
mark
cctime 2004-11-24
  • 打赏
  • 举报
回复
关于CancelIo(),MSDN:
The function does not cancel I/O operations issued for the file handle by other threads.
ttkttkttk 2004-09-10
  • 打赏
  • 举报
回复
就是没有想到很好的解决办法才向大家请教的。
bbcharm 2004-09-10
  • 打赏
  • 举报
回复
如果不缓存 你怎么处理呢
ttkttkttk 2004-09-10
  • 打赏
  • 举报
回复
又遇到一个问题,在完成端口接收数据的过程中,若客户端发送的数据比较大,比如1M,服务器一次接收不完,这样是不是要在服务器缓存很多数据(对那些没有接收完备的数据包进行缓存,等待接收完毕之后交给程序处理)。这样是不是很耗内存?
poorhouse 2004-09-09
  • 打赏
  • 举报
回复
那个贴了完整代码的哥哥,能不能再贴一段相同的功能的UDP的完整代码呀,或者发给我也行tangdreamonline@263.net,拜托了。
ttkttkttk 2004-09-09
  • 打赏
  • 举报
回复
我觉得UDP没有必要用完成端口,我以前做过测试,UDP完成端口还没有UDP同步服务器快。具体代码早就丢了。不过和TCP的差不多啦,反正都是IO操作吗!
changlin365 2004-09-09
  • 打赏
  • 举报
回复
up
Hellboy 2004-09-09
  • 打赏
  • 举报
回复
我也正在写完成端口的服务器端,每进行过具体的测试.

我在每个客户端的socketobj都加了一个正在发送的计数,以及发送队列的长度.

对于有太多的未发送完成的客户端,应该把它关闭.

(主要是基于微软iocp标准模型的基础上作的开发.)
ttkttkttk 2004-09-09
  • 打赏
  • 举报
回复
另外,你测试过没有,一台PC大致可以管理多少socket(最大性能),每秒发送接收多少个数据包(每个包小于64byte)?
ttkttkttk 2004-09-09
  • 打赏
  • 举报
回复
那怎么控制,每个socket有一个计数器吗?
Hellboy 2004-09-09
  • 打赏
  • 举报
回复
是指每个socket
ttkttkttk 2004-09-09
  • 打赏
  • 举报
回复
to Hellboy(int argc, char* argv[])
一般同时最多发送3个左右比较好,是指所有socket,还是每个socket同时最多发送3个发送请求?
Hellboy 2004-09-09
  • 打赏
  • 举报
回复
我的完成端口是这么做的

给每个连接上来的客户端投递5个接受缓冲,每一个接受缓冲返回时,重新用这个IO context进行WSARecv()。这样对于接收的操作就不需要重新创建IO Context了。


对于每个发送的io,只能每次发送就建立一个io context。每个发送的io context都先保存在一个队列中。等待前面的发送完成以后再发送。过多的发送操作会浪费很多资源。一般同时最多发送3个左右比较好,如果每次只投递一个发送io,等待发送完成的时候,在继续投递下一个,这样做会影响性能。至少有两个发送io,可以使系统的发送缓冲一直都有数据。
copy_paste 2004-09-08
  • 打赏
  • 举报
回复
IOCP的是重叠的特例,重叠的其中一个重要概念就是同时处理多个发送/接收请求.
一般做法就是在send/recv时,将socket关联在一个new content中,然后进行投递,就是这么回事.
由于new content, free content在IOCP中是吃力不讨好的事,最好就是做个内存池来实现.
UDX协议 2004-09-08
  • 打赏
  • 举报
回复
发送是会将数据从程序的缓冲区拷贝至系统缓冲区,当系统缓冲区已满(没有来得及发送),系统将锁定程序的缓冲区,直到数据发送完毕,才会释放锁定的缓冲区。

如果这种情况,你再调用异步的发送操作,其结果是系统马上返回,返回一个erro.
说明此次投递根本不成功。

你多做几次测试就明白了。
UDX协议 2004-09-08
  • 打赏
  • 举报
回复
因此,我觉得,应当不会出现wwwllg(wwwllg)所说的情况,当然,提交太多的请求将锁定很多内存。这对程序性能的影响应当考虑。

我觉得你想的太简单,如果象你这样说,任何一个不合法的程序,都可以搞死windows n 次了。
ttkttkttk 2004-09-08
  • 打赏
  • 举报
回复
to elssann()
我得socket content和你定义的差不多。
ttkttkttk 2004-09-08
  • 打赏
  • 举报
回复
DWORD WINAPI CIOCPServerTCP::ListenThreadProc( LPVOID lpVoid )
{
int nRet=0;
CIOCPServerTCP* pThis = reinterpret_cast<CIOCPServerTCP*>(lpVoid);

SIOCPPerSocketTCPContext* lpPerSocketContext = NULL;
SOCKET SocketAccept = INVALID_SOCKET;

DWORD dwRecvNumBytes, dwFlags=MSG_PARTIAL;

while( TRUE )
{
// Loop forever accepting connections from clients
// until console shuts down.
SocketAccept = WSAAccept(pThis->m_SocketListen, NULL,
NULL, NULL, 0);
if( SocketAccept == SOCKET_ERROR )
{
return -1;
}

// we add the just returned socket descriptor to
// the IOCP along with its associated key data.
// also added context structures to a global list.
lpPerSocketContext = pThis->UpdateCompletionPort(SocketAccept,
IOCPIORead, TRUE);
if( lpPerSocketContext == NULL )
return -1;

// post initial receive on this socket
nRet = WSARecv( SocketAccept,
&(lpPerSocketContext->IOContext.wsaInBuf),
1, &dwRecvNumBytes, &dwFlags,
&(lpPerSocketContext->IOContext.Overlapped),
NULL );

int error = WSAGetLastError();
if( nRet == SOCKET_ERROR
&& (ERROR_IO_PENDING != WSAGetLastError()) )
{
pThis->ClientContentClose( lpPerSocketContext, FALSE );
}
} //while

return 0;
}

DWORD WINAPI CIOCPServerTCP::ThreadPoolFunc( LPVOID WorkContext )
{
CIOCPServerTCP* pThis = reinterpret_cast<CIOCPServerTCP*>(WorkContext);
HANDLE hIOCP = pThis->m_hIOCP;

SIOCPPerSocketTCPContext* lpPerSocketContext = NULL;
SIOCPPerIoTCPContext* llpIOContext = NULL;
LPWSAOVERLAPPED lpOverlapped = NULL;

SPacketServerRecvTCP* lpPacketTCPTemp = NULL;

BOOL bSuccess = FALSE;
int nRet = 0;

DWORD dwRecvNumBytes = 0;
DWORD dwSendNumBytes = 0;
DWORD dwFlags = 0;
DWORD dwIoSize = 0;

uint4 nDataSize=0;
SPacketServerRecvTCP *lpPacketTCP=NULL;


while( true )
{
// continually loop to service io completion packets
bSuccess = GetQueuedCompletionStatus( hIOCP, &dwIoSize,
(LPDWORD) &lpPerSocketContext,
if( !lpPerSocketContext )
continue;

// determine what type of IO packet has completed
llpIOContext = (SIOCPPerIoTCPContext*)lpOverlapped;
if( !llpIOContext )
continue;

switch( llpIOContext->IOOperation )
{
// a read operation has completed
case IOCPIORead:
// 注意,连接突然中断时要从等待接收队列中去除该等待项
if( dwIoSize == 0 )
{
// 去除该等待项并关闭连接
pThis->m_pTCPSocketIDToPacketRecvTCPMapWait->RemoveKey( lpPacketTCPTemp->nSocketID );
pThis->ClientContentClose( lpPerSocketContext, FALSE );
continue;
}

// 接收数据处理

// continue to recv
nRet = WSARecv( lpPerSocketContext->Socket,
&(lpPerSocketContext->IOContext.wsaInBuf),
1, &dwRecvNumBytes, &dwFlags,
&(lpPerSocketContext->IOContext.Overlapped),
NULL );
if( nRet == SOCKET_ERROR
&& (ERROR_IO_PENDING != WSAGetLastError()) )
{
// 关闭连接
pThis->ClientContentClose(lpPerSocketContext, FALSE);
}
break;
// a write operation has completed
case IOCPIOWrite:

if( dwIoSize <= 0 )
{
// 释放内存
if( lpPerSocketContext->IOContext.wsaOutBuf.buf )
{
// 释放发送空间
pThis->m_pMemoryPoolLinearMul->PutMem( lpPerSocketContext->IOContext.wsaOutBuf.buf,
lpPerSocketContext->IOContext.wsaOutBuf.len );
}
if( nRet != 0 )
{
// 去除该等待项
pThis->m_pTCPSocketIDToPacketRecvTCPMapWait->RemoveKey( lpPacketTCPTemp->nSocketID );
}
// 关闭连接
pThis->ClientContentClose( lpPerSocketContext, FALSE );
continue;
}
else
{
// 发送完成

}

break;

default:
break;
} //switch
} //while

return 0;
}

SIOCPPerSocketTCPContext* CIOCPServerTCP::UpdateCompletionPort(SOCKET sd,
IOCP_IO_OPERATION ClientIo, BOOL bAddToList)
{
SIOCPPerSocketTCPContext* lpPerSocketTCPContext = NULL;

lpPerSocketTCPContext = ClientContentAlloc(sd, ClientIo);
if( lpPerSocketTCPContext == NULL )
return(NULL);

// 初始化设备上下文
ZeroMemory( lpPerSocketTCPContext, sizeof(SIOCPPerSocketTCPContext) );

// 将连接的socket与对应的设备上下文与完成端口句柄关联起来。
HANDLE hIOCPTemp = NULL;
hIOCPTemp = CreateIoCompletionPort( (HANDLE)sd,
m_hIOCP, (DWORD )lpPerSocketTCPContext, 0 );
if( !hIOCPTemp )
{
ClientContentFree( lpPerSocketTCPContext );
return (NULL);
}

// 获得TCP连接的唯一编号
bool bContinue = true;
int nSocketID = 0;
SIOCPPerSocketTCPContext* lpTempContext;
while( bContinue )
{
nSocketID = m_rSysCoreServerStatus.GetTCPSocketID();
bContinue = m_pTCPSocketIDToTCPContextMap->Lookup( nSocketID, lpTempContext );
}

lpPerSocketTCPContext->nSocketID = nSocketID;
lpPerSocketTCPContext->Socket = sd;
lpPerSocketTCPContext->IOContext.wsaInBuf.buf = lpPerSocketTCPContext->IOContext.szBuffer;
lpPerSocketTCPContext->IOContext.wsaInBuf.len = sizeof( lpPerSocketTCPContext->IOContext.szBuffer );
lpPerSocketTCPContext->IOContext.IOOperation = IOCPIORead;

// 将其加入TCP连接编号与设备上下文映射
if( bAddToList )
m_pTCPSocketIDToTCPContextMap->SetAt( lpPerSocketTCPContext->nSocketID, lpPerSocketTCPContext );

return lpPerSocketTCPContext;
}

// Close down a connection with a client.
bool CIOCPServerTCP::ClientContentClose ( SIOCPPerSocketTCPContext* lpPerSocketContext,
BOOL bGraceful )
{
// 取消所有IO操作
CancelIo( (HANDLE)lpPerSocketContext->Socket);

// 从客户连接链表中删除该项
m_pTCPSocketIDToTCPContextMap->RemoveKey( lpPerSocketContext->nSocketID );

// 关闭连接
closesocket( lpPerSocketContext->Socket );
lpPerSocketContext->Socket = INVALID_SOCKET;

// 释放内存
ClientContentFree( lpPerSocketContext );

return true;
}

// 客户信息结构分配
SIOCPPerSocketTCPContext* CIOCPServerTCP::ClientContentAlloc(SOCKET sd,
IOCP_IO_OPERATION ClientIO)
{
SIOCPPerSocketTCPContext* lpTemp = new SIOCPPerSocketTCPContext();
memset( lpTemp, 0, sizeof(SIOCPPerSocketTCPContext) );
return lpTemp;
}

ttkttkttk 2004-09-08
  • 打赏
  • 举报
回复
提供比较完整的代码:

bool CIOCPServerTCP::InitializeIOCP()
{
DWORD i;
DWORD nThreadID;
SYSTEM_INFO systemInfo;

// 创建完成端口
SOCKET s;

s = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
if ( s == INVALID_SOCKET )
return false;

m_hIOCP = CreateIoCompletionPort( (HANDLE)s, NULL, 0, 0 );
if ( m_hIOCP == NULL )
{
closesocket( s );
return false;
}
// Close the socket, we don't need it any longer.
closesocket( s );

// create the work thread
// Determine how many processors are on the system.
GetSystemInfo( &systemInfo );
m_ProcessorNum = (short) systemInfo.dwNumberOfProcessors;
UINT nWorkerCnt = m_ProcessorNum * 2;

// We need to save the Handles for Later Termination
HANDLE hWorker = NULL;

for ( i = 0; i < nWorkerCnt; i++ )
{
hWorker = (HANDLE)CreateThread( NULL, 0,// Stack size - use default
ThreadPoolFunc, // Thread fn entry point
(void*) this, // Param for thread
0, // Init flag
&nThreadID); // Thread address
if (hWorker == NULL )
{
return false;
}
CloseHandle(hWorker);
}

return true;
}

bool CIOCPServerTCP::CreateListenSocket()
{
int nRet;

m_SocketListen = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
if (m_SocketListen == INVALID_SOCKET)
{
return false;
}

// bind the server
SOCKADDR_IN saServer;

saServer.sin_family = AF_INET;
saServer.sin_port = htons(m_nPort);
saServer.sin_addr.s_addr = INADDR_ANY;

// bind our name to the socket
nRet = bind( m_SocketListen,
(LPSOCKADDR)&saServer,
sizeof(struct sockaddr) );
if (nRet == SOCKET_ERROR)
{
closesocket(m_SocketListen);
return false;
}

// Set the socket to listen
nRet = listen(m_SocketListen, m_MaxConnections);
if (nRet == SOCKET_ERROR)
{
closesocket(m_SocketListen);
return false;
}

// beging the listening thread
DWORD dwThreadId = 0;

m_hThread = (HANDLE)CreateThread( NULL, // Security
0, // Stack size - use default
ListenThreadProc, // Thread fn entry point
(void*) this,
0, // Init flag
&dwThreadId); // Thread address
if (m_hThread == INVALID_HANDLE_VALUE)
{
return false;
}

return true;
}
加载更多回复(32)

18,356

社区成员

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

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