void CCAsyncSocketDlg::OnBnClickedBtnSend() { UpdateData(TRUE); TCHAR ipstr[INET_ADDRSTRLEN]; DWORD dwRemoteIp = htonl(m_dwRemoteIp); InetNtop(AF_INET, &dwRemoteIp, ipstr, sizeof ipstr)); int nWrite = m_sockLocal.SendToEx(m_strSend.GetBuffer(), m_strSend.GetLength(), htons(m_uRemotePort), ipstr); }
void CCAsyncSocketDlg::OnReceive(int nErrorCode) { UpdateData(TRUE); TCHAR buf[4096]; int nRead; CString rAddr; UINT rPort; while (true) { nRead = m_sockRemote.ReceiveFromEx(buf, 4096, rAddr, rPort); if (nRead > 0) { buf[nRead] = 0; m_strRecv.Append(buf); } else { break; } } m_strRecv.Append(_T("\r\n")); UpdateData(FALSE);
上述代码是忽略了错误处理后的代码,其中Dlg类的OnReceive方法是给CAsyncSocket的派生类的OnReceive方法调用的(在此之前派生类保存了Dlg类的指针)。
由于是UDP套接字,所以不存在粘包问题,SendTo和RecvFrom的返回值是一样的(说明发送和接收的数据大小一样),那么后面部分怎么出错的呢?
经过多次输入测试,发现每次只有后半部分读取失败。解决方法如下
int nWrite = m_sockLocal.SendToEx(m_strSend, m_strSend.GetLength() * sizeof(TCHAR), htons(m_uRemotePort), ipstr);
buf[nRead / sizeof(TCHAR)] = 0;
替换对应位置即可,因为我是传递TCHAR数组(在Unicode下CString是基于TCHAR的,如果用char的话每次CA2W、CW2A还是挺麻烦的),而socket传递的是以字节为单位,对于包含N个字符的TCHAR数组,实际上传输字节数是N*sizeof(TCHAR),所以SendTo函数里第二个参数妖改成sizeof(TCHAR),但是对于接收缓冲区而言,缓冲区是个TCHAR数组,单位大小是sizeof(TCHAR)个字节而不是1个字节,所以计算下标时又要用nRead / sizeof(TCHAR)。
顺便TCHAR的字符串操作函数相当于就是把strxxx改成了_tcsxxx,比如标准C的strcpy对于TCHAR来说就是_tcscpy,这是微软对Unicode字符操作函数的扩展,见头文件tchar.h
以及inet_ntop等网络转换函数也变成了InetNtop,见头文件WS2tcpip.h。
这个类本身使用不难,但是由于书上包括文档有不少东西没说清楚,结果折腾了好久。顺带一提,它的一系列函数使用的IP也好,接口也好,竟然还是要用htons、htonl转换的,唉,真是无语。