Microsoft MVP성태의 닷넷 이야기
비동기 소켓사용후 해당 스레드가 남아있을때 처리 방법 [링크 복사], [링크+제목 복사],
조회: 17528
글쓴 사람
유영태 (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]

... 76  77  78  79  80  81  [82]  83  84  85  86  87  88  89  90  ...
NoWriterDateCnt.TitleFile(s)
431혁이7/19/200612248UpdatePanel(Atlas)위의 SmartClient가 이벤트후 사라집니다. ㅜㅜ파일 다운로드1
432정성태7/19/200612620    답변글 [답변]: UpdatePanel(Atlas)위의 SmartClient가 이벤트후 사라집니다. ㅜㅜ
433혁이7/20/200613472        답변글 [답변]: [답변]: UpdatePanel(Atlas)위의 SmartClient가 이벤트후 사라집니다. ㅜㅜ
434정성태7/20/200614221            답변글 [답변]: [답변]: [답변]: UpdatePanel(Atlas) 위의 SmartClient가 이벤트 후 사라집니다. ㅜㅜ [1]
429kwt7/12/200613514ActiveX 개발을 배우고 싶은데요 [3]
427이홍자7/11/200613799스마트 클라이언트 실행 시 그리드의 체크박스가 안보여요.. [1]
426정보문7/11/200614339액티브X 설치2 [1]
423이방은7/9/200613219리소스 임베디드 질문요.. [1]파일 다운로드1
422guest7/6/200612801COM+에서 풀링을 사용할 경우... [1]파일 다운로드1
424guest7/10/200613659    답변글 소스 코드 입니다. [1]
425guest7/11/200613072        답변글 다시 올립니다.파일 다운로드1
428정성태7/11/200612911            답변글 [답변]: 다시 올립니다.
421정보문7/6/200613459액티브X 설치 [1]
1354허재호11/12/201414094    답변글 [답변]: 액티브X 설치
418임대진7/4/200612670이럴적 있으세요?
419정성태7/4/200612825    답변글 [답변]: 이럴적 있으세요?
417이방은6/29/200612566[질문]2.0에서 웹리소스에 대해서 [2]
416박영일6/29/200612801CAS 설정여부 [1]
415김인학6/28/200613329스마트 클라이언트에서 웹서비스를 호출하고 있습니다. [1]
413김성호6/28/200613011IE프로그램문제... [2]
411이진형6/27/200613197어셈블리 네임 알아내기 [2]
414정성태6/28/200612409    답변글 [답변]: 어셈블리 네임 알아내기
410이재원6/26/200612583자바스크립트 스마트클라이언트 이벤트 호출 [3]
412이재원6/27/200612690    답변글 [답변]: 자바스크립트 스마트클라이언트 이벤트 호출
409정준명6/22/200613652윈폼 기반 스마트클라이언트 속도 문제에 대해서 의견을 구합니다. [2]
406이진형6/21/200613842처음에 스마트클라이언트 로딩하는 속도 [1]
... 76  77  78  79  80  81  [82]  83  84  85  86  87  88  89  90  ...