iocp 连续调用WSASend问题

bearxiongying 2010-04-12 01:57:46
我自己写了个iocp程序 但是在连续给同个客户端发包 造成第二个包没发出去
不知道如何处理
本人主要代码如下
DWORD WINAPI ServerThreadFunc(LPVOID lpParam)
{
CHuanshouDlg *lp = (CHuanshouDlg *)lpParam;
WSADATA wsaData;
HANDLE hCompPort;
DWORD ThreadID;
DWORD Ret;
if ((Ret = WSAStartup(0x0202, &wsaData)) != 0)
{
AfxMessageBox("WSAStartup failed with error!");
return 0;
}
if ((hCompPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0)) == NULL)
{
AfxMessageBox("CreateIoCompletionPort failed with error!");
return 0;
}
SYSTEM_INFO SystemInfo;
GetSystemInfo(&SystemInfo);
unsigned cpu = SystemInfo.dwNumberOfProcessors*2;
for(unsigned int i=0; i < cpu; i++)
{
HANDLE ThreadHandle;
lpthreadParam p = new _THREADPARAM;
p->cpid = hCompPort;
p->lp = lp;
//if ((ThreadHandle = CreateThread(NULL, 0, WorkThread, hCompPort, 0, &ThreadID)) == NULL)
if ((ThreadHandle = CreateThread(NULL, 0, WorkThread, p, 0, &ThreadID)) == NULL)
{
AfxMessageBox("CreateThread() failed with error");
return 0;
}
CloseHandle(ThreadHandle);
}
SOCKET ListenSocket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, NULL, WSA_FLAG_OVERLAPPED);
SOCKADDR_IN ServerAddr;
ServerAddr.sin_family = AF_INET;
ServerAddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
ServerAddr.sin_port = htons(8888);
bind(ListenSocket,(LPSOCKADDR)&ServerAddr,sizeof(ServerAddr));
listen(ListenSocket,100);

SOCKADDR_IN ClientAddr;
int addr_length=sizeof(ClientAddr);
while (TRUE)
{
LPSOCKET_INFORMATION SI = new SOCKET_INFORMATION();
if ((SI->Socket = WSAAccept(ListenSocket,(SOCKADDR*)&ClientAddr, &addr_length,NULL,0)) != INVALID_SOCKET)
{
//初始化一个新的用户信息
memset(&SI->Overlapped,0,sizeof(WSAOVERLAPPED));
memset(SI->buffer, 0, DATA_BUFSIZE);
SI->RecvDataBuf.buf = SI->buffer;
SI->RecvDataBuf.len = DATA_BUFSIZE;
SI->SendDataBuf.buf = SI->buffer;
SI->SendDataBuf.len = DATA_BUFSIZE;
SI->IoType = IORECV;
SI->ClientName = "null";
SI->firstlog = true;
InitializeCriticalSection(&SI->single_cs);

if (CreateIoCompletionPort((HANDLE)SI->Socket, hCompPort, (DWORD)SI, 0) == NULL)
{
AfxMessageBox("CreateIoCompletionPort failed with error ");
return 0;
}
//send a I/O request
if(WSARecv(SI->Socket, &SI->RecvDataBuf, 1, &Bytes, &Flags, &SI->Overlapped, NULL) == SOCKET_ERROR)
{
if(WSAGetLastError() != WSA_IO_PENDING)
{
AfxMessageBox("disconnect\n");
shutdown(SI->Socket,SD_SEND);
closesocket(SI->Socket);
delete SI;
continue;
}
}
}
}
return 0;
}

