안녕하세요. 명월입니다.
참 오랫만에 C# 코드를 작성하는 것 같습니다. 요즘 우분투와 자바 매력에 흠뻑(?) 빠져들어서 C#코드는 거의 다루지를 못하고 있습니다.
소켓 통신에 있어서 특정 클라이언트만 메시지를 보내고 싶다라는 질문이 있어서 간단한 예제 소스를 작성해 봤습니다.
결과를 확인 해보시면 위쪽부터 3개는 클라인트 맨 밑은 서버로 구성되어있습니다.
그럼 메시지를 전송해 보겠습니다.
자 결과를 보시면 test1한테는 hi, test2는 hello, test3은 gogogo를 보냈는데 원하는 클라이언트에게만 메시지가 전송이 되었습니다.
그럼 서버 소스를 확인 해 보도록 하겠습니다.
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
namespace server
{
class Program
{
static void Main(string[] args)
{
new Program();
}
private Dictionary<string, socket> m_client = null;
public Program()
{
initialize();
}
protected void initialize()
{
m_client = new Dictionary<string, socket="">();
Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
server.Bind(new IPEndPoint(IPAddress.Any, 10000));
server.Listen(20);
SocketAsyncEventArgs sockAsync = new SocketAsyncEventArgs();
sockAsync.Completed += new EventHandler<socketasynceventargs>(sockAsync_Completed);
server.AcceptAsync(sockAsync);
Command();
}
private void sockAsync_Completed(object sender, SocketAsyncEventArgs e)
{
Socket server = (Socket)sender;
Socket client = e.AcceptSocket;
byte[] name = new byte[100];
client.Receive(name);
String strName = Encoding.Unicode.GetString(name).Trim().Replace("\0", "");
m_client.Add(strName, client);
SocketAsyncEventArgs recieveAsync = new SocketAsyncEventArgs();
recieveAsync.SetBuffer(new byte[4], 0, 4);
recieveAsync.UserToken = client;
recieveAsync.Completed += new EventHandler<socketasynceventargs>(recieveAsync_Completed);
client.ReceiveAsync(recieveAsync);
Console.WriteLine();
Console.WriteLine("***********" + strName + " Connected ***********");
Console.Write("Sender ?");
e.AcceptSocket = null;
server.AcceptAsync(e);
}
private void recieveAsync_Completed(object sender, SocketAsyncEventArgs e)
{
Socket client = (Socket)sender;
if (client.Connected && e.BytesTransferred > 0)
{
int length = BitConverter.ToInt32(e.Buffer, 0);
byte[] data = new byte[length];
client.Receive(data, length, SocketFlags.None);
String data2 = Encoding.Unicode.GetString(data);
String Name = searchSocket(client);
if (Name != null)
{
Console.WriteLine();
Console.WriteLine("***********" + Name + " receive ***********");
Console.WriteLine(data2);
Console.Write("Sender ?");
}
else
{
}
}
client.ReceiveAsync(e);
}
protected String searchSocket(Socket sender)
{
foreach (String key in m_client.Keys)
{
if (m_client[key] == sender)
{
return key;
}
}
return null;
}
protected void Command()
{
while (true)
{
Console.Write("Sender ?");
String sender = Console.ReadLine();
if (m_client.ContainsKey(sender))
{
Console.Write("Message ?");
String message = Console.ReadLine();
Socket client = m_client[sender];
byte[] data = Encoding.Unicode.GetBytes(message);
client.Send(BitConverter.GetBytes(data.Length));
client.Send(data, data.Length, SocketFlags.None);
}
else
{
Console.WriteLine("Not Socket");
}
}
}
}
}
위 소스는 일단 서버 쪽 소스입니다. 급하게 작성한 것이라 예외처리도 없고 엉망이지만 개념만 이해하시고 프로젝트에 맞게 작성하시면 될 듯 싶습니다.
다음은 클라이언트 소스입니다.
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
namespace client
{
class Program
{
static void Main(string[] args)
{
new Program();
}
public Program()
{
initialize();
Console.WriteLine("************Message**********");
Console.ReadLine();
}
private String m_Name;
public void initialize()
{
Console.Write("Name?");
String strName = Console.ReadLine();
if (strName.Trim().Length > 0)
{
m_Name = strName;
Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
client.Connect(IPAddress.Parse("127.0.0.1"), 10000);
byte[] name = new byte[100];
byte[] buffer = Encoding.Unicode.GetBytes(strName);
for (int i = 0; i < 100; i++)
{
if (i < buffer.Length)
{
name[i] = buffer[i];
}
}
client.Send(name);
SocketAsyncEventArgs reciveAsync = new SocketAsyncEventArgs();
reciveAsync.Completed += new EventHandler<socketasynceventargs>(reciveAsync_Completed);
reciveAsync.SetBuffer(new byte[4], 0, 4);
reciveAsync.UserToken = client;
client.ReceiveAsync(reciveAsync);
}
}
private void reciveAsync_Completed(object sender, SocketAsyncEventArgs e)
{
Socket client = (Socket)sender;
if (client.Connected && e.BytesTransferred > 0)
{
byte[] lengthByte = e.Buffer;
int length = BitConverter.ToInt32(lengthByte, 0);
byte[] data = new byte[length];
client.Receive(data, length, SocketFlags.None);
StringBuilder sb = new StringBuilder();
sb.Append(m_Name);
sb.Append(" - ");
sb.Append(Encoding.Unicode.GetString(data));
Console.WriteLine(sb.ToString());
}
client.ReceiveAsync(e);
}
}
}
클라이언트는 그냥 일반 클라이언트 소스이기에 특별히 설명을 하지 않겠습니다.
참고 - [C#]비동기 소켓 채팅 프로그램 - 클라이언트 편
서버의 경우는 일반 서버와 비슷한데 서버를 찾는 작업이 필요하겠군요.
이전에는 클라이언트 List를 ArrayList에 저장했지만 이번에는 키가 필요한 관계로 Dictionary에 저장을 했습니다.
이제부터는 뭐랄까 제 나름대로의 규약(프로토콜)을 작성하겠습니다.
접속을 할때는 무조건 byte(100)짜리의 데이터로 이름을 보내야 접속이 되는 것으로 정했습니다. 클라이언트 소스를 보시면 접속을 할때 이름을 넣고 byte[100]으로 전문을 전송합니다.
그럼 서버쪽에서는 이름을 맞고 그 이름을 키로 등록을 하여ㅕ Dictionary에 등록을 합니다.
이제는 반대로 메시지를 보낼때 형식입니다.
보낼때는 어떤 클라이언트에게 보낼 지 검색을 해야겠지요. 그래서 유저를 검색을 하겠습니다. 그 키로 검색을 소켓에 Message를 넣어서 전송하면 해당클라이언트만 데이터가 수신이 되겠지요.. 이상입니다.
위 소스는 기본적으로 접속이 끊길 때의 처리,예외상황처리 등이 되어있지 않습니다. 즉 실 프로젝트에 적용하실때는 그 사양에 맞게 적용시키시기 바랍니다.
(예제 소스를 올리면 안 된다는 메일이 많이 와서 ㅜㅜ..예제 소스일 뿐이지 적용을 하실 때는 꼭 프로젝트 사양에 맞게 수정하여 사용해 주세요.)
Socket.zip'Development note > C#' 카테고리의 다른 글
[C#] NPOI를 이용하여 Excel를 읽어드리고 다시 출력하기 (0) | 2019.05.14 |
---|---|
[C#] NPOI를 이용한 엑셀 파일 만들기 (1) | 2019.05.14 |
[C#] Zip 압축 해제 코드 소스 (0) | 2019.05.10 |
[C#] 계산기 프로그램 (1) | 2015.06.07 |
[C#] 확장형 브라우져 AxWebBrowser -2 (4) | 2013.10.20 |
[C#] 비동기 웹서버 (4) | 2013.10.16 |
[C#] MetaWeblog (2) (1) | 2013.10.06 |
[C#] MetaWeblog (1) (0) | 2013.10.05 |