I have a project with a server and a client using asynchronous socket connections.
I want to transmit an object from server to client. Unfortunately I have the problem, that sometimes the object isn't fully transmitted in one go. Therefore I need a way to determine when an object was fully transmitted. So I added a four-byte-head to the data transmitted to tell, how long the stream will be.
Server
private void Send(Socket handler, Packet packet)
{
byte[] byteData = ByteHelper.Serialize(packet).Data;
byte[] byteDataLength = BitConverter.GetBytes(byteData.Length);
byte[] transmissionData = new byte[byteDataLength.Length + byteData.Length];
byteDataLength.CopyTo(transmissionData, 0);
byteData.CopyTo(transmissionData, byteDataLength.Length);
if (debug)
{
Status = "Sending "+packet.Type+"-packet to client.";
}
try
{
handler.BeginSend(transmissionData, 0, transmissionData.Length, 0, new AsyncCallback(SendCallback), handler);
}
catch (Exception ex)
{
Status = "[EXCEPTION]" + ex.Message.ToString();
}
}
The client receives the stream and evaluates the first four bytes to create a StateObject which has the right size. But I have the feeling that this is not really a good way to solve my problem. Is there a better way to do this?
Client
private void ReceiveCallback(IAsyncResult ar)
{
StateObject state = (StateObject)ar.AsyncState;
try
{
Socket client = state.workSocket;
int bytesRead = client.EndReceive(ar);
if (bytesRead > 0)
{
do
{
state = AdjustState(state);
if (state != null)
{
if (state.buffer.Length == state.bufferSize)
{
ProcessPacket(state);
receiveDone.Set();
}
}
else
{
receiveDone.Set();
}
}
while (state != null && state.tempBuffer.Length >= state.bufferSize);
if (state != null)
{
client.BeginReceive(state.tempBuffer, 0, state.tempBuffer.Length, 0, new AsyncCallback(ReceiveCallback), state);
}
}
}
catch (Exception ex)
{
MessageBox.Show("ReceiveCallback: " + ex.ToString(), "Client-Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private static StateObject AdjustState(StateObject state)
{
StateObject tempState = state;
if (tempState.tempBuffer.Length >= 4)
{
byte[] sizeBytes = tempState.tempBuffer.Take(4).ToArray();
int bufferSize = BitConverter.ToInt32(sizeBytes, 0);
if (bufferSize == 0)
{
return null;
}
byte[] temp = tempState.tempBuffer.Skip(4).ToArray();
Socket tempSocket = tempState.workSocket;
tempState = new StateObject(bufferSize);
tempState.BufferSet();
if (temp.Length >= bufferSize)
{
tempState.buffer = temp.Take(bufferSize).ToArray();
tempState.tempBuffer = temp.Skip(bufferSize).ToArray();
}
tempState.workSocket = tempSocket;
}
return tempState;
}
Solution
Thanks to usr I've changed the bytesRead-Part in the ReceiveCallbackCode of the client to this. It seems to work now.
if (bytesRead > 0)
{
if (!state.bufferSet)
{
state = AdjustState(state);
client.BeginReceive(state.buffer, 0, state.bufferSize, 0, new AsyncCallback(ReceiveCallback), state);
}
else
{
if (state.buffer.Length == state.bufferSize)
{
ProcessPacket(state);
receiveDone.Set();
}
else
{
client.BeginReceive(state.buffer, state.buffer.Length, state.bufferSize - state.buffer.Length, 0, new AsyncCallback(ReceiveCallback), state);
}
}
}
You are not making use of the return value from EndReceive (bytesRead). It signals how many bytes were received.
And in case bytesRead == 0 you just do nothing which is probably not a proper response to the connection having ended.
And then, there is this unsynchronized busy-loop:
while (state != null && state.tempBuffer.Length >= state.bufferSize);
That's going to burn one CPU core for each client connection. Must solve this differently.
Related
I have a server in C that send message to unity. In Unity, I can receive data once, but when the server sends a new message, unity receives nothing.
For example: Unity can connect to server, receive the first message that said "move right" and move the camera of Unity to the right but then if the server send "move left" or anything else unity is block in the function receive which calls BeginReceive:
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReceiveCallback), state);
Code of my server:
void connectToUnity() {
SOCKADDR_IN sin;
WSAStartup(MAKEWORD(2, 0), &WSAData);
sock = socket(AF_INET, SOCK_STREAM, 0);
sin.sin_addr.s_addr = inet_addr("127.0.0.1");
sin.sin_family = AF_INET;
sin.sin_port = htons(53660);
bind(sock, (SOCKADDR*)&sin, sizeof(sin));
listen(sock, 0);
printf("Connexion to unity\n");
}
void sendDataToUnity(const char * mouvement) {
SOCKET csock;
while (1)
{
int sinsize = sizeof(csin);
if ((csock = accept(sock, (SOCKADDR*)&csin, &sinsize)) != INVALID_SOCKET)
{
send(csock, mouvement, 14, 0);
printf("Donnees envoyees \n");
return;
}
else {
printf("Rien n'a ete envoye");
}
}
}
Code in Unity:
public bool ConnectToServer(string hostAdd, int port)
{
//connect the socket to the server
try
{
//create end point to connect
conn = new IPEndPoint(IPAddress.Parse(hostAdd), port);
//connect to server
clientSocket.BeginConnect(conn, new AsyncCallback(ConnectCallback), clientSocket);
socketReady = true;
connectDone.WaitOne();
Debug.Log("Client socket ready: " + socketReady);
// Receive the response from the remote device.
Receive(clientSocket);
//receiveDone.WaitOne();
}
catch (Exception ex)
{
Debug.Log("socket error: " + ex.Message);
}
return socketReady;
}
//async call to connect
static void ConnectCallback(IAsyncResult ar)
{
try
{
// Retrieve the socket
Socket client = (Socket)ar.AsyncState;
// Complete the connection
client.EndConnect(ar);
Debug.Log("Client Socket connected to: " + client.RemoteEndPoint);
connectDone.Set();
}
catch (Exception e)
{
Debug.Log("Error connecting: " + e);
}
}
private static void Receive(Socket client)
{
try
{
Debug.Log("Try to receive");
// Create the state object.
StateObject state = new StateObject();
state.workSocket = client;
// Begin receiving the data from the remote device.
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReceiveCallback), state);
}
catch (Exception e)
{
Debug.Log(e);
}
}
static void ReceiveCallback(IAsyncResult ar)
{
try
{
//Read data from the remote device.
Debug.Log("receive callback");
StateObject state = (StateObject)ar.AsyncState;
Socket client = state.workSocket;
int bytesRead = client.EndReceive(ar);
if (bytesRead > 0)
{
Debug.Log("Il y a une réponse");
state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead));
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReceiveCallback), state);
response = state.sb.ToString();
Debug.Log(response);
//client.Shutdown(SocketShutdown.Both);
}
else
{
if(state.sb.Length > 1)
{
response = state.sb.ToString();
Debug.Log(response);
}
}
}
catch (Exception ex)
{
Debug.Log("Error: " + ex.Message);
}
}
The function receive is called once in the function ConnectToServer, then I tried to call again in the update like this:
void Update()
{
if (response.Contains("right"))
{
Debug.Log("Move to the right ");
float x = Camera.main.transform.localEulerAngles.x;
float y = Camera.main.transform.localEulerAngles.y;
DeplacementCamera.moveRight(x, y);
response = "";
Receive(clientSocket);
}
}
I have already see this post but without success or maybe I tried it in the wrong way:
Why does BeginReceive() not return for more than one read?
Edit: In the function ReceiveCallback the else is never reached.
you may change your method
private void SetupReceiveCallback(Socket sock)
{
try
{
DataBuffer = new byte[BufferSize];
AsyncCallback recieveData = new AsyncCallback(OnRecievedData);
sock.BeginReceive(DataBuffer, 0, DataBuffer.Length, SocketFlags.None, recieveData, _socket);
}
catch (Exception E)
{
}
}
private void OnRecievedData(IAsyncResult ar)
{
Socket sock = (Socket)ar.AsyncState;
try
{
if (sock.Connected)
{
int nBytesRec = sock.EndReceive(ar);
if (nBytesRec > 0)
{
string Data = Encoding.UTF8.GetString(DataBuffer);
if (Data.Length != 4)
{
string JsonString = Data;
if (!string.IsNullOrEmpty(JsonString))
{
try
{
//Add you code processing here
}
catch (Exception ex)
{
}
}
BufferSize = 4;
}
else
{
BufferSize = BitConverter.ToInt32(DataBuffer, 0);
}
}
SetupReceiveCallback(sock);
}
else
// on not connected to socket
}
catch (Exception E)
{
}
}
public bool Connect(string IP, int Port)
{
try
{
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//if (IsValidIP(IP))
//{
// IPEndPoint epServer = new IPEndPoint(IPAddress.Parse(IP), Port);
// _socket.Connect(epServer);
//}
//else
//{
// _socket.Connect(IP, Port);
//}
//SetupReceiveCallback(_socket);
}
catch(Exception ex)
{
}
return false;
}
I have a WPF 'server' app using a socket, with a UWP 'client' app using StreamSocket. I can transfer small amounts of data fine, but if I try and send a 288kb file, the file is received fine by the WPF app but the UWP app never receives the response.
WPF code:
In the ReadCallback method, the content is sent in the format of command;printername{data}. The data is all received fine but the Send method called before processing the data is not received.
public void Start()
{
Task.Run(() =>
{
var ipHostInfo = Dns.GetHostEntry(Dns.GetHostName());
var ipAddress = ipHostInfo.AddressList.FirstOrDefault(x =>x.AddressFamily == AddressFamily.InterNetwork);
var localEndPoint = new IPEndPoint(ipAddress, Settings.Default.PrintPort);
listener = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
try
{
listener.Bind(localEndPoint);
listener.Listen(100);
IsRunning = true;
while (true)
{
// Set the event to nonsignaled state.
AllDone.Reset();
// Start an asynchronous socket to listen for connections.
listener.BeginAccept(
AcceptCallback,
listener);
// Wait until a connection is made before continuing.
AllDone.WaitOne();
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
});
}
public void AcceptCallback(IAsyncResult ar)
{
if(!IsRunning) return;
// Signal the main thread to continue.
AllDone.Set();
// Get the socket that handles the client request.
Socket handler = listener.EndAccept(ar);
// Create the state object.
StateObject state = new StateObject {WorkSocket = handler};
handler.BeginReceive(state.Buffer, 0, StateObject.BufferSize, 0,
ReadCallback, state);
}
public static void ReadCallback(IAsyncResult ar)
{
try
{
// Retrieve the state object and the handler socket
// from the asynchronous state object.
StateObject state = (StateObject)ar.AsyncState;
Socket handler = state.WorkSocket;
// Read data from the client socket.
int bytesRead = handler.EndReceive(ar);
if (bytesRead > 0)
{
// There might be more data, so store the data received so far.
state.Sb.Append(Encoding.ASCII.GetString(
state.Buffer, 0, bytesRead));
// Check for end-of-file tag. If it is not there, read
// more data.
var content = state.Sb.ToString();
if (content.IndexOf("<EOF>") > -1)
{
content = content.Remove(content.IndexOf("<EOF>"));
if (content.StartsWith("PrintFile"))
{
if (!content.Contains("{"))
{
Send(handler, "false");
return;
}
var parts = content.Substring(0, content.IndexOf('{')).Split(';');
if (string.IsNullOrEmpty(parts[1]))
{
Send(handler, "false");
return;
}
// This send never gets received
Send(handler, "true");
//But the file is printed
var base64 = content.Substring(content.IndexOf('{') + 1);
base64 = base64.Remove(base64.LastIndexOf('}'));
var doc = new Spire.Pdf.PdfDocument(Convert.FromBase64String(base64));
doc.PrintSettings.PrinterName = parts[1];
doc.Print();
}
}
else
{
// Not all data received. Get more.
handler.BeginReceive(state.Buffer, 0, StateObject.BufferSize, 0,
ReadCallback, state);
}
}
}
catch
{
//Ignore
}
}
private static void Send(Socket handler, String data)
{
// Convert the string data to byte data using ASCII encoding.
byte[] byteData = Encoding.ASCII.GetBytes(data);
// Begin sending the data to the remote device.
handler.BeginSend(byteData, 0, byteData.Length, 0,
SendCallback, handler);
}
private static void SendCallback(IAsyncResult ar)
{
try
{
// Retrieve the socket from the state object.
Socket handler = (Socket)ar.AsyncState;
handler.Shutdown(SocketShutdown.Both);
handler.Close();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
UWP Code:
public bool PrintFlle(string printer, string base64)
{
try
{
var result = Send($"PrintFile;{printer}{{{base64}}}<EOF>").Result;
return bool.TryParse(result, out var b) && b;
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
finally
{
socket.Dispose();
}
}
private async Task<string> Send(string input)
{
try
{
socket = new StreamSocket();
await socket.ConnectAsync(HostName, App.LocalSettings.Values["PrintPort"] as string);
using (Stream outputStream = socket.OutputStream.AsStreamForWrite())
{
using (var streamWriter = new StreamWriter(outputStream))
{
await streamWriter.WriteLineAsync(input);
await streamWriter.FlushAsync();
}
}
using (Stream inputStream = socket.InputStream.AsStreamForRead())
{
using (StreamReader streamReader = new StreamReader(inputStream))
{
var output = await streamReader.ReadLineAsync();
socket.Dispose();
return output;
}
}
}
catch (Exception e)
{
socket.Dispose();
return "";
}
}
my code is use to send multiple files from server to client:
Server:
public void SendFiles(string filePath)
{
try
{
Debug.WriteImportant("SendFiles() - " + filePath);
byte[] fileBuff = File.ReadAllBytes(filePath);
byte[] fileSize = BitConverter.GetBytes(fileBuff.Length);
byte[] dir = Encoding.ASCII.GetBytes(Path.GetFileName(Path.GetDirectoryName(filePath)) + "\\" + Path.GetFileName(filePath));
byte[] fileHeader = new byte[fileSize.Length + dir.Length];
fileSize.CopyTo(fileHeader, 0);
dir.CopyTo(fileHeader, fileSize.Length);
Send(fileHeader);
Send(fileBuff);
}
catch (Exception e)
{
Debug.WriteImportant(e.ToString());
}
}
public void Send(byte[] byteData)
{
try
{
// Begin sending the data to the remote device.
pMySocket.BeginSend(byteData, 0, byteData.Length, 0,
new AsyncCallback(SendCallback), pMySocket);
}
catch (Exception e)
{
Debug.WriteImportant(e.ToString());
}
}
private void SendCallback(IAsyncResult ar)
{
try
{
// Retrieve the socket from the state object.
Socket client = (Socket)ar.AsyncState;
// Complete sending the data to the remote device.
int bytesSent = client.EndSend(ar);
Debug.WriteImportant("Sent" + bytesSent + "bytes to client #." + strConstID);
pMySocket.BeginReceive(byRxBuffer, 0, byRxBuffer.Length, 0, ReceiveCallback, pMySocket);
}
catch (Exception e)
{
Debug.WriteImportant(e.ToString());
}
}
Client:
private static void Receive(Socket client)
{
try
{
StateObject state = new StateObject();
state.workSocket = client;
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReceiveHeaderCallback), state);
}
catch (Exception e)
{
Debug.WriteLine(e.ToString());
}
}
public static void ReceiveHeaderCallback(IAsyncResult ar)
{
try
{
Debug.WriteLine("## Begin ReceiveHeaderCallback");
String content = String.Empty;
StateObject state = (StateObject)ar.AsyncState;
Socket handler = state.workSocket;
int bytesRead = handler.EndReceive(ar);
state.fileSize = BitConverter.ToInt32(state.buffer, 0);
byte[] bFileName = new byte[state.buffer.Length - 4];
Array.Copy(state.buffer, 4, bFileName, 0, bFileName.Length);
_Buff = new byte[state.fileSize];
fileName = Encoding.UTF8.GetString(bFileName).TrimEnd('\0');
_totBytesRead = 0;
Debug.WriteLine("## File size = " + state.fileSize);
Debug.WriteLine("## File name = " + fileName);
Array.Clear(state.buffer, 0, state.buffer.Length);
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReceiveCallback), state);
}
catch (Exception e)
{
Debug.WriteLine(e.ToString());
}
}
private static void ReceiveCallback(IAsyncResult ar)
{
try
{
String content = String.Empty;
StateObject state = (StateObject)ar.AsyncState;
Socket client = state.workSocket;
int bytesRead = client.EndReceive(ar);
if (bytesRead > 0)
{
// There might be more data, so store the data received so far.
Buffer.BlockCopy(state.buffer, 0, _Buff, _totBytesRead, bytesRead);
_totBytesRead += bytesRead;
if (_totBytesRead < state.fileSize)
{
// Not all data received. Get more.
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReceiveCallback), state);
}
else
{ File.WriteAllBytes(AppDomain.CurrentDomain.BaseDirectory + fileName, _Buff);
Debug.WriteLine("## File receiving complete. File created = " + fileName);
Array.Clear(state.buffer, 0, state.buffer.Length);
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveHeaderCallback), state);
}
}
}
catch (Exception e)
{
Debug.WriteLine(e.ToString());
}
}
The problem I face now is when I send multiple files at the same time, there will be problem occur in client side. It can't receive the file correctly.
May I ask does the client need to do some kind of acknowledgement telling the server that it had complete receive a file and you can send another file?
Or is it we can somehow flush or close the sending stream after sending a file and open it again when send another file?
What is need to be added to my coding?
I am in the process of making a C# socket library that can be accessed in Visual FoxPro. Ideally, the library will contain two functions: ReceiveMessage and SendMessage. On each call, the function is supposed to {open connection -> read/write -> close connection}. I found most of what I am looking for at http://www.csharp-examples.net/socket-send-receive/; however, now I have to test (on the same machine) the modified code before compiling the dll.
I keep getting a "connection actively refused" error. While I have included all of the code below so you can see what it is doing, the issue is almost certainly in the main() at the bottom. It is my limited understanding that I cannot have two connections to the same port, but I am at a loss on how to fix it. Does anybody know sockets who can give me some direction on how to test these functions (I am new to sockets)?
namespace Sockets
{
class Sockets
{
private static void Receive(Socket socket, byte[] buffer, int offset, int size, int timeout)
{
int startTickCount = Environment.TickCount;
int received = 0; // how many bytes is already received
do
{
if (Environment.TickCount > startTickCount + timeout)
throw new Exception("Timeout.");
try
{
received += socket.Receive(buffer, offset + received, size - received, SocketFlags.None);
}
catch (SocketException ex)
{
if (ex.SocketErrorCode == SocketError.WouldBlock ||
ex.SocketErrorCode == SocketError.IOPending ||
ex.SocketErrorCode == SocketError.NoBufferSpaceAvailable)
{
// socket buffer is probably empty, wait and try again
Thread.Sleep(30);
}
else
throw ex; // any serious error occurr
}
} while (received < size);
}
// -----------------------------------------------------------------------------------------------------------------------
// -----------------------------------------------------------------------------------------------------------------------
// -----------------------------------------------------------------------------------------------------------------------
private static void Send(Socket socket, byte[] buffer, int offset, int size, int timeout)
{
int startTickCount = Environment.TickCount;
int sent = 0; // how many bytes is already sent
do
{
if (Environment.TickCount > startTickCount + timeout)
throw new Exception("Timeout.");
try
{
sent += socket.Send(buffer, offset + sent, size - sent, SocketFlags.None);
}
catch (SocketException ex)
{
if (ex.SocketErrorCode == SocketError.WouldBlock ||
ex.SocketErrorCode == SocketError.IOPending ||
ex.SocketErrorCode == SocketError.NoBufferSpaceAvailable)
{
// socket buffer is probably full, wait and try again
Thread.Sleep(30);
}
else
throw ex; // any serious error occurr
}
} while (sent < size);
}
// -----------------------------------------------------------------------------------------------------------------------
// -----------------------------------------------------------------------------------------------------------------------
// -----------------------------------------------------------------------------------------------------------------------
public static string SendMessage(string ipAddress, string port, string message)
{
Socket socket = new TcpClient().Client;
try
{
socket.Connect(ipAddress, Int32.Parse(port));
Send(socket, Encoding.UTF8.GetBytes(message), 0, message.Length, 10000);
socket.Close();
return "Success";
}
catch (Exception e)
{
socket.Close();
return e.Message;
}
}
// -----------------------------------------------------------------------------------------------------------------------
// -----------------------------------------------------------------------------------------------------------------------
// -----------------------------------------------------------------------------------------------------------------------
public static string ReceiveMessage(string ipAddress, string port, int bufferSize = 1024)
{
Socket socket = new TcpClient().Client;
try
{
socket.Connect(ipAddress, Int32.Parse(port));
byte[] buffer = new byte[bufferSize];
Receive(socket, buffer, 0, buffer.Length, 10000);
socket.Close();
return Encoding.UTF8.GetString(buffer, 0, buffer.Length);
}
catch (Exception e)
{
socket.Close();
return e.Message;
}
}
// -----------------------------------------------------------------------------------------------------------------------
// -----------------------------------------------------------------------------------------------------------------------
// -----------------------------------------------------------------------------------------------------------------------
}
}
class Program
{
static void Main(string[] args)
{
new Thread(delegate() {var test = Sockets.Sockets.SendMessage("localhost", "60010", "abcdefg");}).Start();
new Thread(delegate() {Console.WriteLine(Sockets.Sockets.ReceiveMessage("localhost", "60010"));}).Start();
var waitForEnter = Console.ReadLine();
}
}
With a tilda (~) as the trailing character, here is the functional receive function (old code is commented):
public static string ReceiveMessage(string ipAddress, string port, int bufferSize = 1024)
{
try
{
TcpListener serverSocket = new TcpListener(new System.Net.IPAddress(IPAddress.Parse(ipAddress).GetAddressBytes()), Int32.Parse(port));
serverSocket.Start();
TcpClient clientSocket = serverSocket.AcceptTcpClient();
NetworkStream networkStream = clientSocket.GetStream();
int bytesRead;
string buffer = "";
byte[] bytesFrom = new byte[bufferSize];
while (true)
{
bytesRead = networkStream.Read(bytesFrom, 0, bytesFrom.Length);
string incoming = System.Text.Encoding.ASCII.GetString(bytesFrom, 0, bytesRead);
buffer = buffer + incoming;
while (buffer.Contains("~"))
{
string msg = buffer.Substring(0, buffer.IndexOf("~"));
// buffer = buffer.Remove(0, msg.Length + 3);
return msg;
}
}
//var socket = new TcpListener(new IPAddress(IPAddress.Parse(ipAddress).GetAddressBytes()), Int32.Parse(port));
//try
//{
//byte[] buffer = new byte[bufferSize];
// Receive(socket, buffer, 0, buffer.Length, 10000);
// socket.Close();
// return Encoding.UTF8.GetString(buffer, 0, buffer.Length);
//}
//catch (Exception e)
//{
// socket.Close();
// return e.Message;
//}
}
catch (Exception e)
{
return e.Message;
}
}
I have a TCP server (implemented as a windows service) for listening to GPS devices in a vehicle tracking application, after random period of time from its operation I got the following error : "Only one usage of each socket address (protocol/network address/port) is normally permitted." While I am sure that I am closing every socket after using it. so can anyone tell me what is the problem here I have the MaxUserPort value in windows server 2008 registry with (65534) and the TCPTimeWaitDelay value with (30 seconds) ?
Here is the code:
1) The Main Thread :
private void MainThread() {
byte[] bytes = new Byte[1024];
IPEndPoint localEndPoint = new IPEndPoint(0, this.port);
Socket listener = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp );
// Addedd on [20/03/2012] by Sherif Mosaad for handling socket exceptions ...
//listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, (int)1);
try {
listener.Bind(localEndPoint);
listener.Listen(100);
while (active) {
mainDone.Reset();
listener.BeginAccept(new AsyncCallback(AcceptCallback),listener);
while (active)
if (mainDone.WaitOne(100, true))
break;
}
listener.Shutdown(SocketShutdown.Both);
listener.Close();
Thread.Sleep(2000);
} catch (Exception e) {
if (OnError != null)
OnError(this, e.ToString());
LogManager.LogError(e, "TCPSimpleServer MainThread");
}
}
2) The AcceptCallback handler:
private void AcceptCallback(IAsyncResult ar) {
mainDone.Set();
Socket listener = (Socket)ar.AsyncState;
Socket handler = null;
try
{
handler = listener.EndAccept(ar);
}
catch
{
try
{
listener.Shutdown(SocketShutdown.Both);
listener.Close();
Thread.Sleep(2000);
}
catch { return; }
}
if (OnConnect != null)
OnConnect(this, handler);
StateObject state = new StateObject();
state.workSocket = handler;
state.endPoint = (IPEndPoint)handler.RemoteEndPoint;
stateObjectDictionary.Add(state, state.workSocket);
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}
3) The ReadCallback Handler:
private void ReadCallback(IAsyncResult ar) {
String content = String.Empty;
StateObject state = (StateObject)ar.AsyncState;
Socket handler = state.workSocket;
int bytesRead = 0;
try
{
bytesRead = handler.EndReceive(ar);
}
catch (Exception e)
{
// Connection closed by client
if (OnDisconnect != null)
OnDisconnect(this, state.endPoint);
return;
}
if (bytesRead > 0)
{
string data = Encoding.ASCII.GetString(state.buffer, 0, bytesRead);
if (OnDataAvailable != null)
OnDataAvailable(this, handler, data);
try
{
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}
catch (Exception e)
{
// Connection closed by client
if (OnDisconnect != null)
OnDisconnect(this, state.endPoint);
return;
}
}
else
{
// Connection closed by peer
if (OnDisconnect != null)
OnDisconnect(this, state.endPoint);
}
}
Finally the state object :
public class StateObject
{
public Socket workSocket = null;
public const int BufferSize = 1024;
public byte[] buffer = new byte[BufferSize];
public StringBuilder sb = new StringBuilder();
public IPEndPoint endPoint;
}
Any help please?
There's a race condition. You call mainDone.Set which allows another thread to proceed to BeginAccept while the current thread moves towards EndAccept. Which will get there first? If you start accepting before finishing the previous accept, I suspect this error might pop up.
Fix? You need to set mainDone event after you call EndAccept
Even better, follow a simpler pattern without synchronization primitives. I outline one here.