Microsoft MVP성태의 닷넷 이야기
비동기 소켓사용후 해당 스레드가 남아있을때 처리 방법 [링크 복사], [링크+제목 복사]
조회: 10099
글쓴 사람
유영태 (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)
5425정유경12/10/20205584[왕초보] (4) [1]
5423정유경12/8/20205911[왕초보] (3) [2]
5422이승준12/7/20209366VisualStudio 2019의 인텔리센스가 느려지는 경우가 있나요? [4]
5421정유경12/6/20206995[왕초보] (2) [4]
5420정유경12/5/20206509[왕초보] 랜덤 숫자와 배열에 관한 질문 [1]
5419종범12/4/20206995[WPF] Threadpool사용 시 크리티컬 섹션 대응 질문 입니다. [4]
5418한예지 donator11/27/20207217클래스, 인터페이스 크기를 구하고 싶은데 어떻게 해야 될까요? [1]
5417한예지 donator11/27/20205897인터페이스와 추상클래스에 대해 궁금증이 있습니다. [1]
5416한예지 donator11/27/20205726Object 질문 있어요. [1]
5415한예지 donator11/25/20205820교재 213쪽 예제 4.25 질문드립니다. [1]
5414한예지 donator11/23/20206690제네릭 리스트 출력하는 방법이 궁금합니다. [1]
5413민석11/20/20207221C# minidump를 프로그램이 중단 될 때 만들고 싶습니다. [1]파일 다운로드2
5411원격11/20/20205740visualstdio로 웹 사이트로 만들었을때 원격 디버깅이 가능한가요? [1]
5410최성재11/16/20206102vcpkg로 GDCM 내려받을 때 USE_VTK 설정하는 방법-2번째 질문 [1]파일 다운로드1
5409민성11/16/20209081혹시 다른 질문이긴 한데요 [1]
5408최성재11/16/20206350vcpkg로 GDCM 내려받을 때 USE_VTK 설정하는 방법 [1]
5407민성11/11/20205651안녕하세요 yield return에 대해서 [1]
5406질문자11/10/20206096안녕하세요 wcf nettcpbinding의 timeout에 관해서 질문이 있습니다. [2]
5405민성11/9/20206870안녕하세요 이번에도 또 어려운 질문 같습니다. [1]
5404박진우11/6/20207175안녕하세요. SqlParameter 생성자 관련 질문 있습니다. [1]
5403민성11/5/20207248그리고 한가지만 죄송하지만 더 질문 드리겠습니다. [1]
5402민성11/5/20207491안녕하세요 책을 보고 질문하나만 드릴깨요 [2]
5401민성11/3/20206518안녕하세요 이번에도 질문 하나만 드리겠습니다. [2]
5400진우10/29/20206204SQL Server 관련 몇가지 문의 [2]
5399Wpf개...10/21/20206396Binding 된 항목의 갱신 시 간헐적 끊어짐 발생 문제. [2]
5397나그네10/15/20205901.net Core 3.1 에서 Entity Framework 와 ADO.NET 선택에 관해 여쭤봅니다. [2]
... 16  17  18  19  [20]  21  22  23  24  25  26  27  28  29  30  ...