본문 바로가기

C#/개발

[C#] 서버 - 클라이언트 1 : N 비동기 채팅

서버에 클라이언트를 여러개 연결할 수 있고, 한 클라이언트가 메세지를 전송하면 서버는 해당 메세지를 연결된 모든 클라이언트에 Broadcast 한다. 클라이언트마다 메세지 수신을 위한 스레드가 동작하는데, 스레드가 끝나면 프로세스도 끝나는 걸 방지하기 위해 배경 스레드로 설정한다. 서버는 연결된 클라이언트마다 소켓과 스레드를 생성하여 관리한다.

 

MessageBox 잔상이 심령사진급

 

Server (Console)

 

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

namespace ConsoleServer
{
    class Program
    {
        static void Main(string[] args)
        {
            Server server = new Server();
            server.Echo();
        }
        public class Server
        {
            public const int PORT = 5555;
            TcpListener listener = null;
            public static ArrayList handleList = new ArrayList(10);
            public Server()
            {
                handleList.Clear();
            }
            public void Echo()
            {
                try
                {
                    IPAddress address = Dns.GetHostEntry("").AddressList[0];
                    //listener = new TcpListener(address, PORT);
                    listener = new TcpListener(IPAddress.Any, PORT);
                    listener.Start();
                    Console.WriteLine("Server ready 1-------");
                    while (true)
                    {
                        TcpClient client = listener.AcceptTcpClient(); //클라이언트 개당 소켓생성
                        EchoHandler handler = new EchoHandler(this, client);
                        Add(handler);
                        handler.start();
                    }
                }
                catch (Exception ee)
                {
                    Console.WriteLine("2--------------------");
                    System.Console.WriteLine(ee.Message);
                }
                finally
                {
                    Console.WriteLine("3--------------------");
                    listener.Stop();
                }
            }
            public void Add(EchoHandler handler)
            {
                lock (handleList.SyncRoot)
                    handleList.Add(handler);
            }
            public void broadcast(String str)
            {
                lock (handleList.SyncRoot)
                {
                    string dstes = DateTime.Now.ToString() + " : ";
                    Console.Write(dstes);
                    Console.WriteLine(str);
                    foreach (EchoHandler handler in handleList)
                    {
                        EchoHandler echo = handler as EchoHandler;
                        if (echo != null)
                            echo.sendMessage(str);
                    }
                }
            }
            public void Remove(EchoHandler handler)
            {
                lock (handleList.SyncRoot)
                    handleList.Remove(handler);
            }
        }
        public class EchoHandler
        {
            Server server;
            TcpClient client;
            NetworkStream ns = null;
            StreamReader sr = null;
            StreamWriter sw = null;
            string str = string.Empty;

            string clientName;
            public EchoHandler(Server server, TcpClient client)
            {
                this.server = server;
                this.client = client;
                try
                {
                    ns = client.GetStream();
                    Socket socket = client.Client;
                    clientName = socket.RemoteEndPoint.ToString();
                    Console.WriteLine(clientName + " 접속");
                    sr = new StreamReader(ns, Encoding.Default);
                    sw = new StreamWriter(ns, Encoding.Default);
                }
                catch (Exception) { Console.WriteLine("연결 실패"); }
            }
            public void start()
            {
                Thread t = new Thread(new ThreadStart(ProcessClient));
                t.Start();
            }
            public void ProcessClient()
            {
                try
                {
                    while ((str = sr.ReadLine()) != null)
                        server.broadcast(str);
                }
                catch (Exception)
                {
                    Console.WriteLine(clientName + " 접속해제");
                    sw.Flush();
                }
                finally
                {
                    server.Remove(this);
                    sw.Close();
                    sr.Close();
                    client.Close();
                }
            }
            public void sendMessage(string message){
                sw.WriteLine(message);
                sw.Flush();
            }
        }
    }
}

 

Client (WinForm)

 

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Threading;

namespace Chatting
{
    public partial class Form1 : Form
    {
        string serverIP, dialogName;
        int serverPort;
        bool isAlive = false;
        NetworkStream ns = null;
        StreamReader sr = null;
        StreamWriter sw = null;
        TcpClient client = null;

        public Form1()
        {
            InitializeComponent();
            IPHostEntry hostIP = Dns.GetHostByName(Dns.GetHostName());
            serverIP = hostIP.AddressList[0].ToString();
        }
        private void Form1_Load(object sender, EventArgs e)
        {
            this.txtPort.Text = 5555.ToString();
            this.txtIP.Text = serverIP.ToString();
            this.txtPort.Enabled = false;
            this.txtIP.Enabled = false;
        }
        private void btnConnect_Click(object sender, EventArgs e)
        {
            dialogName = this.txtName.Text;
            if (string.IsNullOrEmpty(dialogName))
            {
                MessageBox.Show("대화명을 입력하세요.");
                return;
            }
            if (string.IsNullOrEmpty(serverIP))
            {
                MessageBox.Show("주소를 입력하세요.");
                return;
            }
            if (string.IsNullOrEmpty(this.txtPort.Text))
            {
                MessageBox.Show("포트를 입력하세요.");
                return;
            }
            serverPort = Int32.Parse(this.txtPort.Text);
            isAlive = true;
            try
            {
                this.Echo();
                sendMessage("[" + dialogName + " 입장]");
                //this.txtSend.Focus();
            }
            catch (Exception)
            {
                this.txtName.Clear();
                this.isAlive = false;
            }
        }
        private void txtSend_KeyPress(object sender, KeyPressEventArgs e)
        {
            if ((int)Keys.Enter == e.KeyChar)
            {
                string message = this.txtSend.Text;
                sendMessage("[" + dialogName + "] " + message.Trim());
                this.txtSend.Clear();
                this.txtSend.SelectionStart = 0;
            }
        } 
        private void btnExit_Click(object sender, EventArgs e)
        {
            try
            {
                sendMessage("[" + dialogName + " 퇴장]");
                sr.Close();
                sw.Close();
                ns.Close();
            }
            catch { }
            finally
            {
                this.Dispose();
            }
        }
        public void Echo()
        {
            try
            {
                client = new TcpClient(this.serverIP, this.serverPort);
                ns = client.GetStream();
                sr = new StreamReader(ns, Encoding.Default);
                sw = new StreamWriter(ns, Encoding.Default);
                Thread receiveThread = new Thread(new ThreadStart(run));
                receiveThread.IsBackground = true;
                receiveThread.Start();
            }
            catch (Exception e)
            {
                MessageBox.Show("서버 시작 실패");
                throw e;
            }
        }
        public void run()
        {
            string message = "start";
            try
            {
                if (client.Connected && sr != null)
                    while ((message = sr.ReadLine()) != null)
                        AppendMessage(message);
            }
            catch (Exception) { MessageBox.Show("error"); }
        }
        public void AppendMessage(string message)
        {
            if (this.txtDialog != null && this.txtSend != null)
            {
                this.txtDialog.AppendText(message + "\r\n");
                this.txtDialog.Focus();
                this.txtDialog.ScrollToCaret();
            }
        }
        private void sendMessage(string message)
        {
            try
            {
                if (sw != null)
                {
                    sw.WriteLine(message);
                    sw.Flush();
                }
            }
            catch (Exception) { MessageBox.Show("전송실패"); }
        }
    }
}

'C# > 개발' 카테고리의 다른 글

[C#] 데이터베이스 관리  (0) 2022.08.05
[C#] 웹 브라우저  (0) 2022.08.05
[C#] 지하철 최단거리  (0) 2022.08.05
[C#] 숫자 야구  (0) 2022.08.05
[C#] 아날로그 시계  (0) 2022.08.05