IStream COM 读取到缓冲区然后通过 winsock 发送

IStream COM read to buffer then send through winsock

我通过 winsock2 创建了一个 server/client。其中客户端截取bmp截图,压缩成jpeg发送给服务器。我遇到的问题是服务器在将文件保存到磁盘时遇到问题。就像数据没有正确接收一样。我认为这与我发送或接收缓冲区的方式有关。

编辑:从调试期间的设置 breakpoints 看来好像所有数据都已收到。 istream->Write returns 正确的字节数与 Sizeistream->Seek returns S_OKCLSID COM 对象包含相同发送的数据。同样在我发送缓冲区之前,我测试它以查看它是否保存并且它确实保存了。因此,我假设它必须是发送或接收 Buffer 的方式。

编辑: 在两端,缓冲区看起来像 This

屏幕截图加发送代码。

    ScreenShot(Packet_Handler* _Handle)
{
IStream* istream;
    //char* Buff;
    HRESULT res = CreateStreamOnHGlobal(NULL, true, &istream);
    Gdiplus::GdiplusStartupInput gdiInput;
    ULONG_PTR gdiToken;
    Gdiplus::GdiplusStartup(&gdiToken, &gdiInput, NULL);
    {
        HDC scrdc, memdc;
        HBITMAP membit;
        scrdc = ::GetDC(0);
        int Height = GetSystemMetrics(SM_CYSCREEN);
        int Width = GetSystemMetrics(SM_CXSCREEN);
        memdc = CreateCompatibleDC(scrdc);
        membit = CreateCompatibleBitmap(scrdc, Width, Height);
        HBITMAP hOldBitmap = (HBITMAP)SelectObject(memdc, membit);
        BitBlt(memdc, 0, 0, Width, Height, scrdc, 0, 0, SRCCOPY);

        Gdiplus::Bitmap bitmap(membit, NULL);
        CLSID clsid;
        GetEncoderClsid(L"image/jpeg", &clsid);


        bitmap.Save(istream, &clsid, NULL);
        bitmap.Save(L"Test.jpeg", &clsid, NULL); //THIS WORKS
        STATSTG pstatstg = { 0 };
        istream->Stat(&pstatstg, 0);
        ULONG Bytes;
        char* isBuff = new char[pstatstg.cbSize.QuadPart];
        LARGE_INTEGER li;
        li.QuadPart = 0;
        istream->Seek(li, STREAM_SEEK_SET, NULL);
        if (istream->Read(isBuff, pstatstg.cbSize.QuadPart, &Bytes) == S_OK)
        {
            IStream* nistream = nullptr;
            ULONG nBytes;
            CreateStreamOnHGlobal(NULL, true, &nistream);
            if (nistream->Write(isBuff, pstatstg.cbSize.QuadPart, &Bytes) == S_OK)
            {

                if (nistream->Seek(li, STREAM_SEEK_SET, NULL) == S_OK)

                {
                    Gdiplus::Bitmap bitmap2(nistream, NULL);
                    bitmap2.Save(L"Testing.jpeg", &clsid, NULL); //THIS WORKS
                }
            }
        }

        int Size = pstatstg.cbSize.QuadPart;
        int SOLI = sizeof(ULARGE_INTEGER);
        int SOCID = sizeof(CLSID);
        int SOIS = sizeof(IStream);

        Send_Full_Packet(_Handle, &Size, isBuff, TRUE);
        Send_Full_Packet(_Handle, &SOCID, &clsid, FALSE);



        printf("saving Screenshot");


        DeleteObject(memdc);
        DeleteObject(membit);
        ::ReleaseDC(0, scrdc);

    }
    Gdiplus::GdiplusShutdown(gdiToken);
    _getch();
    return 0;
}
    void Send_Full_Packet(Packet_Handler* _Handle, int* pSize, void * Buff, bool ispchar)
    {
        int SentPackets = 0;
        int sCheck = 0;
        int rCheck = 0;
        int Size = *pSize;
        char* isBuff;// = new char[Size];

            sCheck = send(_Handle->ConnectSocket, (char*)&Size, sizeof(int), NULL);

        int PacketsSent = 0;
        sCheck = 0;
        int PacketsLeft = Size;
        while (PacketsSent < Size)
        {
            if (ispchar == FALSE)
            {
                SentPackets = send(_Handle->ConnectSocket, (char*)Buff + PacketsSent, PacketsLeft, NULL);
            }
            else {
                isBuff = (char*)Buff;
                SentPackets = send(_Handle->ConnectSocket, isBuff + PacketsSent, PacketsLeft, NULL);
            }
            if (SentPackets == SOCKET_ERROR)
            {
                if (WSAGetLastError() != 10054)
                {
                    if (WSAGetLastError() == 10035)
                    {
                        SentPackets == 0;

                    }
                    else
                    {
                        printf("Sending Socket error: %d\n", WSAGetLastError());
                        break;
                    }

                }
            }
            PacketsSent += SentPackets;
            PacketsLeft -= SentPackets;
        }


    }

