Microsoft MVP성태의 닷넷 이야기
비동기 소켓사용후 해당 스레드가 남아있을때 처리 방법 [링크 복사], [링크+제목 복사]
조회: 10231
글쓴 사람
유영태 (ytreyt at naver.com)
홈페이지
첨부 파일

서버는 블럭없이 대기하는 부분,블럭없이 자료수신하는 부분으로 구성하고 있습니다.
제 구상.
1. loopAccept메서드가 i/o 블럭없이 처리됨.
2. client 접속이 들어오면 TaskCompletionSource의 onComplete 메서드가 동작됩니다.
   여기서 접속된 클라이언트를 저장하고, 메시지를 받는 LoopReceive 메서드를 실행합니다.
3. 역시나 LoopReceive메서드 블럭없이 동작하기에 바로 다음 loopAccept 메서드가 반복됩니다.

4. LoopReceive는 클라이언트에서 자료를 보냈을때 완료되고 다음 loopReceive를 반복합니다.

문제점
.1 클라이언트를 5~6개 이상실행하였다가 종료하면 ,메인스레드이외에 작업스레드가 여러개 생기는데 어디서 잘못된건지 모르겠습니다.
.2 loopReceive 메서드에서 beginReceive로 기다리는 동작을 실행하는데. 클라이언트에서 소켓을 종료하게 되면 해당 메서드는 사라지지 않는건지 궁금합니다.

      public async Task<bool> Start()
        {
            if (!CheckSocketStart())
            {
                return false;
            }
            LocalSocket = GetTcpSocket();
            LocalSocket.Bind(LocalSocketIpEndPoint);
            LocalSocket.Listen(5);

            ActSysMessage?.Invoke("서버시작");
            LoopAccept();
            return true;
        }

        /// <summary>
        /// 클라이언트의 접속을 기다리고 접속이 처리되면 반복적으로 처리
        /// </summary>
        /// <returns></returns>
        private void LoopAccept()
        {
            var awaiter = AcceptTaskCompletionSource(LocalSocket).GetAwaiter();
            awaiter.OnCompleted(() => {
                var client = awaiter.GetResult();
                _sockets.Add(client);
                LoopReceive(client);
                LoopAccept();
            });
        }


        /// <summary>
        /// Socket의 beginAccept가 완료되면 endAccept로 완료처리를 하고 client를
        /// TaskCompletionSource를 통해서 전달함 block을 사용하지 않는게 목표.
        /// </summary>
        /// <param name="socket"></param>
        /// <returns></returns>
        public Task<Socket> AcceptTaskCompletionSource (Socket socket)
        {
            var tcs = new TaskCompletionSource<Socket>(socket);
            socket.BeginAccept(a => {
                try
                {
                    var retsocket= a.AsyncState as Socket;
                    var client = retsocket.EndAccept(a);
                    tcs.SetResult(client);
                }
                catch (Exception e)
                {
                    tcs.SetException(e);
                }

            }, socket);
            return tcs.Task;
        }
        ///반복적으로 데이터를 받는 부분.
        private void LoopReceive( Socket socket)
        {
            if (socket == null || !socket.Connected)
            {
                return;
            }
            var awaiter = ReceiveTaskCompletionSource(socket).GetAwaiter();
            awaiter.OnCompleted(() => {
                var ret = awaiter.GetResult();
                if (ret ==null || ret.Length == 0)
                {
                    return;
                }
                string recv = Encoding.UTF8.GetString(ret);
                ActReceiveMessage?.Invoke(recv);
                LoopReceive(socket);
            });
        }


        /// <summary>
        /// beginReceive가 완료되면 endReceive로 완료처리를 한후에 TaskCompletionSource를 통해서
        /// 받은 데이터를 전송함.
        /// </summary>
        /// <param name="socket"></param>
        /// <returns></returns>
        public Task<Byte[]> ReceiveTaskCompletionSource (Socket socket)
        {

            if (socket == null || !socket.Connected)
            {
                return null;
            }
            var tcs = new TaskCompletionSource<Byte[]>(socket);
            var buffer = new byte[1024];
   
            socket.BeginReceive(buffer, 0, 1024, SocketFlags.None, ar => {
                try
                {
                    var ret = ar.AsyncState as Socket;
                    int retcnt = ret.EndReceive(ar);
                    var retbuffer = new byte[retcnt];
                    Buffer.BlockCopy(buffer, 0, retbuffer, 0, retcnt);
                    tcs.SetResult(retbuffer);
                }
                catch (Exception er)
                {
                    tcs.SetException(er);
                }
            }, socket);
   
            return tcs.Task;
        }








[최초 등록일: ]
[최종 수정일: 8/14/2018]


비밀번호

댓글 작성자
 



2018-08-14 06시36분
1. 작업자 스레드라는 것이 스레드 풀의 스레드를 의미하는 건가요? 풀 관리는 알아서 되는 것이므로 클라이이언트가 종료되는 것과는 아무런 상관이 없습니다.

