socket.Receive 에서 전체 데이터를 가져오고 싶습니다.


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로 딜레이를 줬더니 전체 데이터를 가져옵니다.
지연시간을 어느정도 줘야 할지도 모르겠고
일괄적으로 줘버리면 짧은데이터 가져올때도 지연이 되니 성능에 문제가 있고요.
정확하게 할수 있는 방법 없을까요?

1개의 좋아요

글쎄요. 정상적인 상황이라면 1바이트씩 가져오거나 버퍼사이즈를 늘려서 가져오거나 가져온 길이만큼 처리하면 문제가 없어야 합니다.

제가 봤을 때는 굳이 1바이트씩 가져올 필요가 없고요 특히 그것을 바이트배열에 하나씩 넣는것은 불필요해 보이긴 합니다.

가장 이상적인 것은 스트림으로 처리하고 읽는 족족 파일로 저장하거나 스트림으로 바로 처리하는 것입니다.

1개의 좋아요

원인을 찾아서 질문 수정하였습니다.
정확한 전체완료 확인 방법이 있을지 궁금합니다.

1개의 좋아요

socket.Available로 while을 돌면 안됩니다. while문을 돌면서 데이터를 가져올 때 네트워크의 상황에 따라 목적지에서 데이터가 아직 도달하지 않는 경우가 생깁니다. 이는 정상적인 경우로 데이터를 받는 중에 Available가 0이 될 수도 있어요.

연결이 끊기거나 받아야 하는 데이터의 길이를 content-length 헤더 등으로 정확히 확인하여 그 길이만큼 받는 코드로 변환하여야 합니다.

딜레이를 줘서 된 이유는 Available가 0이 되지 않도록 한 임시 조치로 올바른 방법이 아닙니다.

3개의 좋아요

서버와 tcp 통신을 하기때문에 지금 sleep 을 줘서 해결된것 처럼 보이지만 네트웍 상황에 따라 언제든 틀려질 수 있습니다. 위에 dimohy 님 코멘트 처럼 content-length 헤더로 정확히 그 길이 만큼 받는 코드를 짜야합니다.
tcp 특성에 대해서는 아래 링크를 참조하시면 도움될것 같습니다.

2개의 좋아요

답변 감사합니다.
저도 contents-length로 하면 되지 않을까 생각만 했는데 수십개의 샘플과 stackoverflow 봤는데 contents-length로 가져오는 코드는 안보이더군요.
헤더부분만 깔끔하게 가져오고 그다음 바디 부분 가져오면 좋은데 헤더부분만 가져오기가 힘들것 같던데요.
적당히 가져와서 \r\n\r\n나오는 부분으로 잘라내거나 아님 1바이트로 가져오면서 \r\n\r\n 나올때까지 계속 체크를 하던가…
보통은 어떻게 하나요?

근데 궁금한게

질문에 ‘웹서버’ 에서 데이터를 수신 한다고 하셨는데

웹소켓으로 수신 하시는건가요?

웹소켓이 아닌 일반 http(s) 라면 직접적인 raw적인 소켓 을 다루기보단

HttpClient 같은 BCL 을 이용하면 될텐데

설령 웹소켓으로 데이터를 받는다 해도

관련 라이브러리 이용해서 간편하게 받으면 되지 않을까 해서요

2개의 좋아요

httpwebrequest로 해봤는데 깔끔하게 가져와서 좋기는한데요.
클라이언트에서 요청받은 데이터도 수신 받아야 해서 안될것 같습니다.
클라이언트하고는 socket통신은 변경이 안됩니다.
소켓수신 라이브러리 있으면 추천 부탁드립니다.

1개의 좋아요

흠…
그러니깐
실제 데이터의 내용이 아니라
압축이 되어있는 페이로드 가 필요한건가요?

1개의 좋아요

댓글 다신지 모르고 수정했었는데요.
수정전의 댓글내용을 다시 적자면
httpwebrequest로 사이트에 요청 후 응답에서
GetResponse() 로 수신 데이터 가져오면
헤더에 Content-Encoding: gzip 인 응답인데도
바디에는 디코딩된 데이터가 표시가 됩니다.
디코딩된 바디를 그대로 클라이언트로 보내면 제대로 표시가 안됩니다.

soket으로 받으면 바디에    ] o 8~   m 이런식으로 인코딩된 데이터로 표시가 되는데
인코딩 데이터 그대로 클라이언트에 던져줘야 제대로 표시가 됩니다.

1.소켓클라이언트 > 2.소켓서버 > 3.웹서버 > 4.소켓서버 > 5.소켓클라이언트… 이런구조입니다.
문의 드린부분은 소켓서버 부분이고
프록시서버를 만들려는건 아니지만 큰 구조는 비슷합니다.
1번에서 HTTP 프로토콜로 send하기에
어차피 socket receive로 읽는 기능은 만들어야 됩니다.
그래서 httpwebrequest댓글문의는 적었다가 지웠습니다.

1개의 좋아요

HTTP 프로토콜의 헤더는 텍스트입니다. 그래서 그냥 뉴라인 두번 나올때까지 문자열로 만든 다음
만들어진 문자열에서 Content-Length를 취해도 됩니다. 헤더정보는 Key: Value 형태이므로 그냥 간단히 :를 기준으로 키와 값을 취하는 것으로 원하는 정보를 얻을 수 있습니다.

2개의 좋아요

일단 이 부분은 코드가 없어서 모르겠는데 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 컨텐츠로 요청한 헤더와
그렇지 않고 아무 설정 없이 요청한 헤더의 비교 캡쳐 화면도 같이 첨부해 드립니다.

[일반요청]

image

[gzip 컨텐츠 요청]

image

2개의 좋아요