IStream COM 读取到缓冲区然后通过 winsock 发送
IStream COM read to buffer then send through winsock
我通过 winsock2 创建了一个 server/client。其中客户端截取bmp截图,压缩成jpeg发送给服务器。我遇到的问题是服务器在将文件保存到磁盘时遇到问题。就像数据没有正确接收一样。我认为这与我发送或接收缓冲区的方式有关。
编辑:从调试期间的设置 breakpoints
看来好像所有数据都已收到。 istream->Write
returns 正确的字节数与 Size
和 istream->Seek
returns S_OK
和 CLSID
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
函数放在了接收端。像黄油一样工作。
我通过 winsock2 创建了一个 server/client。其中客户端截取bmp截图,压缩成jpeg发送给服务器。我遇到的问题是服务器在将文件保存到磁盘时遇到问题。就像数据没有正确接收一样。我认为这与我发送或接收缓冲区的方式有关。
编辑:从调试期间的设置 breakpoints
看来好像所有数据都已收到。 istream->Write
returns 正确的字节数与 Size
和 istream->Seek
returns S_OK
和 CLSID
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
函数放在了接收端。像黄油一样工作。