📁 [4] 개발자 정보 & 코드 노트/C#

C# Windows Forms 강의 87편: 실시간 채팅 애플리케이션 제작 - TCP 소켓 통신 활용

wawManager 2025. 5. 1. 12:00

 

1. 강의 개요

이번 강의에서는 TCP 소켓 통신을 활용한 실시간 채팅 애플리케이션을 제작합니다.
서버와 클라이언트 간의 연결을 설정하고,
텍스트 메시지를 주고받는 기능을 구현합니다.
C#의 System.Net.Sockets 네임스페이스를 활용해
네트워크 기반의 실시간 통신 애플리케이션 개발을 실습합니다.


2. 학습 목표

  • TCP 서버 및 클라이언트 구현
  • 메시지 송수신 기능 구현
  • 멀티스레드를 사용해 비동기 통신 처리
  • 사용자 친화적인 채팅 UI 제작

3. 기능 요구사항

필수 기능

1️⃣ 서버 기능:

  • 클라이언트의 연결을 수락하고 메시지를 송수신

2️⃣ 클라이언트 기능:

  • 서버에 연결하고 메시지를 송수신

3️⃣ 채팅 UI 구성:

  • 텍스트 입력 및 송신, 메시지 로그 표시

4️⃣ 멀티스레드 처리:

  • 클라이언트와 서버의 비동기 통신 처리

4. 실습: 실시간 채팅 애플리케이션 제작

1️⃣ 폼 구성

  • 폼(Form) 이름: Form1
  • 컨트롤 배치:

컨트롤 타입 이름 위치 크기

TextBox txtMessageLog 폼 상단 전체 (400 x 300)
TextBox txtInput 폼 하단 왼쪽 (300 x 30)
Button btnSend 폼 하단 오른쪽 (100 x 30)

📌 폼 디자인 예시:

--------------------------------------------------
|          [TextBox - 메시지 로그 표시]          |
--------------------------------------------------
| [Message Input TextBox]  [Send 버튼]           |
--------------------------------------------------

2️⃣ 코드 작성

(1) TCP 서버 구현

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Windows.Forms;

namespace WindowsFormsApp_Chat
{
    public partial class Form1 : Form
    {
        private TcpListener _server;
        private Thread _serverThread;

        public Form1()
        {
            InitializeComponent();
        }

        // 서버 시작
        private void StartServer()
        {
            _server = new TcpListener(IPAddress.Any, 9000);
            _server.Start();

            _serverThread = new Thread(() =>
            {
                while (true)
                {
                    try
                    {
                        TcpClient client = _server.AcceptTcpClient();
                        Thread clientThread = new Thread(() => HandleClient(client));
                        clientThread.Start();
                    }
                    catch
                    {
                        break; // 서버 종료 시 스레드 종료
                    }
                }
            });

            _serverThread.IsBackground = true;
            _serverThread.Start();

            AppendMessage("서버가 시작되었습니다.");
        }

        // 클라이언트 처리
        private void HandleClient(TcpClient client)
        {
            NetworkStream stream = client.GetStream();
            byte[] buffer = new byte[1024];

            try
            {
                while (true)
                {
                    int bytesRead = stream.Read(buffer, 0, buffer.Length);
                    if (bytesRead == 0) break;

                    string message = Encoding.UTF8.GetString(buffer, 0, bytesRead);
                    AppendMessage($"[클라이언트]: {message}");
                }
            }
            catch
            {
                AppendMessage("클라이언트 연결이 종료되었습니다.");
            }
            finally
            {
                client.Close();
            }
        }

        // 메시지 로그에 추가
        private void AppendMessage(string message)
        {
            Invoke(new Action(() =>
            {
                txtMessageLog.AppendText(message + Environment.NewLine);
            }));
        }
    }
}

(2) TCP 클라이언트 구현

        private TcpClient _client;
        private NetworkStream _stream;

        // 클라이언트 연결
        private void ConnectToServer()
        {
            try
            {
                _client = new TcpClient("127.0.0.1", 9000); // 로컬호스트 서버 연결
                _stream = _client.GetStream();

                Thread clientThread = new Thread(ReceiveMessages);
                clientThread.IsBackground = true;
                clientThread.Start();

                AppendMessage("서버에 연결되었습니다.");
            }
            catch
            {
                AppendMessage("서버에 연결할 수 없습니다.");
            }
        }

        // 메시지 수신
        private void ReceiveMessages()
        {
            byte[] buffer = new byte[1024];

            try
            {
                while (true)
                {
                    int bytesRead = _stream.Read(buffer, 0, buffer.Length);
                    if (bytesRead == 0) break;

                    string message = Encoding.UTF8.GetString(buffer, 0, bytesRead);
                    AppendMessage($"[서버]: {message}");
                }
            }
            catch
            {
                AppendMessage("서버 연결이 종료되었습니다.");
            }
        }

        // 메시지 송신
        private void SendMessage(string message)
        {
            if (_stream == null) return;

            byte[] buffer = Encoding.UTF8.GetBytes(message);
            _stream.Write(buffer, 0, buffer.Length);
        }

(3) UI 이벤트 핸들러

        // Send 버튼 클릭 이벤트
        private void btnSend_Click(object sender, EventArgs e)
        {
            string message = txtInput.Text.Trim();

            if (!string.IsNullOrEmpty(message))
            {
                SendMessage(message);
                AppendMessage($"[나]: {message}");
                txtInput.Clear();
            }
        }

(4) Designer 코드

        private void InitializeComponent()
        {
            this.txtMessageLog = new TextBox();
            this.txtInput = new TextBox();
            this.btnSend = new Button();

            // Message Log TextBox 설정
            this.txtMessageLog.Location = new System.Drawing.Point(10, 10);
            this.txtMessageLog.Size = new System.Drawing.Size(400, 300);
            this.txtMessageLog.Multiline = true;
            this.txtMessageLog.ScrollBars = ScrollBars.Vertical;
            this.txtMessageLog.ReadOnly = true;

            // Message Input TextBox 설정
            this.txtInput.Location = new System.Drawing.Point(10, 320);
            this.txtInput.Size = new System.Drawing.Size(300, 30);

            // Send Button 설정
            this.btnSend.Location = new System.Drawing.Point(320, 320);
            this.btnSend.Size = new System.Drawing.Size(80, 30);
            this.btnSend.Text = "Send";
            this.btnSend.Click += new EventHandler(this.btnSend_Click);

            // Form 설정
            this.ClientSize = new System.Drawing.Size(420, 360);
            this.Controls.Add(this.txtMessageLog);
            this.Controls.Add(this.txtInput);
            this.Controls.Add(this.btnSend);
            this.Text = "실시간 채팅 애플리케이션";
        }

3️⃣ 실행 결과

1️⃣ 서버 실행

  • 서버를 시작하면 연결을 기다림

2️⃣ 클라이언트 연결

  • 클라이언트가 서버에 연결되면 대화 가능

3️⃣ 메시지 송수신

  • 클라이언트와 서버가 메시지를 주고받으며 실시간 대화

5. 주요 개념 요약

  • TcpListener: 서버에서 클라이언트 연결을 수락
  • TcpClient: 클라이언트가 서버에 연결
  • NetworkStream: 네트워크 스트림을 통해 데이터 송수신
  • 멀티스레드: 다중 클라이언트 처리를 위한 비동기 처리

 

 

📌 #CSharp #WindowsForms #TCP #소켓통신 #실시간채팅 #멀티스레드