I have a c# socket based server which serves TCP clients.
I use telnet based monitoring to see if the server is listening.
to versify clients are up i use server keep alive (sending a random string data), and if socket expection raises-i remove the client from a client's dictionary.
the probelm is: the telnet connections to the socket doesn't get removed from the list of connections and the number of clients is rising up slowly but surely.
i tried a telnet manually to the server through command line and than closing the telnet connection-nothing.the server just keep sending the keep alive to the telnet connection with no execptions.
this is my read call back:
protected void ReadCallback(IAsyncResult ar)
{
String content = String.Empty;
// Retrieve the state object and the handler socket
// from the async state object.
StateObject state = (StateObject)ar.AsyncState;
Socket handler = state.socket;
if (state.socket == null)
return;
if (!state.socket.Connected)
return;
int id = state.id;
try
{
// Read data from the client socket.
int bytesRead = handler.EndReceive(ar);
if (bytesRead > 0)
{
string newAddedStr = string.Empty;
newAddedStr = Encoding.UTF8.GetString(state.buffer, 0, bytesRead);
//cut the new message and add it
if (state.sb == null)
state.sb = new StringBuilder();
state.sb.Append(newAddedStr);
// There might be more data, so store the data received so far.
//add data until end of XML
content = state.sb.ToString();
//IF GOT FULL MESSAGE FROM SOCKET
if ((content.Length > 0) /*&& (content.IndexOf("\0") > -1)*/)
{
String strh = String.Format("Client # {0} data: ", id);
strh += content.Replace("\0", "");
if (!strh.Contains("keepalive"))
LogWriter.Trace(strh, "");
// l(writeToGetTextBoxMsg), new object[] { strh });
if (state != null)
{
if (state.sb == null)
state.sb = new StringBuilder();
state.sb.Length = 0;
}
//add the rest of the xml
string objData = content.Replace("\0", "");
string xmlData = objData.ToString();
try
{
if (xmlData.Contains("Unsubscribe"))
{
RemoveSubscriber(xmlData);
}
else
{
if (xmlData.Contains("Subscribe"))
{
if (!state.IsInitiated)
{
state.Init();
state.socketClient.OnSocketError += new SocketErrorHandler(socketClient_OnSocketError);
state.socketClient.clientSocket = handler;
}
AddSubscriber(xmlData, state);
}
}
xmlData = null;
objData = null;
content = null;
}
catch (ArgumentOutOfRangeException ex)
{
LogWriter.Trace(newAddedStr,"ArgumentOutOfRangeException in ReadCallback");
}
catch (Exception ex)
{
LogWriter.TraceError(ex.Message + " " + ex.StackTrace + " " + newAddedStr);
}
#region oldCode
#endregion
}
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(this.ReadCallback), state);
}
}
catch (System.Net.Sockets.SocketException es)
{
closeSocket(state, false, "Execption:" + es.Message + "," + es.StackTrace);
if (es.ErrorCode != 64)
{
LogWriter.Trace(string.Format("Socket Exception: {0}, {1}.", es.ErrorCode, es.ToString()), "");
}
}
catch (Exception e)
{
closeSocket(state, false,"Execption:"+e.Message+","+e.StackTrace);
if (e.GetType().FullName != "System.ObjectDisposedException")
{
Console.WriteLine("Exception: " + e.StackTrace);
LogWriter.Trace("Exception Message: " + e.ToString() + e.StackTrace, "");
Console.WriteLine("Exception Message: " + e.ToString());
LogWriter.Trace("ReadCallback:" + e.Message + " " + e.StackTrace, "ERROR");
}
}
}
any ideas?
When you do a read of a socket and 0 is returned you know that the other side of the connection has closed. Are you doing that?
Related
I'm trying to work this one out but it really is a head scratcher, a socket is meant to make two connections to the server, the second one being the legitamate connection. I have this method that I attach to my callback for beginReceive method although its calling Dispose twice?
Usually it only calls it once as the client makes 2 connections to the server, the second one being legitamate but it seems this method wants to call Dispose twice, which makes me think theres something wrong, could somebody verify if there is anything wrong with this?
private void OnIncomingData(IAsyncResult asyncResult)
{
int bytesReceived;
try
{
bytesReceived = _socket.EndReceive(asyncResult);
}
catch
{
Dispose();
return;
}
if (bytesReceived == 0)
{
Dispose();
return;
}
try
{
var packet = new byte[bytesReceived];
Array.Copy(_buffer, packet, bytesReceived);
var received = Encoding.UTF8.GetString(packet);
var packetData = JObject.Parse(received).ToFlatDictionary();
var incomingPacket = new IncomingPacket(packetData);
CoreUtilities.LogToConsole("Received packet: " + incomingPacket.Id + "\n\n" + received);
if (_session.PacketProvider.PacketEvents.TryGetValue(incomingPacket.Id, out var packetEvent))
{
packetEvent.Invoke(_session, incomingPacket);
CoreUtilities.LogToConsole("Finished executing packet " + packetEvent.GetType().Name + " for " + incomingPacket.Id);
}
else
{
CoreUtilities.LogToConsole("Failed to execute unknown incoming packet: " + incomingPacket.Id);
}
}
catch
{
Dispose();
}
finally
{
try
{
_socket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, OnIncomingData, _socket);
}
catch
{
Dispose();
}
}
}
Scenario:
I have multiple machines, each wit hit's own IP, that run a tcpServer, all on the same port. During each transaction data is sent if the tcpServer port is opened. It, looks for a client before opening the port.
What I Need:
I'm writing an app, that needs to:
1. Check through the list of IP's if they are availible
2. Open a tcpClient port to each machine IP, (all on the same ports)
3. After every specified time make sure the connection is open, if not reopen connection.
Future:
I use an XMl file to give the SQL data which is used to get the list of machines and the port to listen on. In this xml is a udp setting as well, if this is yes, then the data being sent, must be received and redirected to a specified UDP port.
The Class:
public static void tcpListen(IPAddress server, int port)
{
try
{
TcpListener listener = new TcpListener(server, port);
listener.AcceptTcpClient();
listener.Start();
while (true)
{
Socket client = listener.AcceptSocket();
var childSocketThread = new Thread(() =>
{
byte[] data = new byte[100];
int size = client.Receive(data);
for (int i = 0; i < size; i++)
feedback = server + ":" + port + ": " + Convert.ToChar(data[i]);
using (StreamWriter w = File.AppendText("TCPLog.log"))
{
Log(feedback, w);
}
client.Close();
});
childSocketThread.Start();
}
}
catch (Exception err)
{
using (StreamWriter w = File.AppendText("error.log"))
{
Log("tcpControl.tcpListen: " + err.Message, w);
}
}
}
The part of the code that repeats every few seconds:
private void ptoCheck()
{
IPAddress sourceIP;
int sourcePort;
int destinationPort;
string tcpStatus;
int oldCOunt = dgvWorkstations.RowCount;
int newCount = sqlScripts.posCount(sqlServer, sqlUser, sqlPassword, sqlDatabase);
if (newCount != oldCOunt)
{
getWorkstations();
}
try
{
foreach (DataGridViewRow row in dgvWorkstations.Rows)
{
int i = row.Index;
bool tcpState;
dgvWorkstations["Status", i].Value = "Checking";
dgvWorkstations.Refresh();
name = row.Cells["POS_Name"].Value.ToString();
sourceIP = IPAddress.Parse(row.Cells["LastKnownIP"].Value.ToString());
sourcePort = Convert.ToInt32(row.Cells["Receive_Port"].Value.ToString());
destinationPort = Convert.ToInt32(row.Cells["Send_Port"].Value.ToString());
tcpState = tcpControl.tcpCheck(sourceIP, sourcePort, name);
if (tcpState == false)
{
dgvWorkstations["Status", i].Value = "Connecting";
dgvWorkstations.Refresh();
tcpStatus = tcpControl.tcpConnect(sourceIP, sourcePort, name);
tcpControl.tcpListen(sourceIP, sourcePort);
dgvWorkstations["Status", i].Value = tcpStatus;
}
if (tcpState == true)
{
dgvWorkstations["Status", i].Value = "Connected";
dgvWorkstations.Refresh();
}
i = i + 1;
}
}
catch (Exception err)
{
using (StreamWriter w = File.AppendText("AError.log"))
{
Log("frmMain.ptoCheck: (" + name + ") " + err.Message, w);
}
}//End Catch
}
I got the following code toe work, from my class I can now make multiple connections, however. The part where I call another method to listen and receive the data blocks the first method and holds it untill the connection drops without making further connections.
My Class:
public void tcpTest2(IPAddress server, Int32 port, int x)
{
TcpClient client;
sendData("Connecting to Host: " + server + " on port: " + port.ToString() + "...");
sendStatus("Connecting", x);
try
{
client = new TcpClient(server.ToString(), port);
if (false)
{
}
if (true)
{
sendData("Connection to Host: " + server + " on port: " + port.ToString() + "..ESTABLISHED");
sendStatus("Connected", x);
receiveData(client, server, port);
}
}
catch (Exception)
{
sendData("Connection to Host: " + server + " on port: " + port.ToString() + "..FAILED");
sendStatus("Failed", x);
}
}
public void receiveData(TcpClient client, IPAddress server, int port)
{
Byte[] data = System.Text.Encoding.Default.GetBytes("|");
data = new byte[1024];
string stringData;
bool connected;
connected = true;
while (connected == true)
{
string fromC = client.Client.RemoteEndPoint.ToString();
NetworkStream ns = client.GetStream();
int recv = ns.Read(data, 0, data.Length);
stringData = Encoding.ASCII.GetString(data, 0, recv);
sendUpdate("{" + fromC + "}" + stringData);
connected = IsConnected(client);
}
if (connected == false)
{
sendData("Connection to Host: " + server + " on port: " + port.ToString() + "..LOST");
sendLost(server);
}
}
public bool IsConnected(TcpClient client)
{
try
{
if (client != null && client.Client != null && client.Client.Connected)
{
if (client.Client.Poll(0, SelectMode.SelectRead))
{
byte[] buff = new byte[1];
if (client.Client.Receive(buff, SocketFlags.Peek) == 0)
{
return false;
}
else
{
return true;
}
}
return true;
}
else
{
return false;
}
}
catch
{
return false;
}
}
}
Here is the final code that worked for me:
My Class:
public void tcpConnect(object pos)
{
IPAddress hostIP = IPAddress.Parse(pos.ToString().Split(':')[0]);
int hostPort = Int32.Parse(pos.ToString().Split(':')[1]);
rowInd = Int32.Parse(pos.ToString().Split(':')[2]);
var client = new TcpClient();
if (!client.ConnectAsync(hostIP, hostPort).Wait(1000))
{
sendData("Connection to Host: " + hostIP + " on port: " + hostPort.ToString() + ".FAILED");
sendStatus("Failed", "", rowInd);
return;
}
if (true)
{
sendData("Connection to Host: " + hostIP.ToString() + " on port: " + hostPort.ToString() + "..ESTABLISHED");
Thread thread = new Thread(new ParameterizedThreadStart(ClientHandler));
thread.IsBackground = true;
Thread.FreeNamedDataSlot(hostIP.ToString() + rowInd.ToString());
thread.Name = hostIP.ToString() + rowInd.ToString();
thread.Start(client);
threadID = thread.ManagedThreadId.ToString();
sendStatus("Connected", threadID, rowInd);
}
}
public bool IsConnected(TcpClient client)
{
try
{
if (client != null && client.Client != null && client.Client.Connected)
{
if (client.Client.Poll(0, SelectMode.SelectRead))
{
byte[] buff = new byte[1];
if (client.Client.Receive(buff, SocketFlags.Peek) == 0)
{
return false;
}
else
{
return true;
}
}
return true;
}
else
{
return false;
}
}
catch
{
return false;
}
}
public void ClientHandler(object c)
{
TcpClient client = (TcpClient)c;
NetworkStream netstream = client.GetStream();
string fromC = client.Client.RemoteEndPoint.ToString();
string fromIP = fromC.Split(':')[0];
bool connected = true;
while (connected)
{
Thread.Sleep(10);
try
{
byte[] data = new byte[client.ReceiveBufferSize];
data = System.Text.Encoding.Default.GetBytes("|");
data = new byte[1024];
string stringData;
NetworkStream ns = client.GetStream();
int recv = ns.Read(data, 0, data.Length);
stringData = Encoding.ASCII.GetString(data, 0, recv);
sendUpdate("|" + fromC + "|" + stringData);
connected = IsConnected(client);
}
catch (Exception err)
{
connected = false;
sendLost(fromIP);
using (StreamWriter w = File.AppendText("Arch-PTO.log"))
{
Log("tcpServices.ClientHandler: " + err.Message, w);
}
}
}
sendLost(fromIP);
}
On my server side I have set up a single thread code that creates a new Socket object every time a client connects. Then I pass whatever I get from the client along with the socket that is connected to a packet handler and do the calculations there. In my main form I have a listview that I populate via entity framework, and whenever a packet from a registered computer connects I update the listview. So my question is can I from the packet handler class pass the socket object to the tag property of my listview when I update it?
My server side code:
private void ReceivedCallback(IAsyncResult result)
{
Socket clientSocket = result.AsyncState as Socket;
SocketError ER;
try
{
int bufferSize = clientSocket.EndReceive(result, out ER);
if (ER == SocketError.Success)
{
byte[] packet = new byte[bufferSize];
Array.Copy(_buffer, packet, packet.Length);
Console.WriteLine("Handling packet from IP:" + clientSocket.RemoteEndPoint.ToString());
//Handle packet stuff here.
PacketHandler.Handle(packet, clientSocket);
_buffer = new byte[61144];
clientSocket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, ReceivedCallback, clientSocket);
//clientSocket.BeginReceive(new byte[] { 0 }, 0, 0, 0, ReceivedCallback, clientSocket);
}
else
{
Console.WriteLine("No bytes received, we're closing the connection.");
clientSocket.Close();
}
}catch(SocketException ex)
{
Console.WriteLine("We caught a socket exception:" + ex.Message);
clientSocket.Close();
}
}
And my packet handler class:
public static void Handle(byte[] packet, Socket clientSocket)
{
if (clientSocket.Connected)
{
if (packet.Length > 0)
{
IPEndPoint RemoteIP = (IPEndPoint)clientSocket.RemoteEndPoint;
ushort packetLength = BitConverter.ToUInt16(packet, 0);
ushort packetType = BitConverter.ToUInt16(packet, 2);
ushort packetID = BitConverter.ToUInt16(packet, 4);
Console.WriteLine("We received a packet of Type: {0}, ID: {1} FROM {2}", packetType, packetID, RemoteIP.ToString());
if (packetType == 1)
{
switch (packetID)
{
case 1://Check if computer is registered in the database
CheckRegisteredRequest request1 = new CheckRegisteredRequest(packet);
Console.WriteLine("We received (Case 1): " + request1.Text);
string Response = "";
bool found = false;
ServerDbContext database = new ServerDbContext();
foreach (computers pcs in database.computers)
{
if (pcs.name == request1.Text.Split(',')[0])
{
found = true;
if (pcs.computer_ip == request1.Text.Split(',')[1])
{
//We found a computer with that name and ip address
Response = "true";
CheckRegisteredResponse resp1 = new CheckRegisteredResponse(Response);
clientSocket.Send(resp1.Data);
computers registeredPC;
var name = request1.Text.Split(',')[0];
using (var ctx = new ServerDbContext())
{
registeredPC = ctx.computers.Where(c => c.name == name).FirstOrDefault<computers>();
}
if (registeredPC != null)
{
registeredPC.networkStatus = "online";
}
using (var ctx = new ServerDbContext())
{
ctx.Entry(registeredPC).State = System.Data.Entity.EntityState.Modified;
ctx.SaveChanges();
}
addNewLog("Computer: " + request1.Text.Split(',')[0] + " came online - IP: " + request1.Text.Split(',')[1]);
RaiseFeedback("PC: " + request1.Text.Split(',')[0] + " came online - IP: " + request1.Text.Split(',')[1]);
break;
}
else
{
//We found a computer with that name but a different ip address, update it
var name = request1.Text.Split(',')[0];
var registeredPC = new computers();
using (var ctx = new ServerDbContext())
{
registeredPC = ctx.computers.Where(c => c.name == name).FirstOrDefault<computers>();
}
if (registeredPC != null)
{
var ip = request1.Text.Split(',')[1];
registeredPC.computer_ip = ip;
registeredPC.networkStatus = "online";
}
using (var ctx = new ServerDbContext())
{
ctx.Entry(registeredPC).State = System.Data.Entity.EntityState.Modified;
ctx.SaveChanges();
}
Response = "true";
CheckRegisteredResponse resp1 = new CheckRegisteredResponse(Response);
clientSocket.Send(resp1.Data);
addNewLog("Computer: " + request1.Text.Split(',')[0] + " came online - IP: " + request1.Text.Split(',')[1]);
RaiseFeedback("PC: " + request1.Text.Split(',')[0] + " came online - IP: " + request1.Text.Split(',')[1]);
break;
}
}
}
if (!found)
{
//There is no computer with that name in the database so send false
Response = "false";
CheckRegisteredResponse resp1 = new CheckRegisteredResponse(Response);
clientSocket.Send(resp1.Data);
}
break;... and so on....
So I've tried via a handler:
this is my custom event handler Args:
public class TextArgs : EventArgs
{
#region Fields
private string szMessage;
private Socket _sockets123;
#endregion Fields
#region ConstructorsH
public TextArgs(string TextMessage,Socket sock)
{
if (sock != null)
{
_sockets123 = sock;
szMessage = TextMessage;
}
else
{
szMessage = TextMessage;
}
}
#endregion Constructors
#region Properties
public string Message
{
get { return szMessage; }
set { szMessage = value; }
}
public Socket _socket
{
get { return _sockets123; }
set { _sockets123 = value; }
}
#endregion Properties
}
This is how i define that handler at the PacketHandler class:
public static event LogsEventHandler Feedback;
private static void RaiseFeedback(string p, Socket sock)
{
LogsEventHandler handler = Feedback;
if (handler != null)
{
handler(null,new TextArgs(p,sock));
}
}
And whenever a computer registers or connects I do the following:
RaiseFeedback("PC: " + request1.Text.Split(',')[0] + " came online - IP: " + request1.Text.Split(',')[1], clientSocket);
or
RaiseFeedback("PC: " + request1.Text.Split(',')[0] + " came online - IP: " + request1.Text.Split(',')[1], null);
And the event fires of these two methods in my main form:
private void OnFeedbackReceived(object sender, TextArgs e)
{
Invoke((MethodInvoker)delegate
{
UpdateComputers();
UpdateGUI(e.Message,e._socket);
}
);
}
public void UpdateGUI(string s, Socket sock)
{
LogBox.Text += s;
LogBox.AppendText(Environment.NewLine);
using (var context = new ServerDbContext())
{
var PCInfo = context.viewUsersInfo;
dataGridView1.DataSource = PCInfo.ToList();
}
if (sock != null)
{
ListViewItem item = ComputersList.FindItemWithText(s.Split(':')[1].ToString());
item.Tag = sock;
}
}
The Question: Yes, you can. Control.Tag is of type object and can hold pretty much anything you choose.
So you can write
Socket socket = someSocket;
ListViewItem item = listView1.FindItemWithText(someText);
if (item != null) item.Tag = socket; else Console.WriteLine(someText + " not found!);
And retrieve it as:
if (item.Tag != null) someSocket = item.Tag AS socket;
if (someSocket != null) ..
It is up to you to watch out for the success of the cast when retrieving it but also if the Socket still is alive and well..
The problem: The stackoverflow in your code is due to an erreanous, short-circuited property, which you have fixed by now. In general, you only need to write explicit getter and setters if they actually do more than just gettin and setting.
They could log out out test data, update other, dependent properties, do checks or conversiones or do other stuff.
But if none of it is needed, simply don't create the private fields and write the automatic getter and setters:
public Socket MySocket {get; set;}
Also note that the naming convention asks you to capitalize proprety names!
So I am creating a socket based web chat using C#. This is a school project.
It is basic server-client chat -> client sends message to server, server "broadcast" == sends message to all connected clients.
However if I connect two clients, and send message from one to another I only get something like:
▯▯▯▯▯▯▯▯▯▯
When I press "SEND" again (with the same message), it turns out good.
This is function for receiving.. I use it in Thread.
public void wait()
{
byte[] bytes = new byte[1024];
while (!shutdown)
{
// if (shutdown) break;
try
{
int bytesRec = senderSocket.Receive(bytes);
Dispatcher.Invoke(new Action(() =>
{
output.Text += "\n" + Encoding.UTF8.GetString(bytes, 0, bytesRec);
if (scroll.VerticalOffset == scroll.ScrollableHeight)
{
scroll.ScrollToEnd();
}
}));
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
}
}
This is for sending: (LCP is my own "protocole".. ignore this, it just adds one letter to string)
private void send_Click(object sender, RoutedEventArgs e)
{
try
{
LCP protocol = new LCP();
string messg = user.Text + " pravi: " + message.Text ;
string messag = protocol.CreateMessage(messg, "M");
byte[] msg = Encoding.UTF8.GetBytes(messag);
// Send the data through the socket.
senderSocket.Send(msg);
// int bytesRec = senderSocket.Receive(bytes);
//output.Text += "\n" + Encoding.UTF8.GetString(bytes, 0, bytesRec);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
and this is how I handle things on server:
try
{
listener.Bind(localEndPoint);
listener.Listen(10);
while (true)
{
Socket handler = listener.Accept();
data = null;
users.Add(handler.RemoteEndPoint);
socks.Add(handler);
new Thread(new ThreadStart(() =>
{
while (true)
{
bytes = new byte[1024];
int bytesRec = handler.Receive(bytes);
data = Encoding.UTF8.GetString(bytes, 0, bytesRec);
LCP protocolParser = new LCP();
Dictionary<string, string> wholeMessage = protocolParser.ParseMessage(data);
if (wholeMessage["type"] == "$J")
{
Console.WriteLine("\nPridružil se je " + wholeMessage["message"]);
byte[] msg = Encoding.UTF8.GetBytes(wholeMessage["message"] + " se je pridružil");
foreach (Socket edp in socks)
{
edp.Send(msg);
//handler.SendTo(msg,edp);
}
}
// Show the data on the console.
else if (wholeMessage["type"] == "$Q")
{
handler.Shutdown(SocketShutdown.Receive);
handler.Close();
socks.Remove(handler);
break;
}
else
{
Console.WriteLine("\n" + wholeMessage["message"]);
byte[] msg = Encoding.UTF8.GetBytes(wholeMessage["message"]);
// Dispatcher.Invoke(new Action(() =>
//{
foreach (Socket edp in socks)
{
edp.Send(msg);
//handler.SendTo(msg,edp);
}
// }));
}
}
//tvoja koda
})
){IsBackground=true}.Start();
// An incoming connection needs to be processed.
// Echo the data back to the client.
}
}
I have solved this using TcpListener and TcpClient class.
I wrote a C# server application. The server utilizes Asynchronous TCP sockets.
The packets are 80-180 bytes of data.
For performance testing I have a single client connect and send packets continuously. With debugging on the first 100 packets (0-100) receive in roughly 5 seconds. By the time the server received packets #300-400 it takes roughly 30 seconds to receive the packets. The performance continues to degrade as more receives occur.
I looked around and have not been able to find a solution. I have tried setting the Socket.NoDelay flag in case the Nagle algorithm was inhibiting the server.
I have disabled all functions within the server; so that it is only receiving to ensure I wasn't losing performance in other code.
I have also checked my CPU utilization and it is ~13%. I have over 2 GB of free memory. When running the application the ram is NOT constantly growing and utilization is minimal.
I am at a loss as to what to debug and look into next...
EDIT: Added Code Sample
public void StartListening()
{
try
{
IPAddress ipAddress = IPAddress.Parse("192.168.2.60");
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, m_Port);
m_MainSocket = new Socket(localEndPoint.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
m_MainSocket.NoDelay = true;
m_MainSocket.Bind(localEndPoint);
m_MainSocket.Listen(10);
m_MainSocket.BeginAccept(new AsyncCallback(clientConnected), null);
System.Diagnostics.Debug.WriteLine("Listening on:Local IP Address: " + localEndPoint.Address.ToString() + " Port :" + localEndPoint.Port.ToString() + "\n");
}
catch (SocketException se)
{
System.Diagnostics.Debug.WriteLine("Listening Exception \n");
System.Diagnostics.Debug.WriteLine(se.Message);
}
}
void clientConnected(IAsyncResult ar)
{
try
{
SocketState state = new SocketState(m_MainSocket.EndAccept(ar));
Client client = new Client(state);
if (client.SocketState.clientSocket.Connected)
{
System.Diagnostics.Debug.WriteLine("Client #?????? Connected \n");
AddLogText("Client #?????? Connected \r\n\r\n");
waitForData(client);
SetSendButton(true);
}
m_MainSocket.BeginAccept(new AsyncCallback(clientConnected), null);
}
catch (ObjectDisposedException)
{
System.Diagnostics.Debug.WriteLine("Client Connected: Socket has been closed\n");
}
catch (SocketException se)
{
System.Diagnostics.Debug.WriteLine("Client Connected Exception \n");
System.Diagnostics.Debug.WriteLine(se.Message);
}
}
void waitForData(Client client)
{
try
{
SocketState state = new SocketState(client.SocketState.clientSocket);
client.SocketState.clientSocket = null;
client.SocketState = state;
client.SocketState.clientSocket.BeginReceive(client.SocketState.DataBuffer, 0, client.SocketState.DataBuffer.Length, SocketFlags.None, new AsyncCallback(readDataCallback), client);
}
catch (SocketException se)
{
System.Diagnostics.Debug.WriteLine("Wait For Data Exception \n");
System.Diagnostics.Debug.WriteLine(se.Message);
}
}
public void readDataCallback(IAsyncResult ar)
{
Client client = (Client)ar.AsyncState;
try
{
// Read data from the client socket.
int iRx = client.SocketState.clientSocket.EndReceive(ar);
client.SocketState.SB.Append(Encoding.ASCII.GetString(client.SocketState.DataBuffer, 0, iRx));
string sPacketString = client.SocketState.SB.ToString();
Server formServer = this;
Packet_Helper packet_helper = new Packet_Helper(sPacketString, formServer);
Packet packet = new Packet(sPacketString);
client.SerialNumber = packet.SerialNumber;
client.FirmwareVersion = packet.FirmwareVersion;
client.ProductID = packet.ProductID;
client.HardwareVersion = packet.HardwareVersion;
if (!m_Clients.ContainsKey(packet.SerialNumber))
{
m_Clients.Add(packet.SerialNumber, client);
UpdateClientList();
string[] packets = client.refreshAll();
for (int i = 0; i < packets.Length; i++)
{
byte[] byteData = Encoding.ASCII.GetBytes(packets[i]);
client.SocketState.clientSocket.BeginSend(byteData, 0, byteData.Length, SocketFlags.None, new AsyncCallback(SendCallback), client);
AddPacketsSentText(packets[i] + "--" + (iSent++).ToString() + "\r\n\r\n");
}
}
System.Diagnostics.Debug.WriteLine("Read " + sPacketString.Length.ToString() + " bytes from " + client.SerialNumber + "\n" + sPacketString + "\n");
AddLogText("Read " + sPacketString.Length.ToString() + " bytes from " + client.SerialNumber + " \r\n");
AddLogText(sPacketString.ToString() + "\r\n\r\n");
waitForData(client);
}
catch (ObjectDisposedException)
{
System.Diagnostics.Debugger.Log(0, "1", "\nOnDataReceived: Socket has been closed\n");
}
catch (SocketException se)
{
if (se.ErrorCode == 10054) // Error code for Connection reset by peer
{
string sclientSerial = "??????";
if (client.SerialNumber != null || client.SerialNumber != "")
sclientSerial = client.SerialNumber;
AddLogText("Client " + sclientSerial + " Disconnected" + "\r\n\r\n");
System.Diagnostics.Debug.WriteLine("Client " + sclientSerial + " Disconnected" + "\n");
m_Clients.Remove(sclientSerial);
UpdateClientList();
}
else
{
System.Diagnostics.Debug.WriteLine("Read Data Exception \n");
System.Diagnostics.Debug.WriteLine(se.Message);
}
}
}
class SocketState
{
private Socket m_ClientSocket; //Socket connection to the client
private byte[] m_DataBuffer = new byte[256]; //Buffer to store the data sent by the client
private StringBuilder m_SB = new StringBuilder(); //for building recieved data into a string
/// <summary>
/// Gets or Sets the client Socket
/// </summary>
public Socket clientSocket
{
get { return m_ClientSocket; }
set { m_ClientSocket = value; }
}
/// <summary>
/// Gets the DataBuffer
/// </summary>
public byte[] DataBuffer
{
get { return m_DataBuffer; }
set { DataBuffer = value; }
}
/// <summary>
/// Gets or Sets the SB
/// </summary>
public StringBuilder SB
{
get { return m_SB; }
set { m_SB = value; }
}
public SocketState(Socket socket)
{
m_ClientSocket = socket;
m_ClientSocket.ReceiveBufferSize = 256;
m_ClientSocket.NoDelay = true;
//m_DataBuffer = Enumerable.Repeat((byte)0, 256).ToArray();
}
}
Edit: AddLogText() function added. This function is used to add text to a Text Box that is in the UI.
//Delegate - enables asychronous calls for setting the text property of the tb_ListeningLog
delegate void AddLogTextCallback(string text);
private void AddLogText(string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.tb_ListeningLog.InvokeRequired)
{
AddLogTextCallback d = new AddLogTextCallback(AddLogText);
this.Invoke(d, new object[] { text });
}
else
{
this.tb_ListeningLog.Text += text;
tb_ListeningLog.SelectionStart = tb_ListeningLog.Text.Length;
tb_ListeningLog.ScrollToCaret();
}
}
I'm taking a bit of a shot in the dark with this answer, but the code you've posted certainly helps.
The reason you're probably seeing slow performance as time goes on is because of the code in your readDataCallback method. The way you have it set up, the processing of the data is done before you go for another receive. This means that as the length of the processing increases, the duration between receiving your data increases.
I don't know what code is in a lot of your methods, but you should generally look at any loops that may be taking a while to finish. If you're having trouble finding the bottleneck by looking through your code, try finding which methods take the longest to finish and continue to narrow your code down.
For instance (I'm guessing that the bottleneck is in this area of code):
if (!m_Clients.ContainsKey(packet.SerialNumber))
{
m_Clients.Add(packet.SerialNumber, client);
AddLogText("Running UpdateClientList\r\n");
UpdateClientList();
AddLogText("Doing client.refreshAll\r\n");
string[] packets = client.refreshAll();
AddLogText("Doing for loop\r\n");
for (int i = 0; i < packets.Length; i++)
{
byte[] byteData = Encoding.ASCII.GetBytes(packets[i]);
client.SocketState.clientSocket.BeginSend(byteData, 0, byteData.Length, SocketFlags.None, new AsyncCallback(SendCallback), client);
AddPacketsSentText(packets[i] + "--" + (iSent++).ToString() + "\r\n\r\n");
}
}
Just observe the amount of time between each method with your eyes, or make it easier and use a Stopwatch or DateTime to show exact time.
Also, if you find that the behavior of the code cannot be made more efficient, you could toy around with the idea of processing the data in a separate thread. I'm assuming that this behavior isn't desired, though, because of the question at hand.
For your AddLogText method, try using tb_ListeningLog.Text.AppendText instead of +=.
I am not sure why you have such a long piece of code to read more data. Also, try placing the message in a Queue which can be processed by a different thread.
Here is an implementation I use:
// Read data from the client
private void ReadCallback(IAsyncResult ar)
{
StateObject state = (StateObject)ar.AsyncState;
Socket socket = state.workSocket;
try
{
if (socket.Connected)
{
// Read the socket
int bytesRead = socket.EndReceive(ar);
// Deserialize objects
foreach (MessageBase msg in MessageBase.Receive(socket, bytesRead, state))
{
// Add objects to the message queue
lock (this.messageQueue)
messageQueue.Enqueue(msg);
}
// Notify any event handlers
if (DataRecieved != null)
DataRecieved(socket, bytesRead);
// Asynchronously read more client data
socket.BeginReceive(state.Buffer, state.readOffset, state.BufferSize - state.readOffset, 0,
ReadCallback, state);
}
else
{
HandleClientDisconnect(socket);
}
}
catch (SocketException)
{
HandleClientDisconnect(socket);
}
}