Friday, December 5, 2008

C# Client Network Library

C# 하면서 만들었던 거의 기억보존용.

아직 테스트 중이고, 살펴보면 허술한 부분도 많지만
뭐 간단하게 동작하는 데는 큰 문제 없음.

주요 특징 :
- Async 를 걸고 IOCP 에서 return 받는 간단한 구조.
- 방식은 proactor 이지만 thread 가 하나이기 때문에 뭐라 지칭하긴 그렇고, 그냥 비동기 콜임. :)
- boost::asio 를 많이 모방했음. 그래서 함수 이름이나, packet 을 읽는 방식이 거의 같음. -0-;
- connect 는 sync, send/recv 는 async.

Packet type - namespace Protocol

namespace Protocol
{
public struct packet_header
{
public System.UInt16 index_;
public System.UInt16 body_length_;
}

public struct packet
{
public packet_header h_;
public System.Byte[] data_;
}

public class packet_size
{
public static readonly int header_size;
public static readonly int max_body_size;
public static readonly int max_packet_size;

static packet_size()
{
header_size = Marshal.SizeOf(typeof(packet_header));
max_body_size = 1024;
max_packet_size = header_size + max_body_size;
}
}
}


Network Library - namespace NetworkLib

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;

using Protocol;

namespace network_lib
{
public class packet_args : EventArgs
{
private packet p_;
public packet_args(packet p)
{
p_ = p;
}

public packet data
{
get { return p_; }
}
}
public delegate void packet_event(object sender, packet_args p);

class packet_parse
{
public event packet_event e_;
public void do_packet_parse(packet p)
{
if (e_ != null)
e_(this, new packet_args(p));
}
}

public class client_session
{
#region Private Variables

private string ip_;
private string port_;
private Socket client_socket_;

private packet read_packet_;
private Byte[] read_buffer_;
private Byte[] write_buffer_;

private IAsyncResult result_;
private AsyncCallback cb_async_read_body_;
private AsyncCallback cb_handle_read_;
private AsyncCallback cb_handle_write_;

private packet_parse event_packet_;

#endregion

#region Constructors

public client_session()
{
init();
}

public client_session(string ip, string port)
{
ip_ = ip;
port_ = port;

init();
}

#endregion

#region Properties

public string IP
{
get { return ip_; }
set { ip_ = value; }
}

public string Port
{
get { return port_; }
set { port_ = value; }
}

public bool connected
{
get { return (client_socket_ != null) ? client_socket_.Connected: false; }
}

#endregion

#region Public Methods

public void add_packet_event(packet_event h)
{
event_packet_.e_ += h;
}

public void connect()
{
try
{
IPEndPoint ip = new IPEndPoint(IPAddress.Parse(ip_), System.Convert.ToUInt16(port_));
client_socket_ = new Socket(System.Net.Sockets.AddressFamily.InterNetwork,
System.Net.Sockets.SocketType.Stream,
System.Net.Sockets.ProtocolType.Tcp);
client_socket_.Connect(ip);
if (client_socket_.Connected)
do_async_read();
}
catch (SystemException se)
{
MessageBox.Show(se.Message);
}
}

public void disconnect()
{
if (connected)
client_socket_.Disconnect(false);
}

public void do_async_read()
{
try
{
// do_async_read_head
result_ = client_socket_.BeginReceive(read_buffer_,
0,
packet_size.header_size,
SocketFlags.None,
cb_async_read_body_,
read_buffer_);
}
catch (SocketException se)
{
MessageBox.Show(se.Message);
}
}

public void do_async_write(packet p)
{
try
{
packet_header heaer = p.h_;
System.Byte[] buffer = raw_serialize_ex(heaer);
buffer.CopyTo(write_buffer_, 0);
p.data_.CopyTo(write_buffer_, packet_size.header_size);

// do_async_write
result_ = client_socket_.BeginSend(write_buffer_,
0,
packet_size.header_size + p.h_.body_length_,
SocketFlags.None,
cb_handle_write_,
write_buffer_);
}
catch (SocketException se)
{
MessageBox.Show(se.Message);
}
}

// copied from group.google.com
public object raw_deserialize_ex(byte[] rawdatas, Type anytype)
{
int rawsize = Marshal.SizeOf(anytype);
if (rawsize > rawdatas.Length)
return null;
GCHandle handle = GCHandle.Alloc(rawdatas, GCHandleType.Pinned);
IntPtr buffer = handle.AddrOfPinnedObject();
object retobj = Marshal.PtrToStructure(buffer, anytype);
handle.Free();
return retobj;
}

// copied from group.google.com
public byte[] raw_serialize_ex(object anything)
{
int rawsize = Marshal.SizeOf(anything);
byte[] rawdatas = new byte[rawsize];
GCHandle handle = GCHandle.Alloc(rawdatas, GCHandleType.Pinned);
IntPtr buffer = handle.AddrOfPinnedObject();
Marshal.StructureToPtr(anything, buffer, false);
handle.Free();
return rawdatas;
}

#endregion

#region Private Methods

private void init()
{
client_socket_ = null;

cb_async_read_body_ = new AsyncCallback(do_async_read_body);
cb_handle_read_ = new AsyncCallback(cb_handle_read);
cb_handle_write_ = new AsyncCallback(cb_handle_write);

read_packet_.data_ = new System.Byte[packet_size.max_body_size];
read_buffer_ = new System.Byte[packet_size.max_packet_size];
write_buffer_ = new System.Byte[packet_size.max_packet_size];

event_packet_ = new packet_parse();
}

private void do_async_read_body(IAsyncResult r)
{
try
{
int bytes_transferred = client_socket_.EndReceive(r);
if (bytes_transferred == 0)
{
// normal close
return;
}

if (bytes_transferred != packet_size.header_size)
{
MessageBox.Show(" !Error - invalid received header size : " + bytes_transferred);
return;
}

packet_header header;
System.Byte[] header_buffer = new System.Byte[packet_size.header_size];
System.Buffer.BlockCopy(read_buffer_, 0, header_buffer, 0, packet_size.header_size);
header = (packet_header)raw_deserialize_ex(header_buffer, typeof(packet_header));

read_packet_.h_.index_ = header.index_;
read_packet_.h_.body_length_ = header.body_length_;

// do_async_read_body
result_ = client_socket_.BeginReceive(read_buffer_,
packet_size.header_size,
header.body_length_,
SocketFlags.None,
cb_handle_read_,
read_buffer_);
}
catch (SocketException se)
{
MessageBox.Show(se.Message);
}
}

private void cb_handle_read(IAsyncResult r)
{
try
{
int bytes_transferred = client_socket_.EndReceive(r);
if (bytes_transferred > packet_size.max_body_size)
{
MessageBox.Show(" !Error - invalid received body size : " + bytes_transferred);
return;
}

System.Buffer.BlockCopy(read_buffer_, packet_size.header_size, read_packet_.data_, 0, bytes_transferred);

// parse packet
event_packet_.do_packet_parse(read_packet_);

// do_async_read
do_async_read();
}
catch (SocketException se)
{
MessageBox.Show(se.Message);
}
}

private void cb_handle_write(IAsyncResult r)
{
int bytes_transferred = client_socket_.EndSend(r);
string msg = "packet has been sent. bytes : " + bytes_transferred + "\n";
}

#endregion
}
}

No comments:

Post a Comment