지난번에 1:1 소켓프로그래밍으로 채팅 프로그램을 만들어보았습니다.


참고 : http://yeolco.tistory.com/31


이번 시간에는 1:n 비동기 방식 채팅프로그램을 만들어보겠습니다.


각 클라이언트 소켓마다 쓰레드를 만들어서 통신하는 방법입니다.






실행화면


3명의 사용자가 동시에 사용했을때




잘 작동하는 화면입니다.





사용자 1명이 나가고 새로운 사용자가 서버에 접속하는 화면입니다.




◎ Server쪽 소스코드입니다.


using System;

using System.Collections.Generic;

using System.Net;

using System.Net.Sockets;

using System.Text;

using System.Threading;

using System.Windows.Forms;


namespace SIMT_CHATTING_SERVER

{

    public partial class Form1 : Form

    {

        TcpListener server = null; // 서버

        TcpClient clientSocket = null; // 소켓

        static int counter = 0; // 사용자 수

        string date; // 날짜 

        // 각 클라이언트 마다 리스트에 추가

        public Dictionary<TcpClient, string> clientList = new Dictionary<TcpClient, string>();


        public Form1()

        {

            InitializeComponent();

            // 쓰레드 생성

            Thread t = new Thread(InitSocket);

            t.IsBackground = true;

            t.Start();

        }


        private void InitSocket()

        {

            server = new TcpListener(IPAddress.Any, 9999); // 서버 접속 IP, 포트

            clientSocket = default(TcpClient); // 소켓 설정

            server.Start(); // 서버 시작

            DisplayText(">> Server Started");


            while (true)

            {

                try

                {

                    counter++; // Client 수 증가

                    clientSocket = server.AcceptTcpClient(); // client 소켓 접속 허용

                    DisplayText(">> Accept connection from client");


                    NetworkStream stream = clientSocket.GetStream();

                    byte[] buffer = new byte[1024]; // 버퍼

                    int bytes = stream.Read(buffer, 0, buffer.Length);

                    string user_name = Encoding.Unicode.GetString(buffer, 0, bytes);

                    user_name = user_name.Substring(0, user_name.IndexOf("$")); // client 사용자 명


                    clientList.Add(clientSocket, user_name); // cleint 리스트에 추가


                    SendMessageAll(user_name + " 님이 입장하셨습니다.", "", false); // 모든 client에게 메세지 전송


                    handleClient h_client = new handleClient(); // 클라이언트 추가

                    h_client.OnReceived += new handleClient.MessageDisplayHandler(OnReceived);

                    h_client.OnDisconnected += new handleClient.DisconnectedHandler(h_client_OnDisconnected);

                    h_client.startClient(clientSocket, clientList); 

                }

                catch (SocketException se) { break; }

                catch (Exception ex) { break; }

            }


            clientSocket.Close(); // client 소켓 닫기

            server.Stop(); // 서버 종료

        }


        void h_client_OnDisconnected(TcpClient clientSocket) // cleint 접속 해제 핸들러

        {

            if (clientList.ContainsKey(clientSocket))

                clientList.Remove(clientSocket);

        }


        private void OnReceived(string message, string user_name) // cleint로 부터 받은 데이터

        {

            if(message.Equals("leaveChat")) {

                string displayMessage = "leave user : " + user_name;

                DisplayText(displayMessage);

                SendMessageAll("leaveChat", user_name, true);

            }

            else {

                string displayMessage = "From client : " + user_name + " : " + message;

                DisplayText(displayMessage); // Server단에 출력

                SendMessageAll(message, user_name, true); // 모든 Client에게 전송

            }

        }


        public void SendMessageAll(string message, string user_name, bool flag)

