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 第二条客户端没有接到
没有出现沾包情况 客户端只接到第一个包的完整信息
...全文
699 25 打赏 收藏 转发到动态 举报
AI 作业
写回复
用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)

18,363

社区成员

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

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