while (socket.Available > 0)
{
byte currByte = new Byte[1];
int byteCounter = socket.Receive(currByte, currByte.Length, SocketFlags.None);
if (byteCounter.Equals(1))
{
byteList.Add(currByte[0]);
}
}
c#에서 웹서버에 connect 하여 위의 코드처럼 수신받고 있습니다.
짧은 데이터들은 문제없고
큰 데이터들은 어느정도만 가져오고 전체를 못가져옵니다.
테스트 하다가 어느정도 해결은 하였습니다.
socket.send 와 socket.receive 사이에 Thread.sleep로 딜레이를 줬더니 전체 데이터를 가져옵니다.
지연시간을 어느정도 줘야 할지도 모르겠고
일괄적으로 줘버리면 짧은데이터 가져올때도 지연이 되니 성능에 문제가 있고요.
정확하게 할수 있는 방법 없을까요?
서버와 tcp 통신을 하기때문에 지금 sleep 을 줘서 해결된것 처럼 보이지만 네트웍 상황에 따라 언제든 틀려질 수 있습니다. 위에 dimohy 님 코멘트 처럼 content-length 헤더로 정확히 그 길이 만큼 받는 코드를 짜야합니다.
tcp 특성에 대해서는 아래 링크를 참조하시면 도움될것 같습니다.
답변 감사합니다.
저도 contents-length로 하면 되지 않을까 생각만 했는데 수십개의 샘플과 stackoverflow 봤는데 contents-length로 가져오는 코드는 안보이더군요.
헤더부분만 깔끔하게 가져오고 그다음 바디 부분 가져오면 좋은데 헤더부분만 가져오기가 힘들것 같던데요.
적당히 가져와서 \r\n\r\n나오는 부분으로 잘라내거나 아님 1바이트로 가져오면서 \r\n\r\n 나올때까지 계속 체크를 하던가…
보통은 어떻게 하나요?
댓글 다신지 모르고 수정했었는데요.
수정전의 댓글내용을 다시 적자면
httpwebrequest로 사이트에 요청 후 응답에서
GetResponse() 로 수신 데이터 가져오면
헤더에 Content-Encoding: gzip 인 응답인데도
바디에는 디코딩된 데이터가 표시가 됩니다.
디코딩된 바디를 그대로 클라이언트로 보내면 제대로 표시가 안됩니다.
soket으로 받으면 바디에 ] o 8~ m 이런식으로 인코딩된 데이터로 표시가 되는데
인코딩 데이터 그대로 클라이언트에 던져줘야 제대로 표시가 됩니다.
1.소켓클라이언트 > 2.소켓서버 > 3.웹서버 > 4.소켓서버 > 5.소켓클라이언트… 이런구조입니다.
문의 드린부분은 소켓서버 부분이고
프록시서버를 만들려는건 아니지만 큰 구조는 비슷합니다.
1번에서 HTTP 프로토콜로 send하기에
어차피 socket receive로 읽는 기능은 만들어야 됩니다.
그래서 httpwebrequest댓글문의는 적었다가 지웠습니다.
HTTP 프로토콜의 헤더는 텍스트입니다. 그래서 그냥 뉴라인 두번 나올때까지 문자열로 만든 다음
만들어진 문자열에서 Content-Length를 취해도 됩니다. 헤더정보는 Key: Value 형태이므로 그냥 간단히 :를 기준으로 키와 값을 취하는 것으로 원하는 정보를 얻을 수 있습니다.
일단 이 부분은 코드가 없어서 모르겠는데 AutomaticDecompression속성이 설정 되어 있다면
자동으로 디코딩해서 원본으로 변환된거 같습니다.
기본적으로 http 요청시 gzip으로 요청하면
웹서버에서 해당 압축 알고리즘 사용이 가능하다면
요청한 컨텐츠로 응답을 해줍니다.
다음은 간단한 테스트 코드로 확인해본 결과 입니다.
private async Task Test()
{
HttpClient client = new HttpClient();
// gzip 컨텐츠 요청
client.DefaultRequestHeaders.AcceptEncoding.Add(new System.Net.Http.Headers.StringWithQualityHeaderValue("gzip"));
// 닷넷데브 사이트 get 요청
var response = await client.GetAsync("https://forum.dotnetdev.kr/t/socket-receive/5547/10");
var resMessage = response.EnsureSuccessStatusCode();
// 응답된 결과 컨텐츠가 gzip 인코딩인지 확인
var isEncoding_gzip = resMessage.RequestMessage.Headers.AcceptEncoding.Contains(new System.Net.Http.Headers.StringWithQualityHeaderValue("gzip"));
if (isEncoding_gzip is false)
return;
// gzip 압축 데이터이므로 직접 바로 확인 불가능하다.
var strContent = await response.Content.ReadAsStringAsync();
// Un gzip
var gzipStream = new GZipStream(await response.Content.ReadAsStreamAsync(), CompressionMode.Decompress);
var reader = new StreamReader(gzipStream, Encoding.UTF8);
// 압축 해제 결과
var body = reader.ReadToEnd();
}
static void Main(string[] args)
{
Program p = new();
p.Test();
Console.ReadLine();
}
위 확인된 결과 처럼
raw방식의 TCP 소켓을 사용하지 않고도,
http 프로토콜의 수신 패킷을 일일히 분석하지 않고도,
순수 gzip 컨텐츠의 스트림만 받아 올 수 있습니다.
이렇게 받아온 스트림을 다시 클라이언트에게 던져주면 되지 않을까요?
ps : 아 추가로 위 테스트 코드에서 gzip 컨텐츠로 요청한 헤더와
그렇지 않고 아무 설정 없이 요청한 헤더의 비교 캡쳐 화면도 같이 첨부해 드립니다.