Reputation: 7230
I've been playing with some c# socket code that I found at MSDN (original server code and client code) and I've run into a problem that I don't understand. First, here is my socket server code:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace AsyncSocketServerTest
{
class Program
{
public class StateObject
{
public Socket socket = null;
public const int BufferSize = 1024;
public byte[] buffer = new byte[BufferSize];
public List<byte> bytes = new List<byte>();
}
public static ManualResetEvent allDone = new ManualResetEvent(false);
private const string ipAdd = "127.0.0.1";
public static void StartListening()
{
byte[] bytes = new byte[1024];
IPAddress ipAddress = IPAddress.Parse(ipAdd);
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, 25981);
Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
listener.Bind(localEndPoint);
listener.Listen(100);
while (true)
{
allDone.Reset();
listener.BeginAccept(new AsyncCallback(AcceptCallback), listener);
allDone.WaitOne();
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
Console.WriteLine("\nPress ENTER to continue...");
Console.Read();
}
public static void AcceptCallback(IAsyncResult ar)
{
allDone.Set();
Socket listener = (Socket)ar.AsyncState;
Socket handler = listener.EndAccept(ar);
StateObject state = new StateObject();
state.socket = handler;
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback), state);
}
public static void ReadCallback(IAsyncResult ar)
{
Console.WriteLine("Inside ReadCallback()...");
// retrieve the state object and the handler socket from the asynchronous state object
StateObject state = (StateObject)ar.AsyncState;
Socket socket = state.socket;
// read data from the client socket
int bytesRead = socket.EndReceive(ar);
if (bytesRead > 0)
{
// there might be more data, so store the data received so far
for (int bufferIndex = 0; bufferIndex < bytesRead; bufferIndex++)
{
state.bytes.Add(state.buffer[bufferIndex]);
}
socket.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback), state);
}
else
{
if (state.bytes.Count > 0)
{
// All the data has been read from the client; display it on the console.
byte[] bytesReceived = state.bytes.ToArray();
Console.WriteLine("Received {0} bytes from client...", bytesReceived.Length.ToString());
}
// generate a 50 byte response to send back to the client
Random r = new Random();
byte[] responseToSend = new byte[50];
r.NextBytes(responseToSend);
// *** THIS APPEARS TO BE CAUSING A PROBLEM ***
// send the response back to client
SendBytes(socket, responseToSend);
// ********************************************
// edit - commented out; the socket shouldn't be closed before the response is sent back to the client asynchronously
//socket.Close();
}
}
private static void SendBytes(Socket client, byte[] bytesToSend)
{
client.BeginSend(bytesToSend, 0, bytesToSend.Length, 0, new AsyncCallback(SendCallback), client);
}
private static void SendCallback(IAsyncResult ar)
{
try
{
Socket handler = (Socket)ar.AsyncState;
int bytesSent = handler.EndSend(ar);
Console.WriteLine("Sent {0} bytes to client.", bytesSent);
handler.Shutdown(SocketShutdown.Both);
handler.Close();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
static void Main(string[] args)
{
StartListening();
Console.ReadKey();
}
}
}
And now for the client code:
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace AsyncSocketClientTest
{
class Program
{
public class StateObject
{
public Socket socket = null;
public const int BufferSize = 1024;
public byte[] buffer = new byte[BufferSize];
public List<byte> bytes = new List<byte>();
}
private const string ipAdd = "127.0.0.1";
// ManualResetEvent instances signal completion
private static ManualResetEvent connectDone = new ManualResetEvent(false);
private static ManualResetEvent sendDone = new ManualResetEvent(false);
private static ManualResetEvent receiveDone = new ManualResetEvent(false);
private static void StartClient()
{
try
{
IPAddress ipAddress = IPAddress.Parse(ipAdd);
IPEndPoint remoteEndPoint = new IPEndPoint(ipAddress, 25981);
Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
client.BeginConnect(remoteEndPoint, new AsyncCallback(ConnectCallback), client);
connectDone.WaitOne();
// generate 100 random bytes to send to the server
Random r = new Random();
byte[] buffer = new byte[100];
r.NextBytes(buffer);
// send data to the server
SendBytes(client, buffer);
sendDone.WaitOne();
// *** THIS APPEARS TO BE CAUSING A PROBLEM ***
// receive the response from the remote host
ReceiveBytes(client);
receiveDone.WaitOne();
// ********************************************
// release the socket
client.Shutdown(SocketShutdown.Both);
client.Close();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
private static void ConnectCallback(IAsyncResult ar)
{
try
{
// retrieve the socket from the state object
Socket client = (Socket)ar.AsyncState;
// complete the connection
client.EndConnect(ar);
Console.WriteLine("Socket connected to {0}", client.RemoteEndPoint.ToString());
// signal that the connection has been made
connectDone.Set();
}
catch (SocketException sockEx)
{
// if the server isn't running, we're going to get a socket exception here...
Console.WriteLine(sockEx.Message);
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
private static void ReceiveBytes(Socket client)
{
Console.WriteLine("Inside ReceiveBytes()...");
try
{
// create the state object
StateObject state = new StateObject();
state.socket = client;
// begin receiving data from the remote device
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
private static void ReceiveCallback(IAsyncResult ar)
{
Console.WriteLine("Inside ReceiveCallback()...");
try
{
// Retrieve the state object and the client socket from the asynchronous state object
StateObject state = (StateObject)ar.AsyncState;
Socket client = state.socket;
// Read data from the remote host
int bytesRead = client.EndReceive(ar);
if (bytesRead > 0)
{
// there might be more data, so store the data received so far
for (int bufferIndex = 0; bufferIndex < bytesRead; bufferIndex++)
{
state.bytes.Add(state.buffer[bufferIndex]);
}
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);
}
else
{
if (state.bytes.Count > 0)
{
// All the data has been read from the client; display it on the console.
byte[] bytesReceived = state.bytes.ToArray();
Console.WriteLine("Read {0} bytes from socket...", bytesReceived.Length.ToString());
}
// Signal that all bytes have been received
receiveDone.Set();
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
private static void SendBytes(Socket client, byte[] bytesToSend)
{
// Begin sending the data to the remote device
client.BeginSend(bytesToSend, 0, bytesToSend.Length, 0, new AsyncCallback(SendCallback), client);
}
private static 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);
Console.WriteLine("Sent {0} bytes to server.", bytesSent);
// signal that all bytes have been sent
sendDone.Set();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
static void Main(string[] args)
{
StartClient();
}
}
}
If I comment out the code in the client that receives the response back from the server as well as the code in the server that attempts to send the response to the client, then things appear to be working as you would expect (i.e., the client connects to the server, sends data and the server receives the data properly). When I uncomment these sections of the code, however, I'm seeing some behavior that I don't understand. In this case, I see the client connect to the server and send data to it. On the server side, the code appears to hang inside ReadCallback(). To better illustrate this, when the code sections I mentioned previously are commented out, I see this:
Client output:
Socket connected to 127.0.0.1:25981
Sent 100 bytes to server.
Server output:
Waiting for a connection...
Waiting for a connection...
Inside ReadCallback()...
Inside ReadCallback()...
Received 100 bytes from client...
As you can see from this output, when the server receives the 100 bytes of client data, I see two calls to ReadCallback(). So now I uncomment the aforementioned code and run it again. This time, I see:
Client output:
Socket connected to 127.0.0.1:25981
Sent 100 bytes to server.
Inside ReceiveBytes()...
Server output:
Waiting for a connection...
Waiting for a connection...
Inside ReadCallback()...
This time, my client sends 100 bytes of data to the server, sets the sendDone ManualResetEvent and then goes into ReceiveBytes(). On the server side, I see a single call to ReadCallback() and nothing else. That leads me to believe that the server didn't properly finish reading the data from the client although I'm not sure why. What am I missing?
Upvotes: 1
Views: 2184
Reputation: 504
This doesn't really answer your exact question but may I suggest an alternate way to go about this? For me threads are a bit easier to understand and the code looks a bit cleaner:
Server
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication2 {
class Program {
static void Main(string[] args) {
ServerWorkThread objThread = new ServerWorkThread();
while(true) {
objThread.HandleConnection(objThread.mySocket.Accept());
}
}
}
public class ServerWorkThread {
public Socket mySocket;
public ServerWorkThread() {
IPEndPoint objEnpoint = new IPEndPoint(IPAddress.Parse("***.***.***.***"), 8888);
mySocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
mySocket.Bind(objEnpoint);
mySocket.Listen(100);
}
public void HandleConnection(Socket iIncomingSocket) {
Thread worker = new Thread(this.RecieveAndSend);
worker.Start(iIncomingSocket);
worker.Join();
}
public void RecieveAndSend(object iIncoming) {
Socket objSocket = (Socket)iIncoming;
byte[] bytes = new byte[1024];
int bytesRecieved = objSocket.Receive(bytes);
string strReceived = System.Text.Encoding.ASCII.GetString(bytes, 0, bytesRecieved);
Console.WriteLine("Received from client: " + strReceived);
Console.WriteLine("Sending acknowledgement to client");
string strSend = ("Command of: " + strReceived + " was processed successfully");
objSocket.Send(System.Text.Encoding.ASCII.GetBytes(strSend));
objSocket.Close();
}
}
}
Client:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Client {
class Program {
static void Main(string[] args) {
ClientWorkThread thread1 = new ClientWorkThread("I am thread 1");
thread1.SendCommand();
ClientWorkThread thread2 = new ClientWorkThread("I am thread 2");
thread2.SendCommand();
ClientWorkThread thread3 = new ClientWorkThread("I am thread 3");
thread3.SendCommand();
Console.Read();
}
}
public class ClientWorkThread {
private Socket pSocket;
private string command;
public ClientWorkThread(string iCommand) {
IPEndPoint objEnpoint = new IPEndPoint(IPAddress.Parse("***.***.***.***"), 8888);
pSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
pSocket.Connect(objEnpoint);
command = iCommand;
}
public void SendCommand() {
Thread worker = new Thread(this.Send);
worker.Start(pSocket);
}
public void Send(object iSending) {
Socket objSocket = (Socket)iSending;
objSocket.Send(System.Text.Encoding.ASCII.GetBytes(command + " now DO WORK "));
Console.WriteLine("Sending: " + command + " now DO WORK ");
byte[] bytes = new byte[1024];
int bytesRecieved = objSocket.Receive(bytes);
string strReceived = System.Text.Encoding.ASCII.GetString(bytes, 0, bytesRecieved);
Console.WriteLine("Received from server: " + strReceived);
objSocket.Close();
}
}
}
Server output: Received from client: I am thread 1 now DO WORK Sending acknowledgement to client Received from client: I am thread 2 now DO WORK Sending acknowledgement to client Received from client: I am thread 3 now DO WORK Sending acknowledgement to client
Client output: Sending: I am thread 2 now DO WORK Sending: I am thread 3 now DO WORK Received from server: Command of: I am thread 2 now DO WORK was processed successfully Received from server: Command of: I am thread 3 now DO WORK was processed successfully Sending: I am thread 1 now DO WORK Received from server: Command of: I am thread 1 now DO WORK was processed successfully
You could also use thread.Join() to have them finish executing in order.
Upvotes: 1