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