CAsyncSocket只传输了一部分数据(UDP),后面是乱码

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("
"));
    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转换的,唉,真是无语。

原文地址:https://www.cnblogs.com/Harley-Quinn/p/6964705.html