/// <summary>
/// I/O工作线程
/// </summary>
DWORD WINAPI WorkThread(LPVOID CompletionPortID)
{
//HANDLE hCompPort = (HANDLE)CompletionPortID;
lpthreadParam p = (lpthreadParam)CompletionPortID;
HANDLE hCompPort = (HANDLE)p->cpid;
CHuanshouDlg *lp = (CHuanshouDlg *)p->lp;
while (TRUE)
{
DWORD BytesTransferred = 0;
LPSOCKET_INFORMATION SI = NULL;
LPWSAOVERLAPPED Overlapped = NULL;

//Threads pool,waiting for awake
BOOL t = GetQueuedCompletionStatus(hCompPort, &BytesTransferred, (LPDWORD)&SI, &Overlapped, INFINITE);
if (t)
{
EnterCriticalSection(&SI->single_cs);
switch(SI->IoType)
{
case IORECV:
{

if (0 == BytesTransferred)
{
if(!SI->firstlog)
{
lp->QuitGame(SI->Socket,SI->ClientName);
}
continue;
}
CString message,msg2;
message = SI->RecvDataBuf.buf;
if(message == "")
{
memset(SI->buffer, 0, DATA_BUFSIZE);
SI->RecvDataBuf.len = DATA_BUFSIZE;
SI->RecvDataBuf.buf = SI->buffer;
SI->IoType = IORECV;
if (WSARecv(SI->Socket, &SI->RecvDataBuf, 1, &Bytes, &Flags, &SI->Overlapped, NULL) == SOCKET_ERROR)
{
if(WSAGetLastError() != WSA_IO_PENDING)
{
lp->QuitGame(SI->Socket,SI->ClientName);
delete SI;
continue;
}
}
continue;
}
int f = -1;
f = message.Find("<policy-file-request/>\0");
if(f >= 0)
{
CString xml = "<cross-domain-policy>";
xml = xml + "<site-control permitted-cross-domain-policies=\"all\"/>";
xml = xml + "<allow-access-from domain=\"*\" to-ports=\"8888\" />";
xml = xml + "</cross-domain-policy>\0";

sprintf(SI->SendDataBuf.buf,"%s",xml);
SI->SendDataBuf.len = strlen(xml) + 1;
SI->IoType = IOSEND;
//发送策略文件
if (WSASend(SI->Socket, &SI->SendDataBuf, 1, &Bytes, Flags, &SI->Overlapped, NULL) == SOCKET_ERROR)
{
if(WSAGetLastError() != WSA_IO_PENDING)
{
shutdown(SI->Socket,SD_SEND);
closesocket(SI->Socket);
delete SI;
continue;
}
}
}
else
{
CMarkup xml = CMarkup();
xml.SetDoc(message);

CString act;
CString item;
CString condition;
item = "msg";
condition = "act";
if(xml.FindChildElem(item))
{
xml.IntoElem();
act = xml.GetAttrib(condition);
}else{
AfxMessageBox("格式错误:" + SI->ClientName+"->" + message);
continue;
}
char* act2 = act.GetBuffer(act.GetLength());
switch(actMap[act2])
{
case login:
{
condition = "username";
CString username;
username = xml.GetAttrib(condition);

//踢掉同名用户
map<CString, LPSOCKET_INFORMATION>::iterator iter;
EnterCriticalSection(&clients_cs);
iter = clients.find(username.GetBuffer());
if(iter != clients.end())
{
LPSOCKET_INFORMATION oldlp = LPSOCKET_INFORMATION(iter->second);
if(SI->Socket != oldlp->Socket)
{
shutdown(LPSOCKET_INFORMATION(iter->second)->Socket,SD_SEND);
closesocket(LPSOCKET_INFORMATION(iter->second)->Socket);
}
clients.erase(iter);
}
SI->ClientName = username;
SI->firstlog = false;
SI->IoType = IOSEND;
clients[username] = SI;
LeaveCriticalSection(&clients_cs);
lp->loginGame(username);

//接到数据后向客户端发同意登录信息
CString _msg = "<root act='login' />\0";
SI->SendDataBuf.len = _msg.GetLength() + 1;
SI->SendDataBuf.buf = _msg.GetBuffer(_msg.GetLength());
SI->IoType = IOSEND;
if (WSASend(SI->Socket, &SI->SendDataBuf, 1, &Bytes, Flags, &SI->Overlapped, NULL) == SOCKET_ERROR)
{
if(WSAGetLastError() != WSA_IO_PENDING)
{
lp->QuitGame(SI->Socket,SI->ClientName);
delete SI;
continue;
}
}
break;
}
default:
{
break;
}
}
}
break;
}
case IOSEND:
{
CString num;
num.Format("%d",BytesTransferred);
lp->m_log += "IOSEND:"+ num +"\r\n";
lp->SendMessage(UPDATA_MESSAGE);
memset(SI->buffer, 0, DATA_BUFSIZE);
SI->RecvDataBuf.len = DATA_BUFSIZE;
SI->RecvDataBuf.buf = SI->buffer;
SI->IoType = IORECV;
if (WSARecv(SI->Socket, &SI->RecvDataBuf, 1, &Bytes, &Flags, &SI->Overlapped, NULL) == SOCKET_ERROR)
{
if(WSAGetLastError() != WSA_IO_PENDING)
{
lp->QuitGame(SI->Socket,SI->ClientName);
delete SI;
continue;
}
}
break;
}
default:
break;
}
LeaveCriticalSection(&SI->single_cs);
}
}
return FALSE;
}

