[C#]소켓통신 - 서버에서 특정 클라이언트만 메시지 전송

개발 노트/C#  2015.05.14 22:46


안녕하세요 명월입니다.
참 오랫만에 C# 코드를 작성하는 것 같습니다. 요즘 우분투와 자바 매력에 흠뻑(?) 빠져들어서 C#코드는 거의 만지지를 않는데 말입니다.
소켓 통신에 있어서 특정 클라이언트만 메시지를 보내고 싶다라는 질문이 있어서 간단한 예제 소스를 작성해 봤습니다.
일단 결과부터 확인 하겠습니다.


결과를 확인 해보시면 위쪽부터 3개는 클라인트 맨 밑은 서버로 구성되어있습니다.
그럼 메시지를 전송해 보겠습니다.


자 결과를 보시면 test1한테는 hi, test2는 hello, test3은 gogogo를 보냈는데 원하는 클라이언트에게만 메시지가 전송이 되었습니다.

그럼 서버 소스를 확인 해 보도록 하겠습니다.

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

namespace server
{
    class Program
    {
        static void Main(string[] args)
        {
            new Program();
        }

        private Dictionary m_client = null;

        public Program()
        {
            initialize();
        }
        protected void initialize()
        {
            m_client = new Dictionary();
            Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            server.Bind(new IPEndPoint(IPAddress.Any, 10000));
            server.Listen(20);

            SocketAsyncEventArgs sockAsync = new SocketAsyncEventArgs();
            sockAsync.Completed += new EventHandler(sockAsync_Completed);
            server.AcceptAsync(sockAsync);

            Command();
        }

        private void sockAsync_Completed(object sender, SocketAsyncEventArgs e)
        {
            Socket server = (Socket)sender;
            Socket client = e.AcceptSocket;
            byte[] name = new byte[100];
            client.Receive(name);
            String strName = Encoding.Unicode.GetString(name).Trim().Replace("\0", "");
            m_client.Add(strName, client);
            SocketAsyncEventArgs recieveAsync = new SocketAsyncEventArgs();
            recieveAsync.SetBuffer(new byte[4], 0, 4);
            recieveAsync.UserToken = client;
            recieveAsync.Completed += new EventHandler(recieveAsync_Completed);
            client.ReceiveAsync(recieveAsync);
            Console.WriteLine();
            Console.WriteLine("***********" + strName + " Connected ***********");
            Console.Write("Sender ?");
            e.AcceptSocket = null;
            server.AcceptAsync(e);
        }
        private void recieveAsync_Completed(object sender, SocketAsyncEventArgs e)
        {
            Socket client = (Socket)sender;
            if (client.Connected && e.BytesTransferred > 0)
            {
                int length = BitConverter.ToInt32(e.Buffer, 0);
                byte[] data = new byte[length];
                client.Receive(data, length, SocketFlags.None);
                String data2 = Encoding.Unicode.GetString(data);
                String Name = searchSocket(client);
                if (Name != null)
                {
                    Console.WriteLine();
                    Console.WriteLine("***********" + Name + " receive ***********");
                    Console.WriteLine(data2);
                    Console.Write("Sender ?");
                }
                else
                {
                }

            }
            client.ReceiveAsync(e);
        }
        protected String searchSocket(Socket sender)
        {
            foreach (String key in m_client.Keys)
            {
                if (m_client[key] == sender)
                {
                    return key;
                }
            }
            return null;
        }
        protected void Command()
        {
            while (true)
            {
                Console.Write("Sender ?");
                String sender = Console.ReadLine();
                if (m_client.ContainsKey(sender))
                {
                    Console.Write("Message ?");
                    String message = Console.ReadLine();
                    Socket client = m_client[sender];
                    byte[] data = Encoding.Unicode.GetBytes(message);
                    client.Send(BitConverter.GetBytes(data.Length));
                    client.Send(data, data.Length, SocketFlags.None);
                }
                else
                {
                    Console.WriteLine("Not Socket");
                }
            }
        }
    }
}

위 소스는 일단 서버 쪽 소스입니다. 급하게 작성한 것이라 예외처리도 없고 엉망이지만 개념만 이해하시고 프로젝트에 맞게 작성하시면 될 듯 싶습니다.

다음은 클라이언트 소스입니다.

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

namespace client
{
    class Program
    {
        static void Main(string[] args)
        {
            new Program();
        }
        public Program()
        {
            initialize();
            Console.WriteLine("************Message**********");
            Console.ReadLine();
        }
        private String m_Name;
        public void initialize()
        {
            Console.Write("Name?");
            String strName = Console.ReadLine();
            if (strName.Trim().Length > 0)
            {
                m_Name = strName;
                Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                client.Connect(IPAddress.Parse("127.0.0.1"), 10000);

                byte[] name = new byte[100];
                byte[] buffer = Encoding.Unicode.GetBytes(strName);
                for (int i = 0; i < 100; i++)
                {
                    if (i < buffer.Length)
                    {
                        name[i] = buffer[i];
                    }
                }
                client.Send(name);
                SocketAsyncEventArgs reciveAsync = new SocketAsyncEventArgs();
                reciveAsync.Completed += new EventHandler(reciveAsync_Completed);
                reciveAsync.SetBuffer(new byte[4], 0, 4);
                reciveAsync.UserToken = client;
                client.ReceiveAsync(reciveAsync);
            }
        }

        private void reciveAsync_Completed(object sender, SocketAsyncEventArgs e)
        {
            Socket client = (Socket)sender;
            if (client.Connected && e.BytesTransferred > 0)
            {
                byte[] lengthByte = e.Buffer;
                int length = BitConverter.ToInt32(lengthByte, 0);
                byte[] data = new byte[length];
                client.Receive(data, length, SocketFlags.None);
                StringBuilder sb = new StringBuilder();
                sb.Append(m_Name);
                sb.Append("  -  ");
                sb.Append(Encoding.Unicode.GetString(data));
                Console.WriteLine(sb.ToString());
            }
            client.ReceiveAsync(e);
        }
    }
}

클라이언트는 그냥 일반 클라이언트 소스이기에 특별히 설명을 하지 않겠습니다.
참고 - [C#]비동기 소켓 채팅 프로그램 - 클라이언트 편

서버의 경우는 일반 서버와 비슷한데 서버를 찾는 작업이 필요하겠군요.
이전에는 클라이언트 List를 ArrayList에 저장했지만 이번에는 키가 필요한 관계로 Dictionary에 저장을 했습니다.

이제부터는 뭐랄까 제 나름대로의 규약(프로토콜)을 작성하겠습니다.
접속을 할때는 무조건 byte(100)짜리의 데이터로 이름을 보내야 접속이 되는 것으로 정했습니다. 클라이언트 소스를 보시면 접속을 할때 이름을 넣고 byte[100]으로 전문을 전송합니다.
그럼 서버쪽에서는 이름을 맞고 그 이름을 키로 등록을 하여ㅕ Dictionary에 등록을 합니다.

이제는 반대로 메시지를 보낼때 형식입니다.
보낼때는 어떤 클라이언트에게 보낼 지 검색을 해야겠지요. 그래서 유저를 검색을 하겠습니다. 그 키로 검색을 소켓에 Message를 넣어서 전송하면 해당클라이언트만 데이터가 수신이 되겠지요.. 이상입니다.

위 소스는 기본적으로 접속이 끊길 때의 처리,예외상황처리 등이 되어있지 않습니다. 즉 실 프로젝트에 적용하실때는 그 사양에 맞게 적용시키시기 바랍니다.
(예제 소스를 올리면 안 된다는 메일이 많이 와서 ㅜㅜ..예제 소스일 뿐이지 적용을 하실 때는 꼭 프로젝트 사양에 맞게 수정하여 사용해 주세요.)

Socket.zip

댓글 4개가 달렸습니다.
댓글쓰기

  1. 2015.07.29 00:53 |  수정/삭제  댓글쓰기

    비밀댓글입니다

  2. Sglee
    2015.09.30 15:51 신고 |  수정/삭제  댓글쓰기

    언제나 감사히 잘 보고 있습니다.
    막히는 부분에 대한 해답이 소스 사이사이 녹아있어 저에게는 항상 소화제같은 곳이네요

  3. sslee
    2017.03.16 18:19 신고 |  수정/삭제  댓글쓰기

    클라이언트 끼리의 대화는 어떻게 수정을 해야할까요?

  4. ddddaa
    2017.09.14 11:42 신고 |  수정/삭제  댓글쓰기

    항상 잘보고 있습니다. 감사합니다.