[英]Sending and receiving compressed data over a TCP socket
在通過 TCP 套接字發送和接收壓縮數據方面需要幫助。
如果我不使用壓縮,代碼工作得很好,但是當我使用壓縮時會發生一些非常奇怪的事情..基本上,問題是 stream.Read() 操作被跳過,我不知道為什么..
我的代碼:
using (var client = new TcpClient())
{
client.Connect("xxx.xxx.xx.xx", 6100);
using (var stream = client.GetStream())
{
// SEND REQUEST
byte[] bytesSent = Encoding.UTF8.GetBytes(xml);
// send compressed bytes (if this is used, then stream.Read() below doesn't work.
//var compressedBytes = bytesSent.ToStream().GZipCompress();
//stream.Write(compressedBytes, 0, compressedBytes.Length);
// send normal bytes (uncompressed)
stream.Write(bytesSent, 0, bytesSent.Length);
// GET RESPONSE
byte[] bytesReceived = new byte[client.ReceiveBufferSize];
// PROBLEM HERE: when using compression, this line just gets skipped over very quickly
stream.Read(bytesReceived, 0, client.ReceiveBufferSize);
//var decompressedBytes = bytesReceived.ToStream().GZipDecompress();
//string response = Encoding.UTF8.GetString(decompressedBytes);
string response = Encoding.UTF8.GetString(bytesReceived);
Console.WriteLine(response);
}
}
您會注意到上面的一些擴展方法。 這是代碼,以防您想知道那里是否有問題。
public static MemoryStream ToStream(this byte[] bytes)
{
return new MemoryStream(bytes);
}
public static byte[] GZipCompress(this Stream stream)
{
using (var memoryStream = new MemoryStream())
{
using (var gZipStream = new GZipStream(memoryStream, CompressionMode.Compress))
{
stream.CopyTo(gZipStream);
}
return memoryStream.ToArray();
}
}
public static byte[] GZipDecompress(this Stream stream)
{
using (var memoryStream = new MemoryStream())
{
using (var gZipStream = new GZipStream(stream, CompressionMode.Decompress))
{
gZipStream.CopyTo(memoryStream);
}
return memoryStream.ToArray();
}
}
這些擴展在以下方面工作得很好,所以我確定它們不是問題:
string original = "the quick brown fox jumped over the lazy dog";
byte[] compressedBytes = Encoding.UTF8.GetBytes(original).ToStream().GZipCompress();
byte[] decompressedBytes = compressedBytes.ToStream().GZipDecompress();
string result = Encoding.UTF8.GetString(decompressedBytes);
Console.WriteLine(result);
有誰知道為什么在壓縮發送的字節時跳過 Read() 操作?
編輯
在向他們展示上述示例代碼后,我收到了來自 API 提供商的消息。 他們有話要說:
乍一看,我猜標題丟失了。 輸入必須以 'c' 開頭,后跟輸入的長度(在我們的示例中為 sprintf(cLength,"c%09d",hres))。 我們需要這個,因為我們無法讀取,直到我們找到一個二進制 0 來識別結尾。
之前他們提供了一些C
示例代碼,我不是 100% 完全理解,如下:
example in C:
#include <zlib.h>
uLongf hres;
char cLength[COMPRESS_HEADER_LEN + 1] = {'\0'};
n = read(socket,buffer,10);
// check if input is compressed
if(msg[0]=='c') {
compressed = 1;
}
n = atoi(msg+1);
read.....
hres = 64000;
res = uncompress((Bytef *)msg, &hres, (const Bytef*)
buffer/*compressed*/, n);
if(res == Z_OK && hres > 0 ){
msg[hres]=0; //original
}
else // errorhandling
hres = 64000;
if (compressed){
res = compress((Bytef *)buffer, &hres, (const Bytef *)msg, strlen(msg));
if(res == Z_OK && hres > 0 ) {
sprintf(cLength,"c%09d",hres);
write(socket,cLength,10);
write(socket, buffer, hres);
}
else // errorhandling
makefile: add "-lz" to the libs
他們正在使用 zlib。 我不懷疑這有什么不同,但我確實嘗試過使用 zlib.net,但我仍然沒有得到任何回應。
有人能給我一個例子,說明我應該如何在 C# 中發送這個輸入長度?
編輯 2
為了回應@quantdev,這是我現在正在嘗試的長度前綴:
using (var client = new TcpClient())
{
client.Connect("xxx.xxx.xx.xx", 6100);
using (var stream = client.GetStream())
{
// SEND REQUEST
byte[] bytes = Encoding.UTF8.GetBytes(xml);
byte[] compressedBytes = ZLibCompressor.Compress(bytes);
byte[] prefix = Encoding.UTF8.GetBytes("c" + compressedBytes.Length);
byte[] bytesToSend = new byte[prefix.Length + compressedBytes.Length];
Array.Copy(prefix, bytesToSend, prefix.Length);
Array.Copy(compressedBytes, 0, bytesToSend, prefix.Length, compressedBytes.Length);
stream.Write(bytesToSend, 0, bytesToSend.Length);
// WAIT
while (client.Available == 0)
{
Thread.Sleep(1000);
}
// GET RESPONSE
byte[] bytesReceived = new byte[client.ReceiveBufferSize];
stream.Read(bytesReceived, 0, client.ReceiveBufferSize);
byte[] decompressedBytes = ZLibCompressor.DeCompress(bytesReceived);
string response = Encoding.UTF8.GetString(decompressedBytes);
Console.WriteLine(response);
}
}
您需要檢查您在 TCP 流上進行的 Read() 調用的返回值:它是有效讀取的字節數。
MSDN 說:
返回值
讀入緩沖區的總字節數。 如果當前沒有可用的字節數,則這可能小於請求的字節數,如果已到達流的末尾,則該值可能為零 (0)。
client.ReceiveBufferSize
,則需要額外調用Read
以檢索剩余字節。在調用 read 之前,請檢查套接字上是否確實有一些數據可用:
while(client.Available == 0)
// wait ...
http://msdn.microsoft.com/en-us/library/system.net.sockets.tcpclient.available%28v=vs.110%29.aspx
我想你可能有文件結尾左右。 您可以在讀取流之前嘗試設置流位置嗎
stream.position = 0;
http://msdn.microsoft.com/en-us/library/vstudio/system.io.stream.read
Encoding.UTF8.GetString 不應用於任意字節數組。 例如:壓縮的字節可能包含空字符,這在 UTF-8 編碼的文本中是不允許的,除非用作終止符。
如果您想打印接收到的字節以進行調試,也許您應該將它們打印為整數。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.