bool CHuanshouDlg::SendMessageToOneByName(CString msg,CString username)
{
EnterCriticalSection(&clients_cs);
map<CString, LPSOCKET_INFORMATION>::iterator iter;
LPSOCKET_INFORMATION lp;
iter = clients.find(username.GetBuffer());
if(iter == clients.end())
{
LeaveCriticalSection(&clients_cs);
return false;
}
lp = (LPSOCKET_INFORMATION)iter->second;
lp->SendDataBuf.len = msg.GetLength() + 1;
lp->SendDataBuf.buf = msg.GetBuffer(msg.GetLength());
lp->IoType = IOSEND;
Flags = 0;
if (WSASend(lp->Socket, &lp->SendDataBuf, 1, &Bytes, Flags, &lp->Overlapped, NULL) == SOCKET_ERROR)
{
shutdown(lp->Socket,SD_SEND);
closesocket(lp->Socket);
clients.erase(iter);
LeaveCriticalSection(&clients_cs);
return false;
}
LeaveCriticalSection(&clients_cs);
return true;
}
/发送系统消息按钮
void CHuanshouDlg::OnBnClickedSend()
{

SendMessageToOneByName("test11\0","bear");
Sleep(100);
SendMessageToOneByName("test2\0","bear");

}

很奇怪,只要在发送的时候 Sleep(100);就成功
没有sleep 第二条客户端没有接到
没有出现沾包情况 客户端只接到第一个包的完整信息
...全文
665 25 打赏 收藏 转发到动态 举报
写回复
用AI写文章
25 条回复
切换为时间正序
请发表友善的回复…
发表回复
尹成 2010-04-25
  • 打赏
  • 举报
回复
学习,帮顶
tech_study_00 2010-04-23
  • 打赏
  • 举报
回复
还停电?

为什么要互斥发送?
冷月清晖 2010-04-23
  • 打赏
  • 举报
回复
[Quote=引用 12 楼 tech_study_00 的回复:]
做游戏开发的MM,不简单娜!
[/Quote]
你怎么知道这个照片不是他女朋友的呢?

完成端口,你需要理解它该怎么用。(我也初学)
但提供些个人理解:你的流程是顺序的吧。
a的结果更新了服务端,服务端更新b。b的结果更新了服务端,服务端更新b。
这样就清楚了。
a完成之后,服务端更新c。b完成之后,服务端更新c。如果这时候c更新未完成,等待c完成再更新c。
bearxiongying 2010-04-22
  • 打赏
  • 举报
回复
停了两天电 郁闷了
次发送的时候,都新创建一个overlapped
而且SendDataBuf之类的都放在了 overlapped那个结构中还是不管用
好像给每个客户端发消息都互斥的话 很影响性能呢
whs1980 2010-04-21
  • 打赏
  • 举报
回复
这个,需要引入互斥的机制了,...
bearxiongying 2010-04-20
  • 打赏
  • 举报
回复
[Quote=引用 16 楼 whs1980 的回复:]
SendMessageToOneByName("test11\0","bear");
SendMessageToOneByName("test2\0","bear");
你这是对同一个用户发送的两条信息,这样肯定有问题了.因为:
IOCP里的WSASEND是非阻塞的,也就是说,只要调用WSASEN后立即返回,而不管是否已经发送出去.但,要发送的内容仍然保存在SendDataBuf里.这时,如……
[/Quote]
说的有道理
其实我具体问题是这样的 三个客户端 a b c
a做了个操作影响了c 需要服务器给c发包
b几乎同时做了个操作 需要服务器给c发包
这两个包几乎同时 就造成了第二个包没有发出去
所以这两包不好合成一起发
那怎么决定第一个包发完了 才发第二个包呢
指点下~~
yhlovehx 2010-04-20
  • 打赏
  • 举报