接收码

    ScreenShot_Receive_Thread(Packet_Handler* _Handle)
{

    Packet_Handler::_Packet_Type Packet = Packet_Handler::_Packet_Type::Remote;
    while (true)
    {
        if (send(_Handle->si.Connections[_Handle->si.ConnectionCounter - 1], (char*)&Packet, sizeof(Packet_Handler::_Packet_Type), 0) != INVALID_SOCKET)
        {
            while (true)
            {
                int Size = 0;
                char* Buff = NULL;
                Buff = _Handle->Receive_Full_Packet(_Handle, &Size, Buff, TRUE);
                CLSID clsid;
                int SOCID = sizeof(CLSID);
                _Handle->Receive_Full_Packet(_Handle, &SOCID, &clsid, FALSE);
                IStream* istream = nullptr;
                ULONG Bytes;
                CreateStreamOnHGlobal(NULL, true, &istream);
                if (istream->Write(Buff, Size, &Bytes) == S_OK)
                {
                    LARGE_INTEGER li;
                    li.QuadPart = 0;
                    if (istream->Seek(li, STREAM_SEEK_SET, NULL) == S_OK)
                    {
                        Gdiplus::Bitmap bitmap(istream, NULL);
                        bitmap.Save(L"Testing.jpeg", &clsid, NULL);//DOESNT WORK
                    }
                }

            }
        }
    }
    MessageBox(NULL, L"CLient COnnection Error!", NULL, NULL);
    return 0;
}


  char* Packet_Handler::Receive_Full_Packet(Packet_Handler * _Handle, int* pSize, void * Buff, bool ispchar)
{
    int rCheck = 0;
    int sCheck = 0;
    int Size = *pSize;
    rCheck = recv(_Handle->si.Connections[_Handle->si.ConnectionCounter - 1], (char*)&Size, sizeof(int), 0);
    if (rCheck == -1)
    {
        int Error = WSAGetLastError();
        AllocConsole();
        freopen("CONOUT$", "w", stdout);
        std::cout << "The Error is: " << Error << std::endl;
    }
    *pSize = Size;
    char* isBuff;// = NULL;
    int PacketsReceived = 0;
    int PacketsLeft = Size;
    int ReceivedPackets = 0;
    while (PacketsReceived < Size)
    {
        if (ispchar == FALSE)
        {
            ReceivedPackets = recv(_Handle->si.Connections[_Handle->si.ConnectionCounter - 1], (char*)Buff + PacketsReceived, PacketsLeft, 0);

        }
        else
        {
            isBuff = new char[Size]; //I Think my problem is here// 

            ReceivedPackets = recv(_Handle->si.Connections[_Handle->si.ConnectionCounter - 1], isBuff + PacketsReceived, PacketsLeft, 0);

        }
        if (ReceivedPackets == SOCKET_ERROR)
        {
            int Error = WSAGetLastError();
            if (Error != 10054)
            {
                if (Error == 10035  || Error == 6)
                {
                    ReceivedPackets == 0;
                }
                else
                {
                    AllocConsole();
                    freopen("CONOUT$", "w", stdout);
                    std::cout << "The Error is: " << Error << std::endl;
                    //MessageBox(NULL, (WCHAR*)WSAGetLastError(), NULL, NULL);
                    _getch();
                    //return NULL;
                    break;
                }
            }
        }

        PacketsReceived += ReceivedPackets;
        PacketsLeft -= ReceivedPackets;
    }
    return isBuff;
}

数据可能需要多次 recv 调用才能完全接收。发生这种情况时,Receive_Full_Packet 会为每个此类调用分配一个新缓冲区,迅速泄漏除其中一个以外的所有缓冲区。它 returns 对调用者来说是最后一个这样的分配——一个充满随机垃圾的大部分未初始化的缓冲区,最后一块数据在它的最后。

isBuff = new char[Size]; 行移到循环外,将其放在第一个 recv 检索大小的调用之后。

终于想通了。事实证明,我发送缓冲区的方式绝对没有问题。它实际上是 CLSID。我最终将 GetEncoderClsid 函数放在了接收端。像黄油一样工作。