        {

            foreach (var pair in clientList)

            {

                date = DateTime.Now.ToString("yyyy.MM.dd. HH:mm:ss"); // 현재 날짜 받기


                TcpClient client = pair.Key as TcpClient;

                NetworkStream stream = client.GetStream();

                byte[] buffer = null;


                if (flag)

                {

                    if(message.Equals("leaveChat"))

                        buffer = Encoding.Unicode.GetBytes(user_name + " 님이 대화방을 나갔습니다.");

                    else

                        buffer = Encoding.Unicode.GetBytes("[ " + date + " ] " + user_name + " : " + message);

                }

                else

                {

                    buffer = Encoding.Unicode.GetBytes(message);

                }


                stream.Write(buffer, 0, buffer.Length); // 버퍼 쓰기

                stream.Flush();

            }

        }


        private void DisplayText(string text) // Server 화면에 출력

        {

            if (textBox1.InvokeRequired)

            {

                textBox1.BeginInvoke(new MethodInvoker(delegate

                {

                    textBox1.AppendText(text + Environment.NewLine);

                }));

            }

            else

                textBox1.AppendText(text + Environment.NewLine);

        }

    }

}




◎ Client쪽 소스코드입니다.


using System;

using System.Net.Sockets;

using System.Text;

using System.Threading;

using System.Windows.Forms;


namespace Chatting

{

    public partial class Form2 : Form

    {

        TcpClient clientSocket = new TcpClient(); // 소켓

        NetworkStream stream = default(NetworkStream); 

        string message = string.Empty;


        public Form2()

        {

            InitializeComponent();

        }


        private void Form2_Load(object sender, EventArgs e)

        {

            try

            {

                clientSocket.Connect("192.168.0.31", 9999); // 접속 IP 및 포트

                stream = clientSocket.GetStream();

            }

            catch (Exception e2)

            {

                MessageBox.Show("서버가 실행중이 아닙니다.", "연결 실패!");

                Application.Exit();

            }


            message = "채팅 서버에 연결 되었습니다.";

            DisplayText(message);


            byte[] buffer = Encoding.Unicode.GetBytes(Form1.nickname + "$");

            stream.Write(buffer, 0, buffer.Length);

            stream.Flush();


            Thread t_handler = new Thread(GetMessage);

            t_handler.IsBackground = true;

            t_handler.Start();

        }


        private void button1_Click(object sender, EventArgs e) // 메세지 보내기

        {

            textBox2.Focus();

            byte[] buffer = Encoding.Unicode.GetBytes(textBox2.Text + "$");

            stream.Write(buffer, 0, buffer.Length);

            stream.Flush();

            textBox2.Text = "";

        }


        private void GetMessage() // 메세지 받기

        {

            while (true)

            {

                stream = clientSocket.GetStream();

                int BUFFERSIZE = clientSocket.ReceiveBufferSize;

                byte[] buffer = new byte[BUFFERSIZE];

                int bytes = stream.Read(buffer, 0, buffer.Length);


                string message = Encoding.Unicode.GetString(buffer, 0, bytes);

                DisplayText(message);

            }

        }


        private void DisplayText(string text) // Server에 메세지 출력

        {

            if (textBox1.InvokeRequired)

            {

                textBox1.BeginInvoke(new MethodInvoker(delegate

                {

                    textBox1.AppendText(text + Environment.NewLine);

                }));

            }

            else

                textBox1.AppendText(text + Environment.NewLine);

        }


        private void textBox2_KeyUp(object sender, KeyEventArgs e)

        {

            if (e.KeyCode == Keys.Enter) // 엔터키 눌렀을 때

                button1_Click(this, e);

        }


        private void Form2_FormClosing(object sender, FormClosingEventArgs e) // 폼 닫을 때 실행

        {

            byte[] buffer = Encoding.Unicode.GetBytes("leaveChat" + "$");

            stream.Write(buffer, 0, buffer.Length);

            stream.Flush();

            Application.ExitThread();

            Environment.Exit(0);

        }

    }

}





정보가 유익하셨다면 아래 공감버튼 눌러주시면 감사하겠습니다.

질문사항은 댓글로 달아주시면 성의껏 답변해드리겠습니다.



to Top