回复
完成端口发送数据不建议一次投递超过一个以上的WSASend的操作
因为WSASend操作可以导致数据 一次没有发送完
这样数据就会乱套 逻辑处理上会有很多问题

应该建立一个发送队列发送的数据直接投递到队列里头 发送引擎 直接读取队列就行

接收的话可以一次投递多个recv操作 没什么问题
奉海 2010-04-19
  • 打赏
  • 举报
回复
1.用同一个overlapped连续发送,需要在GetQueuedCompletionStatus获得上一次发送完成后才有意义,但这样就失去了完成端口异步IO的高效性。
2.每发一次数据用不同的overlapped,这样发送数据性能最佳。overlapped最好用内存池,避免不停的new ,delete.
whs1980 2010-04-19
  • 打赏
  • 举报
回复
SendMessageToOneByName("test11\0","bear");
SendMessageToOneByName("test2\0","bear");
你这是对同一个用户发送的两条信息,这样肯定有问题了.因为:
IOCP里的WSASEND是非阻塞的,也就是说,只要调用WSASEN后立即返回,而不管是否已经发送出去.但,要发送的内容仍然保存在SendDataBuf里.这时,如果你要再次发送,按照你上面的写发,又后去改写SendDataBuf里的内容,所以发送有问题了.sleep(100)后,上次的内容才真正发送出去,所以再次发送也没有问题.
有两种方法解决这个问题:
(1)在确认第一次发送成功后,再发第二次
(2)把发给同一个客户端的内容包装在一起,一次发送.
(3)把这两种方式组合在一起使用,效果更好.
lijianli9 2010-04-19
  • 打赏
  • 举报
回复
16,17楼的解释有理
bearxiongying 2010-04-17
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 shfhere 的回复:]
要么每次发送的时候,都新创建一个overlapped,要么检查上次发送完成才继续发送
[/Quote]

好像是这的问题 我先来试试
yuyunliuhen 2010-04-15
  • 打赏
  • 举报
回复
建议用下代码格式 看起来费力。。。
tech_study_00 2010-04-15
  • 打赏
  • 举报
回复
但是在连续给同个客户端发包 造成第二个包没发出去;

在你发送完一个包以后,你会得到一个完成发送的事件通知,告诉你发送了多少字节,
你查看一下发送完成的字节数和需要发送的字节数是不是相等?
否则需要继续发完未发完的数据。
tech_study_00 2010-04-15
  • 打赏
  • 举报
回复
做游戏开发的MM,不简单娜!
mtkcpp 2010-04-15
  • 打赏
  • 举报
回复
学习,帮顶
Eleven 2010-04-15
  • 打赏
  • 举报
回复
[Quote=引用 8 楼 scq2099yt 的回复:]
WSASend发送不出去是什么意思,失败了还是怎么了?如果是失败了,看看失败的原因WSAGetLastEorro().
[/Quote]
...
shfhere 2010-04-15
  • 打赏
  • 举报
回复
要么每次发送的时候,都新创建一个overlapped,要么检查上次发送完成才继续发送
dengsf 2010-04-14
  • 打赏
  • 举报
回复
[Quote=引用 4 楼 bearxiongying 的回复:]
不同的overlapped 具体怎么做啊
我在WSASend之前都加了memset(&lp->Overlapped,0,sizeof(WSAOVERLAPPED));
可还是老样子
[/Quote]
overlapped提交,但未从iocp队列得到结果前,不要修改里面的内容,如memset之类,会出问题。

你的情况,overlapped跟玩家结构绑定的,一般同时只能有一个overlapped操作。
若要发送数据,但前一个send未有结果前,可以先将数据缓存起来,等有结果再继续send。

想有数据就send,则不应绑定到玩家信息结构中。应每次投递都new个新的overlapped结构,得到结果并处理后就delete之。
scq2099yt 2010-04-14
  • 打赏
  • 举报
回复
WSASend发送不出去是什么意思,失败了还是怎么了?如果是失败了,看看失败的原因WSAGetLastEorro().
bearxiongying 2010-04-12
  • 打赏
  • 举报