2. 메서드가 사라진다는 의미가 스레드 풀의 스레드가 사라져야 한다는 것을 의미하는 건가요? 풀의 스레드는 풀 관리자가 알아서 하는 것입니다. 클라이언트 측 소켓의 종료와는 아무런 상관이 없습니다.
정성태
2018-08-14 06시50분
[유영태] 작업자 스레드라는 것은 visual studio에서 디버깅모드에 들어갔을때 표시되는 스레드를 지칭한겁니다.
그렇다면 해당 visual studio에서 보여주는 스레드가 풀에서 관리되는 것인가요?
beginreceive 메서드가 비동기로 실행되고 있는 상태에서 client측에서 데이터를 전송하지 않으면 전송될때까지 기다리는 메서드가 생성되는게 아닌가요? 단지 블럭상태만 아니고요. 그런데 클라이언트가 종료됬음으로 데이터를 전송하지 않아서 무제한 적으로 기다리게 되는 상황이 발생하지 않나요?
[guest]
2018-08-14 06시59분
Visual Studio에서 보여주는 스레드는 스레드 풀의 것이 아니고, 프로세스에 있는 모든 스레드를 보여줍니다. 따라서 작업자 스레드라고 해도 이런 경우에는 풀의 스레드입니다. (유영태 님이 생성하고 관리하는 스레드가 있나요?)

기다리는 메서드가 생성된다는 것은 곧, 비동기가 아닌 동기로 스레드를 점유하고 있는 것이 있느냐는 것과 같습니다. 코딩하신 거에 그런 동작이 있었나요? (혹시, 제 책을 가지고 있다면 비동기 부분에 대해 다시 한번 정독하실 것을 권합니다.)

일반적으로 클라이언트가 소켓을 끊으면 Read했던 동작이 반환됩니다. 비동기로 Read를 했으니 ReadCompleted 비스무리한 콜백이 호출되는데 위의 코드에서는 ReceiveTaskCompletionSource의 socket.BeginReceive에 등록한 콜백이 호출됩니다. 그리고 그때의 retcnt는 0으로 나오고 그걸로 소켓 종료를 판단할 수 있으므로 이후 대응 소켓의 Close를 호출해 소켓 자원만 잘 해제해주시면 됩니다.
정성태
2018-08-14 07시02분
[5256] 아하 감사합니다. 잘배우고 갑니다.
[guest]

... 16  [17]  18  19  20  21  22  23  24  25  26  27  28  29  30  ...
NoWriterDateCnt.TitleFile(s)
5510jay6/8/20215834string 문자열에 쌍따옴표(")를 넣고 싶습니다. [1]파일 다운로드1
5509한예지 donator6/3/20215781ExeccuteNonQuery 질문있습니다! [2]
5508최재남6/3/20216615마우스 이벤트 관련 질문 좀 드리겠습니다. [6]
5507한예6/2/20215784static과 스택오버플로우 질문있습니다. [5]파일 다운로드1
5506이창석6/1/20217196센서의 값을 받아서 PC를 통해 모니터링 하는 것을 만들고 있습니다. [1]
5505kss5/31/20215768.net5에서 소멸자가 어떻게 바뀐건가요? [1]
5503xing...5/24/20216156xingapinet 에 수정 요청 부탁 드립니다. [1]
5501한예지 donator5/23/20215917IProgress 사용법이 궁금합니다. [2]
5500한예ㅈ5/23/20217789비동기 코드 흐름 질문있습니다. [3]
5498곰장어5/21/20215658List에 static 변수를 추가했을때의 궁금증 [3]파일 다운로드1
5497지평선5/20/20216901윈도우 배율을 알 수 있을까요? [1]
5496cs린이5/20/20215536C# 8.0 질문입니다. [2]파일 다운로드6
5495Natie5/13/20215069객체를 생성과 동시에 초기화 하는 방법 [1]
5494지예예지5/13/20217339비동기 코드 흐름이 궁금합니다! [2]
5493xing...5/6/20215247xing api XQCSPAT00600 질문입니다 [4]파일 다운로드1
5492한예지 donator5/5/20215328FromCurrentSynchronizationContext 관련 코드 질문있습니다! [2]
5491조우성5/4/20219766WinForm과 WPF의 성능차이 비교한 자료가 있을까요? [4]
5490한예지 donator5/3/20216127UI 스레드의 Invoke 질문있습니다. [4]
5489저누4/28/20216707시작하세요 C# 9.0, 225페이지 구조체 관련 질문드립니다. [2]
5488종범4/27/20216964C# 비동기 함수 async, await 와 Task의 관계에 대해 질문 드립니다. [6]
5487진우4/27/20216319C# 엑셀 자동화 성능 향상 문의 [2]
5486지나가던...4/26/20215869닷넷 구현 코드 관련 질문 [2]
5485이재원4/17/20216251교재 315페이지 내용 질문 [3]
5484Syong4/16/20219802윈폼 기반의 응용프로그램 dll 참조와 32,64bit 빌드 관련 문의 [4]
5483한예지 donator4/15/20215301익명 형식과 var 관계 질문 있습니다. [2]
5482질문4/13/20217095WPF를 위한 MVVM toolkit 선택과 관련한 문의드립니다. [4]
... 16  [17]  18  19  20  21  22  23  24  25  26  27  28  29  30  ...