问题1:在完成例程的回调函数CALLBACK CompletionRoutine 内,如何通过回调参数 Overlapped 来判断是哪个套接字发生了IO操作?我看了几个例子,好像通常是自己定义一个结构体然后把WSAOVERLAPPDE放在第一的位置,然后在回调函数内强制转换,比如:// 这里是定义的结构体
typedef struct _SOCKET_INFORMATION 
{
WSAOVERLAPPED Overlapped;  // 重叠结构 完成例程中此结构一定要放在最前面
CHAR cpIP[16];       
CHAR Buffer[DATA_BUFSIZE];
WSABUF DataBuf;
SOCKET s;
}SOCKET_INFORMATION,*LPSOCKET_INFORMATION;
// CALLBACK CompletionRoutine 的第一行进行强制转换
LPSOCKET_INFORMATION SI = (LPSOCKET_INFORMATION)Overlapped; 
我不理解的是:为什么在回调函数内这样强制转化就能够得到是哪个套接字发生了IO操作呢?请大家帮我理解一下这里的指针操作!谢谢!
问题2:接上述问题,我是将自己定义的这个结构体换成了一个自己的用户类,CXEDUser,类里也定义了WSAOVERLAPPED结构体,而且也如结构体一样,定义了成员变量sock, flags, dwRecvBytes,等等。并且也把overlapped放到成员变量的第一位,然后用类的指针,按照结构体的方法强制转换,但是得到了一个颠倒的结果,overlapped变成了dwRecvBytes, dwRecvBytes变成了sock,也就是所取到的值和变量的名称不是对应的了,是错乱的,估计是指针进行强制转化的时候出了问题,请问要如何操作才能像结构体那样成功转化呢?代码:// CXEDUser 类的头文件
class CXEDUser  
{
public:
WSAOVERLAPPED m_Overlapped;
UINT m_uPort;
LPCTSTR m_lpIPAddress;
SOCKET m_UserSock;
WSAEVENT m_Event;
WSABUF m_DataBuf;
DWORD m_dwRecvBytes, m_dwFlags;
CXEDUser(SOCKET UserSock, LPCTSTR lpIPAddress, UINT uPort);
CXEDUser();
virtual ~CXEDUser();};// 在回调函数中的强制转化
CXEDUser* pUser = (CXEDUser*)Overlapped;
TRACE("%d\n", pUser->m_Overlapped);// 这里输出的结果变成了发生IO操作的字节数,也就是dwRecvBytes
非常感谢

解决方案 »

  1.   

    问题一:
    你调用一个函数,比如WSARecv,其需要的是一个WSAOVERLAPPED指针,在回调函数里,你得到的也是这个WSAOVERLAPPED指针(它们指向同一片内存),现在比如你想得到cpIP,则它就在从WSAOVERLAPPED指针开始的,sizeof(WSAOVERLAPPED)之后的位置上。而把WSAOVERLAPPED指针强转为_SOCKET_INFORMATION指针,是为了书写上的方法,直接->cpIP就取到了,不强转也是做得到的,就麻烦了,你还需要自己去在内存中移动指针。
    相反,如果WSARecv需要一个WSAOVERLAPPED参数,则它就是按值传递的,那么在回调函数里,就无法通过强转得到_SOCKET_INFORMATION,因为传值的时候,只传了WSAOVERLAPPED,后面的_SOCKET_INFORMATION多出来的部分并没有拷贝。
    这说白了就是传指针、传引用与传值的区别了,你的c++知识严重不足。问题二:
    因为你有一个~CXEDUser()虚函数,造成了类大小的改变(增加了虚表),那么CXEDUser的地址就不再与成员m_Overlapped地址重合,那么强转就是错误的。
    你可以把virtual去掉,应该就可以了(但去掉析构函数的virtual并不标准,不推荐)。建议:
    从WSAOVERLAPPED继承才是最好的解决办法,比那个struct强,你自己慢慢领会:
    class CXEDUser : public WSAOVERLAPPED
    {
    public:
        UINT            m_uPort;
        LPCTSTR            m_lpIPAddress;
        SOCKET            m_UserSock;    
        WSAEVENT        m_Event;
        WSABUF            m_DataBuf;
        DWORD            m_dwRecvBytes, m_dwFlags;
        CXEDUser(SOCKET UserSock, LPCTSTR lpIPAddress, UINT uPort);
        CXEDUser();
        virtual ~CXEDUser();
    };使用时:
    CXEDUser* user = new(...);
    WSARecv(..., user);回调时:
    CXEDUser* user = (CXEDUser*) par;
      

  2.   

    虚表肯定要出现在最前面的位置,所以你写到后面也用。如果非要用虚表,那么在把WSAOVERLAPPED*强转为CUser*的时候,要如下处理:CUser* pUser = (CUser*) ((UINT_PTR) lop - sizeof(UINP_PTR));
    这个公式同样可以用于你上面的CXEDUser,保持CXEDUser不变,在回调函数里面加上面的代码。
      

  3.   

    实在是大神级别的回答~~大神直接指出了我的问题,因为是自学,而且是初学,所有很多基础概念比较模糊,对指针、内存、堆栈的理解都很片面,甚至有很多错误~~~基础没打好,写东西都是边学边BAIDU,很艰难啊~!不知大神能在百忙中留个QQ给于指点么?
    希望大神给于帮助!!!上分了!