回复
还在等答案呢 呜呜~~
加载更多回复(5)
结构层次及相互联系 (1)、工作线程:响应连接的IO投递返回并负责投递读请求,并将IO返回结果投递给处理线程,可设定参数决定工作线程数量; (2)、处理线程:处理线程调用回调函数将信息传递给应用层或协议栈,可设定参数决定工作处理数量; (3)、看守线程:响应Accept事件调用AcceptEx,检测连接和心跳超时 ,将信息投递给工作线程,模块仅有一个看守线程。 1. 技术要求 (1)、线程同步:Lock指令、临界段; (2)、主要Socket API:WSASend、WSARecv、AcceptEx、DisconnectEx; (3)、内存管理:连接池(句柄重用)、内存池; (4)、数据0拷贝:通过内置处理线程,上层应用可以避免自建线程池及复制数据的过程。同时提供GBuf内存分配功能,应用层获得分配地址及填充数据之后亦可直接投递给内核/驱动层; (5)、数据顺序同步:同一个连接同时只有一个处理线程响应其IO事件; (6)、IO请求投递:单投递读、多投递写; (7)、0缓冲读投递:可条件编译实现,以适用大规模连接要求。 (8)、超时机制:可设置空连接(连接不发送数据)超时时间以防止DOS攻击,也可设置心跳超时时间防止网络故障导致的现有连接成为虚连接避免耗尽系统资源。 (9)、接口技术:API、回调函数、客户句柄(客户连接句柄)。 (10)、主、被动发送:不使用HASH、MAP及LIST技术,即可提供安全可靠高效的客户连接句柄,以实现服务器端主被动发送数据功能; (11)、PerHandleData的回收不以IO投递的计数器或链表来做依据但仍能安全回收,同时尽量避免在高频的读写操作时做其他无关的操作以提高读写效率。 (12)、处理线程和工作线程有着良好分工界限,繁重的工作交给处理线程完成,工作线程工作量最大限度的减少,仅响应投递返回及读投递的操作; (13)、支持AWE,模块自动识别AWE是否开启(需手动开启),“否”则使用虚拟内存机制。 2. 功能要求 (1)、多IP多端口监听,每个监听可设置不同的回调函数,以高效的区别处理数据 (2)、可设置每秒最大的连接并发量和空连接(连接不发数据)超时时间以防止DOS攻击造成的服务瘫痪、具有心跳处理(防网络异常造成的虚连接)功能 (3)、不加协议的透明传输,可适用广泛的网络通讯环境 (4)、可现实主、被动发送数据,但不会因兼顾主动发送而额外增加降低效率的工作 (5)、内置处理线程,上层应用可不必自建线程池处理数据,所有IO事件按顺序调用回调函数并可以在回调函数内直接处理数据,不必担心多线程造成的接收数据乱序的问题。 (6)、高效率的数据对应关联机制,在初次连接并根据登录数据设置每个连接对应的宿主(Owner)之后,再接收的数据即可立即获得该连接对应的宿主,而不必再做额外的查询工作,并且模块内部采用的是指针关联方式,对于长连接、主动发送的服务器系统而言是高效率的。 (7)、可兼容IPv6 3. 注意事项 因硬件环境和应用环境不同,不合理的配置会出现效率及性能上的问题,因此以下情况出现时,请务必与作者联系以确保获得更好的参数配置: (1)、连接量超过1000个的。超过的应结合具体硬件配置和网络带宽等因素综合设定运行参数。 (2)、带宽使用率超过20%的。工作线程和处理线程数量的设置也是综合考虑数据吞吐量和数据处理负载的因素来设置的,过多的线程会在调度上浪费时间,同时也应该综合考虑线程优先级别来设置工作线程和处理线程数量,两者的设置也不一定能相等。 (3)、服务器端有主动发送需求的、短连接(含网络故障造成的连接断开)出现频率高的。 压力测试工具介绍: 一、 使用G-TcpClient模块 二、 可以设定间隔时间发起大规模长、短连接 三、 可以发起密集数据包,包括即时和定时发送,1M的光纤带宽最大可以达到100K/S(单向)以上,100M本地网最大可以达到10M/S(单向)以上 四、 数据发送仅由一个独立线程但当,每点击一次Connect就创建一个线程根据当前参数发起连接。 五、 测试前提:服务器接收客户端数据后立即原样返回给客户端

18,355

社区成员

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

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