안녕하세요. 명월입니다.
이번 글은 비동기 소켓 클라이언트에 대해 설명하겠습니다.
저번 포스팅에서도 확인했지만 먼저 결과화면부터 확인하겠습니다.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
namespace AsyncClient
{
public struct Telegram
{
private int m_DataLength;
private byte[] m_Data;
public void SetLength(byte[] Data)
{
if (Data.Length < 4)
{
return;
}
m_DataLength = BitConverter.ToInt32(Data, 0);
}
public int DataLength
{
get { return m_DataLength; }
}
public void InitData()
{
m_Data = new byte[m_DataLength];
}
public byte[] Data
{
get { return m_Data; }
set { m_Data = value; }
}
public String GetData()
{
return Encoding.Unicode.GetString(m_Data);
}
public byte[] GetBuffer()
{
return new byte[4];
}
public void SetData(String Data)
{
m_Data = Encoding.Unicode.GetBytes(Data);
m_DataLength = m_Data.Length;
}
}
enum ChatType
{
Send,
Receive,
System
}
class AsyncClient
{
private Socket m_Client = null;
private List<stringbuilder> m_Display = null;
private int m_Line;
static void Main(string[] args)
{
new AsyncClient();
}
public void DataInput()
{
String sData;
Telegram _telegram = new Telegram();
SendDisplay("ChattingProgram ClientStart", ChatType.System);
while (true)
{
sData = Console.ReadLine();
if (sData.CompareTo("exit") == 0)
{
break;
}
else
{
if (m_Client != null)
{
if (!m_Client.Connected)
{
m_Client = null;
SendDisplay("Connection Failed!", ChatType.System);
SendDisplay("Press Any Key...", ChatType.System);
}
else
{
_telegram.SetData(sData);
SocketAsyncEventArgs _sendArgs = new SocketAsyncEventArgs();
_sendArgs.SetBuffer(BitConverter.GetBytes(_telegram.DataLength), 0, 4);
_sendArgs.Completed += new EventHandler<socketasynceventargs>(Send_Completed);
_sendArgs.UserToken = _telegram;
m_Client.SendAsync(_sendArgs);
}
}
else
{
break;
}
}
}
}
public void SendDisplay(String nMessage, ChatType nType)
{
StringBuilder buffer = new StringBuilder();
switch (nType)
{
case ChatType.Send:
buffer.Append("SendMessage : ");
break;
case ChatType.Receive:
buffer.Append("ReceiveMessage : ");
break;
case ChatType.System:
buffer.Append("SystemMessage : ");
break;
}
buffer.Append(nMessage);
if (m_Line < 20)
{
m_Display.Add(buffer);
}
else
{
m_Display.RemoveAt(0);
m_Display.Add(buffer);
}
m_Line++;
Console.Clear();
for (int i = 0; i < 20; i++)
{
if (i < m_Display.Count)
{
Console.WriteLine(m_Display[i].ToString());
}
else
{
Console.WriteLine();
}
}
Console.Write("Input Message (exit - 終了): ");
}
public AsyncClient()
{
m_Display = new List<stringbuilder>();
m_Line = 0;
Socket _client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint _ipep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8000);
SocketAsyncEventArgs _args = new SocketAsyncEventArgs();
_args.RemoteEndPoint = _ipep;
_args.Completed += new EventHandler<socketasynceventargs>(Connect_Completed);
_client.ConnectAsync(_args);
DataInput();
}
private void Connect_Completed(object sender, SocketAsyncEventArgs e)
{
m_Client = (Socket)sender;
if (m_Client.Connected)
{
Telegram _telegram = new Telegram();
SocketAsyncEventArgs _receiveArgs = new SocketAsyncEventArgs();
_receiveArgs.UserToken = _telegram;
_receiveArgs.SetBuffer(_telegram.GetBuffer(), 0, 4);
_receiveArgs.Completed += new EventHandler<socketasynceventargs>(Recieve_Completed);
m_Client.ReceiveAsync(_receiveArgs);
SendDisplay("Server Connection Success", ChatType.System);
}
else
{
m_Client = null;
SendDisplay("Connection Failed!", ChatType.System);
SendDisplay("Press Any Key...", ChatType.System);
}
}
private void Send_Completed(object sender, SocketAsyncEventArgs e)
{
Socket _client = (Socket)sender;
Telegram _telegram = (Telegram)e.UserToken;
_client.Send(_telegram.Data);
SendDisplay(_telegram.GetData(), ChatType.Send);
}
private void Recieve_Completed(object sender, SocketAsyncEventArgs e)
{
Socket _client = (Socket)sender;
Telegram _telegram = (Telegram)e.UserToken;
_telegram.SetLength(e.Buffer);
_telegram.InitData();
if (_client.Connected)
{
_client.Receive(_telegram.Data, _telegram.DataLength, SocketFlags.None);
SendDisplay(_telegram.GetData(), ChatType.Receive);
_client.ReceiveAsync(e);
}
else
{
SendDisplay("Connection Failed!", ChatType.System);
m_Client = null;
}
}
}
}
생성자 부분을 확인하겠습니다.
클라이언트 접속 부분입니다. 선언 부분은 서버와 다르네요. 이 부분은 굳이 비동기가 아니어도 상관없기 때문에 동기식처럼 직접 Connect 클래스를 받고 처리했습니다.
서버는 바인딩해서 Listen 을 하고 클라이언트는 Connect를 하는 것입니다.
이 부분은 접속 Send, Recieve 이벤트 부분입니다. 서버와 크게 다른게 없습니다.
단지 차이라고 하면 서버는 다중접속이 가능하게 하므로 Accept 완료 후에 재 대기 형태로 진입하지만 클라이언트는 하나의 커넥션만 유지하는 형태이기 때문에 접속이 완료 대고 재 Connection을 호출하지는 않습니다.( 물론 개발의 사양에 따라선 다중 접속이 필요할때도 있습니다. 프로그램은 정답이 없습니다. ^^)
관련 소스를 올리겠습니다.
질문이나 궁금한점이 있으면 메일이나 댓글 주세요...
'Development note > C#' 카테고리의 다른 글
[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 |
[C#] 비동기 소켓 채팅 프로그램 - 서버편 (7) | 2013.09.28 |
[C#] 파일 다운로드 예제 - 클라이언트 폼, 메인 폼 초기화, 이벤트 (0) | 2013.02.27 |
[C#] 파일 다운로드 예제 - 서버 폼 초기화, 이벤트 (0) | 2013.02.26 |
[C#] 파일 다운로드 예제 - 소켓 부품 만들기 (0) | 2013.02.25 |