Monday, August 24, 2009

How to create an async Server/Client sockets program


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;

namespace SocketsLearning
{

public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
void SetTextEventLog(string text)
{
txtEventLog.Text += text + Environment.NewLine;
}
void acceptCallback(IAsyncResult ar)
{
if (txtEventLog.InvokeRequired)
{
txtEventLog.Invoke(new SetTextCallback(SetTextEventLog), new object[] { "Server is reading sent data (Invoke)" });
}
else
SetTextEventLog("Server is reading sent data (Not Invoke)");
// Add the callback code here.
Socket listener = (Socket)ar.AsyncState;
Socket handler = listener.EndAccept(ar);
listener.BeginAccept(new AsyncCallback(acceptCallback), listener);

// Create the state object.
StateObject state = new StateObject();
state.workSocket = handler;
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(readCallback), state);


}
void readCallback(IAsyncResult ar)
{

StateObject state = (StateObject)ar.AsyncState;
Socket handler = state.workSocket;

// Read data from the client socket.
int read = handler.EndReceive(ar);

// Data was read from the client socket.
if (read > 0)
{
state.sb.Append(Encoding.ASCII.GetString(state.buffer,0,read));
handler.BeginReceive(state.buffer,0,StateObject.BufferSize, 0,
new AsyncCallback(readCallback), state);

}
else
{
if (state.sb.Length > 1) {
// All the data has been read from the client;
// display it on the console.
string content = state.sb.ToString();
if (txtRecieve.InvokeRequired)
{
txtRecieve.Invoke(
new SetTextCallback(SetText), new object[] { content + " (Invoke)" });
}
else
SetText(content + " (not Invoke)");
}
handler.Close();
}
}

private void SetText(string text)
{
txtRecieve.Text += text + Environment.NewLine;
}
delegate void SetTextCallback(string text);
private void txtStartListening_Click(object sender, EventArgs e)
{
try
{
IPAddress ip = IPAddress.Parse(txtIP.Text);
IPEndPoint ipen = new IPEndPoint(ip, Convert.ToInt32(txtPort.Text));
Socket soc = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
soc.Bind(ipen);
soc.Listen(1000);
txtEventLog.Text += "Server listening to (" + ip.ToString() + ":" + txtPort.Text + ")" + Environment.NewLine;
soc.BeginAccept(new AsyncCallback(acceptCallback), soc);

}
catch
{
txtEventLog.Text += "Error while trying to listen to IP/port" + Environment.NewLine;
}

}

private void button2_Click(object sender, EventArgs e)
{
IPAddress ip = IPAddress.Parse(txtClientIP.Text);
IPEndPoint ipen = new IPEndPoint(ip, Convert.ToInt32(txtClientPort.Text));
Socket sendsoc = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
sendsoc.BeginConnect(ipen, new AsyncCallback(connectCallback), sendsoc);

}
void connectCallback(IAsyncResult ar)
{
// Retrieve the socket from the state object.
Socket client = (Socket)ar.AsyncState;

// Complete the connection.
client.EndConnect(ar);
if (txtEventLog.InvokeRequired)
{
txtEventLog.Invoke(new SetTextCallback(SetTextEventLog), new object[] { "Client connected to server (Invoke)" });
}
else
SetTextEventLog("Client connected to server (not Invoke)");

// Convert the string data to byte data using ASCII encoding.
byte[] byteData = Encoding.ASCII.GetBytes(txtSend.Text);

// Begin sending the data to the remote device.
client.BeginSend(byteData, 0, byteData.Length, SocketFlags.None,
new AsyncCallback(sendCallBack), client);


}
void sendCallBack(IAsyncResult ar)
{
Socket client = (Socket)ar.AsyncState;
int bytesSent = client.EndSend(ar);
if (txtEventLog.InvokeRequired)
{
txtEventLog.Invoke(new SetTextCallback(SetTextEventLog), new object[] { "Client sent " + bytesSent.ToString() + " bytes to client (Invoke)" });
}
else
SetTextEventLog("Client sent " + bytesSent.ToString() + " bytes to client (Invoke)");
client.Close();
}

}

public class StateObject
{
public Socket workSocket = null;
public const int BufferSize = 1024;
public byte[] buffer = new byte[BufferSize];
public StringBuilder sb = new StringBuilder();
}
}

Appendum:
The server can send a message back to the client very easily. At first I thought this required creating a client socket, but no; All you need to do is use Send with the worker Socket in the "BeginRecieve" callback: workersocket.Send(stuff). Thanks to reference #4 for that.

References:
1. http://www.codeguru.com/csharp/csharp/cs_network/sockets/article.php/c7695
2. http://msdn.microsoft.com/en-us/library/dz10xcwh.aspx
3. http://msdn.microsoft.com/en-us/library/6aes97be.aspx
4. http://blog.xploiter.com/c-and-aspnet/socket-programming-in-c-using-the-built-in-libraries-a-fully-working-production-example-part-1/

No comments:

Post a Comment

There was an error in this gadget