본문 바로가기
Study/C#

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

by wawManager 2025. 5. 1.

 

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 #소켓통신 #실시간채팅 #멀티스레드