[C# 강좌 - 35] 소켓 통신 - 4


Study/C#  2012. 10. 9. 10:49

안녕하세요 개발자 명월입니다.


이번 포스팅에서는 소켓통신을 하다보면 메시지 문제 해결방법에 대한 포스팅입니다.

TCP 전송을 하다 보면 메시지가 밀리는 현상이 발생합니다.

 

예제로 살펴 보도록 하겠습니다.

using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace Blog20121009
{
    class Program
    {
        static void Main(string[] args)
        {
            new Program();
        }
        public Program()
        {
            new Thread(new ThreadStart(Server)).Start();
            new Thread(new ThreadStart(Client)).Start();

            Console.ReadLine();
        }
        public void Server()
        {
            IPEndPoint ipep = new IPEndPoint(IPAddress.Any,9999);
            Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            server.Bind(ipep);
            server.Listen(20);
            Socket client = server.Accept();
            Console.WriteLine("Server Message : 접속되었습니다.");
            byte[] _data = new byte[1024];
            client.Receive(_data);
            Console.WriteLine("Server Message : {0}",Encoding.Default.GetString(_data).Trim('\0'));
            for (int i = 0; i < 10; i++)
            {
                _data = Encoding.Default.GetBytes(i.ToString()+" 번째 데이터 전송");
                client.Send(_data);
                Thread.Sleep(5);
            }
            client.Close();
            server.Close();
        }
        public void Client()
        {
            IPEndPoint ipep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9999);
            Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            client.Connect(ipep);
            byte[] _data = Encoding.Default.GetBytes("Connection");
            client.Send(_data);
            for (int i = 0; i < 10; i++)
            {
                _data = new byte[1024];
                client.Receive(_data);
                Console.WriteLine("Client Message : {0}", Encoding.Default.GetString(_data).Trim('\0'));
                Thread.Sleep(10);
            }
            client.Close();
        }
    }
}

 

위 소스는 서버에서 클라이언트로 0.005초 단위로 패킷을 전송하고 클라이언트에서는 0.01초 단위로 메시지를 받습니다.

 

 

결과는 역시나 패킷이 밀리거나 합쳐져 버렸습니다. 지금은 한 프로세스 안에 두개의 스레드로 하여 스레드 타임의 차이를 준것 이지만 실제로 서로 다른 프로그램이라고 하면 또 다른 시스템간의 통신이라고 한다면 분명이 처리 속도 차이가 발생 합니다. 그때마다 이렇게 패킷이 밀리거나 합쳐져 버리면 문제가 발생하겠습니다.

 

보통 전문 송수신을 할때는 자리수를 정해 놓거나 마지막에 \r\n을 주어서 구분하는 방법도 있기는 합니다.

그러나 우리는 그런 방법이 아닌 조금 더 정확한 방법을 알아보도록 하겠습니다.

 

메시지를 보내기 전에 해더에 우리는 어느정도의 크기를 전송할 것이다 라는 것을 명시하면 이런 문제가 말끔히 해결 됩니다. 이런 문자 메시지는 큰 영향이 없으나 파일 다운, 업로드 할때는 이 차이가 커지니 꼭 알아두시길 바랍니다.

using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace Blog20121009
{
    class Program
    {
        static void Main(string[] args)
        {
            new Program();
        }
        public Program()
        {
            new Thread(new ThreadStart(Server)).Start();
            new Thread(new ThreadStart(Client)).Start();

            Console.ReadLine();
        }
        public void Server()
        {
            IPEndPoint ipep = new IPEndPoint(IPAddress.Any,9999);
            Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            server.Bind(ipep);
            server.Listen(20);
            Socket client = server.Accept();
            Console.WriteLine("Server Message : 접속되었습니다.");

            /*
            byte[] _data = new byte[1024];
            client.Receive(_data);
            Console.WriteLine("Server Message : {0}",Encoding.Default.GetString(_data).Trim('\0'));
            for (int i = 0; i < 10; i++)
            {
                _data = Encoding.Default.GetBytes(i.ToString()+" 번째 데이터 전송");
                client.Send(_data);
                Thread.Sleep(5);
            }
            */
            String _data = "";
            Recieve(client, ref _data);
            Console.WriteLine("Server Message : {0}",_data);
            for (int i = 0; i < 10; i++)
            {
                Send(client, i.ToString() + " 번째 데이터 전송");
                Thread.Sleep(5);
            }
            client.Close();
            server.Close();
        }
        public void Client()
        {
            IPEndPoint ipep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9999);
            Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            client.Connect(ipep);
            /*
            byte[] _data = Encoding.Default.GetBytes("Connection");
            client.Send(_data);
            for (int i = 0; i < 10; i++)
            {
                _data = new byte[1024];
                client.Receive(_data);
                Console.WriteLine("Client Message : {0}", Encoding.Default.GetString(_data).Trim('\0'));
                Thread.Sleep(10);
            }
            */
            Send(client, "Connection");
            for (int i = 0; i < 10; i++)
            {
                String _data = "";
                Recieve(client, ref _data);
                Console.WriteLine("Client Message : {0}",_data);
            }
            client.Close();
        }
        public bool Send(Socket sock, String msg)
        {
            byte[] data = Encoding.Default.GetBytes(msg);
            int size = data.Length;

            byte[] data_size = new byte[4];
            data_size = BitConverter.GetBytes(size);
            sock.Send(data_size);

            sock.Send(data, 0, size, SocketFlags.None);
            return true;
        }
        public bool Recieve(Socket sock,ref String msg)
        {
            byte[] data_size = new byte[4];
            sock.Receive(data_size, 0, 4, SocketFlags.None);
            int size = BitConverter.ToInt32(data_size,0);
            byte[] data = new byte[size];
            sock.Receive(data, 0, size, SocketFlags.None);
            msg = Encoding.Default.GetString(data);
            return true;
        }
    }
}

 

 

데이터의 밀림이 없습니다.

이런 데이터 통신은 보통 채팅이나 간단한 문자열을 통한 통신보다는 데이터통신 즉, 파일을 전송할 때 유용하게 쓰입니다. 파일을 전송함에 있어서 한번이라도 데이터가 밀리거나 제대로 입력이 되지 않으면 그 파일은 에러가 나기 때문입니다. 관련 소스 첨부하겠습니다.

 

첨부 소스 : Blog20121009.zip