I am working on a simple Client to send data to a server at my office. I tested the code locally on my computer using a server called TCPServer, I connect, send data, receive reply, disconnect, send again, rinse and repeat, it all works perfectly, but when I connect to office and do the same thing it connects fine, I can connect/disconnect forever, but when I send data it just hangs. Nothing is received in the log at the office. I can't send a single byte there.
Seems like a firewall issue doesn't it.
But I can run an older program I wrote years ago in Delphi (pascal), and it connects and sends the same data over without issue, same port, everything, so the problem is not a firewall issue. Thoughts on this? Here is the basic code layout.
Connect Button
Disconnect Button
Send Button
At the top of the class I declare the TcpClient Variable
public TcpClient m_client = new TcpClient();
In the _Click for Connect Button and Disconnect Button I have code to connect to server and set some indicators etc
private void ConnectButton_Click(object sender, EventArgs e)
{
string address = FixIP(IPAddressMaskedTextBox.Text);
int Port = Convert.ToInt32(PortNumberMaskedTextBox.Text);
Control control = (Control)sender;
String name = control.Name;
try
{
switch (name)
{
case ("ConnectButton"):
//Connect to server
connect(address, Port);
if (m_client.Connected)
{
SingleConnectionRichTextBox.Clear();
ConnectedLightButton.BackColor = Color.Lime;
SingleConnectionRichTextBox.Text += "Connected at IP " + address + " and Port " + Port.ToString() + "\r\n";
}
break;
case ("DisconnectButton"):
if (m_client.Connected)
{
SingleConnectionRichTextBox.Text += "Connection Terminated\r\n";
ConnectedLightButton.BackColor = Color.Red;
m_client.Client.Disconnect(false);
m_client = new TcpClient();
}
break;
}
}
catch (Exception err)
{
MessageBox.Show(err.ToString());
}
}
public void connect(string address, int port)
{
try
{
if (!m_client.Connected)
{
ConnectedLightButton.BackColor = Color.Yellow;
SingleConnectionRichTextBox.Text += "Attempting to Connect...\r\n";
m_client.Connect(address, port);
}
else
{
SingleConnectionRichTextBox.Text += "Connection Failed...\r\n";
ConnectedLightButton.BackColor = Color.Red;
throw new Exception("Connect: Already connected\r\n");
}
}
catch (Exception err)
{
MessageBox.Show(err.ToString());
}
}
The Send button has it's own event, mostly because when connecting to office it can take a minute for sockets to be created etc.
private void SendButton_Click(object sender, EventArgs e)
{
try
{
if (m_client.Connected)
{
string completeString = "";
for (int cnt = 0; cnt < SingleSendRichTextBox.Lines.Count() - 1; cnt++)
{
string aLine = Regex.Replace(SingleSendRichTextBox.Lines[cnt], #"\e\[(\d+;)*(\d+)?[ABCDHJKfmsu]", "");
if (cnt == 0)
{
//First line gets Start Block, plus a CR on end
aLine = (char)0x0B + aLine + (char)0x0D;
}
else if (cnt == (SingleSendRichTextBox.Lines.Count() -1))
{
//Last line gets CR + End Block + CR on end
aLine += (char)0x0D + (char)0x1C + (char)0x0D;
}
else
{
//All middle lines get CR on end
aLine += (char)0x0D;
}
//MessageBox.Show("Sending line# " + cnt.ToString() + " = " + aLine);
completeString += aLine;
}
Byte[] data = Encoding.ASCII.GetBytes(completeString);
WriteBytes(data);
ReadAllBytes();
}
else
{
MessageBox.Show("Nothing is connected currently...");
}
}
catch (Exception err)
{
MessageBox.Show(err.ToString());
}
}
public void WriteBytes(Byte[] data)
{
try
{
if ((m_client.Connected)&&(data.Count() > 0))
{
// Get access to network stream
NetworkStream stm = m_client.GetStream();
stm.Write(data, 0, data.Length);
stm.Flush();
//MessageBox.Show("Data Sent, length = " + data.Length.ToString());
}
else
{
MessageBox.Show("Either we are not connected, or there is no data to send!!");
}
}
catch (Exception err)
{
MessageBox.Show(err.ToString());
}
}
public void ReadAllBytes()
{
try
{
// Buffer to store the response bytes.
Byte[] readdata = new Byte[256];
// String to store the response ASCII representation.
String responseData = String.Empty;
NetworkStream stm = m_client.GetStream();
// Read the first batch of the TcpServer response bytes.
Int32 bytes = stm.Read(readdata, 0, readdata.Length);
responseData = Encoding.ASCII.GetString(readdata, 0, bytes);
SingleReplyRichTextBox.Text += responseData + "\r\n";
}
catch (Exception err)
{
MessageBox.Show(err.ToString());
}
}
Does anything in here raise a red flag that is obvious? I tried Stream VS NetworkStream. I tried turning off the Reply listener. I took this code and combined it all into one function, and moved the TcpClient creation over to a different Class as static so I could create it in each function, but while all of these worked fine locally, nothing works connecting to office. It won't send a single byte. I set m_client as static at the top too, works fine locally, not to office.
Is GetStream failing maybe? or its sending the data on a different socket?
use a Task and cancellation token to cancel the task. Don't init a "new" TCPclient. In the background the TCPclient will be not closed (socket-timeout of .net)
Use try catches to see the exception and add the log to the conversation to make it more clear pls
Related
I'm searching for a Streamclass which contains:
- a method for sending/receiving a byte-array
- a method for sending/receiving a string
The only Class I've found was NetworkStream. But the disadvantage with the NetworkStream-Class is, that if i want sending a string, i must befor convert this string into a byte-array and send this byte-array, because there is no method for sending strings directly.
And on the other side classes like Streamwriter have methods for sending/receiving strings, but there have no methods for sending/receiving a byte-array.
And if i try to combine these two Streamclasses like this:
TcpClient clientConnection = new TcpClient();
NetworkStream nws = clientConnection.GetStream();
StreamWriter sw = new StreamWriter(nws);
sw.writeLine("ABC");
sw.Flush();
nws.Write(byteArray, 0, lengthToSend);
i get a lot of strange errors (like byteArray will not receive on the other side completly), because i'm using here the same one stream in two different ways.
So, must i used NetworkStream-Class for my plan or exists there a better way?
I had the same problem,and the point is that the other side doesnt know what you are sending byte array or string so what i did is putting a header for each msg specially when dealing with serious server/client application coz you will have multiple data (user info, requesting info,replying info .. etc)
i am using streamwriter to send and streamreader to receive but i am also using threads
the connection remains open as long as the client is connected so i declare them once
here is a full example of my codes
public class Client
{
private StreamWriter swSender;
private StreamReader srReceiver;
private TcpClient tcpServer;
private Thread thrMessaging;
private string UserName = "UK";
private byte Tries = 0;
private bool Connected = false;
public void Connect()
{
if (!Connected)
{
IPAddress[] localIPs = Dns.GetHostAddresses(Dns.GetHostName());
string User = localIPs[0].ToString();
string ServIP = "127.0.0.1";//change this to your server ip
InitializeConnection(ServIP, User);
}
else
{
CloseConnection("Disconnected at user's request.");
}
}
private void InitializeConnection(string ServIp, string User)
{
IPAddress ipAddr = IPAddress.Parse(ServIp);
tcpServer = new TcpClient();
try
{
tcpServer.Connect(ipAddr, 1986);//change that 1986 to your server port
}
catch
{
if (Connected) CloseConnection("");
MessageBox.Show("Connecteing to " + ServIp + "\r\nServer is Down ... Try nomber " + Tries); return;
}
Connected = true;
UserName = User;
swSender = new StreamWriter(tcpServer.GetStream());
swSender.WriteLine(User);
swSender.Flush();
thrMessaging = new Thread(new ThreadStart(ReceiveMessages));
thrMessaging.Start();
}
private void ReceiveMessages()
{
srReceiver = new StreamReader(tcpServer.GetStream());
string ConResponse = srReceiver.ReadLine();
if (ConResponse[0] == '1')
{
}
else
{
string Reason = "Not Connected: ";
Reason += ConResponse.Substring(2, ConResponse.Length - 2);
return;
}
while (Connected)
{
try
{
string NewMsg = srReceiver.ReadLine();
if (NewMsg != null && NewMsg != "")
PacketHandler.HandlePacket(NewMsg, this);
}
catch { }
}
}
public void CloseConnection(string Reason)
{
try
{
Connected = false;
swSender.Close();
srReceiver.Close();
tcpServer.Close();
}
catch { }
}
public void SendMessage(string Msg)
{
if (Msg.Length >= 1)
{
try
{
Tries++;
swSender.WriteLine(Msg);
swSender.Flush();
Tries = 0;
}
catch
{
if (Tries < 5)
{
try
{
CloseConnection("No connection made");
Connect();
}
catch { }
SendMessage(Msg);
}
else { MessageBox.Show("Connecting to server faild for 5 tries"); Tries = 0; }
}
}
}
then at the packet handler i do my handling to check what kind of data the client received
something like this
public static void HandlePacket(string MsgRec, Client Client)
{
string[] Info = MsgRec.Split('|');
string Type = Info[0];
if (Type == "")
{
return;
}
string subtype = Info[1];
int TLen = Type.Length + subtype.Length + 2;
string Data = MsgRec.Remove(0, TLen);//this is the main data the server sent
ushort PacketType = ushort.Parse(Type);
ushort SubType = ushort.Parse(subtype);
switch ((Structs.PacketType)PacketType)
{
case Structs.PacketType.Login:
{
//do your stuff here
break
}
case Structs.PacketType.Image:
{
//convert the Data back to byte array then get the image out from it
break
}
case Structs.PacketType.ByteArray:
{
//convert the Data back to byte array
break
}
}
}
i know its kinda messy and not the perfect way to do it , but it works for me
and remember that at the other side when sending something you need to add the packet type and subtype , or just any header with any splitter if u doin something simple
Finally : i think using Sockets and packets would be much easier if u are sending small packets length
I have written code for server and multiple client using threads and sockets. Normally the clients exits by sending the 'exit' keyword to server but I want the server to also detect situation when the clients exits forcefully without sending 'exit' keyword to server, for example when user in middle of sending message to server presses cross button of client window. What I want is that server should detect this situation and displays some error code and continue receiving message from other clients connected to it.
Second problem I am facing how can I disconnect server even if multiple clients are connected to it. In my code I am using tcpListener.Stop() but when i use this method error message "server failed to start at ipaddress" is displayed and number of such windows opens is equivalent to number of clients server is listening. For example if server is listening to 1000 clients then 1000 such windows will open showing the earlier mentioned error message which doesn't look good from the point of person using this software. So How can I handle this situation? Also in this situation if clients again starts sending message to the server then is also starts receiving messages even though I have disconnected the server. The server should remain disconnected until the user restarts server.
Following is my code for server.
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
// Constants IP address of server and maximum number of clients server can connect.
static class Constants
{
public const string IP = "127.0.0.1";
public const int No_Of_Clients = 2;
}
// server port number
int port_number;
static IPAddress ipAddress = IPAddress.Parse(Constants.IP);
TcpListener tcpListener;
public Form1()
{
InitializeComponent();
button1.Click += button1_Click;
button2.Click += button2_Click;
//this.FormClosing += Form1_FormClosing;
}
//Socket socketForClient;
private void button1_Click(object sender, EventArgs e)
{
if (String.IsNullOrEmpty(textBox1.Text.Trim()))
{
System.Windows.Forms.MessageBox.Show("Port Number Empty", "Error");
}
else
{
port_number = int.Parse(textBox1.Text);
createserver(Constants.No_Of_Clients);
serveripaddress();
infoBox1.Text = string.Format("The server is now listening at port {0} at ip address {1}", port_number, Constants.IP);
infoBox1.Text = infoBox1.Text + "\r\n" + string.Format("The server can listen to maximum {0} number of clients", Constants.No_Of_Clients);
}
}
// this code disconnects the server
private void button2_Click(object sender, EventArgs e)
{
try
{
tcpListener.Stop();
}
catch (Exception f)
{
MessageBox.Show(f.Message);
}
}
public void serveripaddress()
{
serverip.Text = "Server IP : " + Constants.IP;
//serverport.Text = "Port Number : " + port.ToString();
}
// Starts server
private void createserver(int no_of_clients)
{
tcpListener = new TcpListener(ipAddress, port_number);
tcpListener.Start();
for (int i = 0; i < no_of_clients; i++)
{
Thread newThread = new Thread(new ThreadStart(Listeners));
newThread.Start();
}
} // end of createserver();
//listen to client receiving messages
public void Listeners()
{
Socket socketForClient;
try
{
socketForClient = tcpListener.AcceptSocket();
}
catch
{
System.Windows.Forms.MessageBox.Show(string.Format("Server failed to start at {0}:{1}", Constants.IP, port_number), "Error");
return;
}
if (socketForClient.Connected)
{
//System.Windows.Forms.MessageBox.Show("hello");
string string1 = string.Format("Client : " + socketForClient.RemoteEndPoint + " is now connected to server.");
infoBox1.Text = infoBox1.Text + "\r\n" + string1;
NetworkStream networkStream = new NetworkStream(socketForClient);
System.IO.StreamWriter streamWriter = new System.IO.StreamWriter(networkStream);
System.IO.StreamReader streamReader = new System.IO.StreamReader(networkStream);
string theString = "";
while (true)
{
try
{
theString = streamReader.ReadLine();
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
// if (streamReader.ReadLine() == null )
//{
// System.Windows.Forms.MessageBox.Show(string.Format("Server failed to start at {0}:{1}", Constants.IP, port_number), "Error");
// }
if (theString != "exit")
{
textBox2.Text = textBox2.Text + "\r\n" + "-----------------------------------------------------------------------------------";
string string2 = string.Format("Message recieved from client(" + socketForClient.RemoteEndPoint + ") : " + theString);
textBox2.Text = textBox2.Text + "\r\n" + string2;
// ASCII code for the message from client
string string3 = string.Format("ASCII Code for message is : ");
textBox2.Text = textBox2.Text + "\r\n" + string3;
string string4 = "";
foreach (char c in theString)
{
string4 += string.Format(System.Convert.ToInt32(c) + " ");
}
textBox2.Text = textBox2.Text + string4;
// Hex value of message from client
string hex = "";
foreach (char c in theString)
{
int tmp = c;
hex += String.Format("{0:x2}", (uint)System.Convert.ToUInt32(tmp.ToString()));
}
string string5 = string.Format("Hex Code for the message from client : " + hex);
textBox2.Text = textBox2.Text + "\r\n" + string5;
//sending acknowledgement to client
try
{
socketForClient.Send(new ASCIIEncoding().GetBytes("The string was recieved from Client(" + socketForClient.RemoteEndPoint + ") : " + theString));
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
} // end of if loop
// if exit from client
else
{
string string7 = string.Format("Client " + socketForClient.RemoteEndPoint + " has exited");
infoBox1.Text = infoBox1.Text + "\r\n" + string7;
break;
}
} // end of while loop
streamReader.Close();
networkStream.Close();
streamWriter.Close();
} // end of if loop
socketForClient.Close();
}
}
}
To be informed about closed client connections you have to send periodically a 'heartbeat' message to the client. If the client connection died the tcp/ip mechanism will inform you after the timeout that the connection died (can't remember the name of the exception).
If the client wants to know if the connection died he has also to send a heartbeat message.
This is needed as the tcp connection recognizes lost connections only if data is sent over this connection.
For the second problem you should keep a list of all the active clients (your variable socketForClient). When you want to end your server you close all the client connections (the clients in the list).
I am making a chat service for a game,
I am using a TCP listener an client for the account information, some sort of login service. I'm wondering if i can keep the socked the client connected to the server with, to check if he is still online, and keep sending him messages if he has new messages.
I already tried making a list of sockets for the login queue, but it disconnected the previous socket to to server as soon as i accepted a new socket.
byte[] usernameByte = new byte[100];
int usernameRecieved = s.Receive(usernameByte);
//guiController.setText(System.DateTime.Now + " Recieved Login...");
byte[] passByte = new byte[100];
int passRecieved = s.Receive(passByte);
//guiController.setText(System.DateTime.Now + " Recieved Password...");
string username = "";
string password = "";
for (int i = 0; i < usernameRecieved; i++)
username += (Convert.ToChar(usernameByte[i]));
for (int i = 0; i < passRecieved; i++)
password += (Convert.ToChar(passByte[i]));
if (DomainController.getInstance().checkAccount(username, password))
{
ASCIIEncoding asen = new ASCIIEncoding();
s.Send(asen.GetBytes("true"));
s.Send(asen.GetBytes("U are succesfully logged in, press enter to continue"));
guiController.setText(serverName,System.DateTime.Now+"");
guiController.setText(serverName, "Sent Acknowledgement - Logged in");
}
else
{
ASCIIEncoding asen = new ASCIIEncoding();
s.Send(asen.GetBytes("false"));
s.Send(asen.GetBytes("U are NOT logged in, press enter to continue"));
guiController.setText(serverName, System.DateTime.Now + "");
guiController.setText(serverName, "\nSent Acknowledgement - Not logged in");
}
This is the code i currently use to check the account information the user send me. Right after i send this the user dropd the connection and i move on to the next one.
I have tried making 1 list of seperate sockets and processing them one by one, but that failed because the previous socket's connection dropped, even tho it were 2 different machines that tried to connect.
Does anyone have a sollution / a way to save sockets, that I can use to make the program keep all the connections alive? so i can send a message from user 1 to user 2, and just use the socket they connected with? or do i need to add an id every time they make a connection?
EDIT
The client Code: (this is just a test client)
while (true)
{
TcpClient tcpclnt = new TcpClient();
Console.WriteLine("Connecting.....");
tcpclnt.Connect("xx.xxx.xxx.xx", 26862);
// use the ipaddress as in the server program
while(!(checkResponse(tcpclnt.GetStream())))
{
Thread.Sleep(1000);
}
Console.WriteLine("Connected");
Console.Write("Enter the string to be transmitted : ");
String str = Console.ReadLine();
if (str == "")
{
str = " ";
}
Stream stm = tcpclnt.GetStream();
ASCIIEncoding asen = new ASCIIEncoding();
byte[] ba = asen.GetBytes(str);
Console.WriteLine("Transmitting.....");
stm.Write(ba, 0, ba.Length);
Console.Write("Enter the string to be transmitted : ");
String str2 = Console.ReadLine();
if (str2 == "")
{
str2 = " ";
}
Stream stm2 = tcpclnt.GetStream();
ASCIIEncoding asen2 = new ASCIIEncoding();
byte[] ba2 = asen2.GetBytes(str2);
Console.WriteLine("Transmitting.....");
stm.Write(ba2, 0, ba2.Length);
if (str == "false")
{
blijvenWerken = false;
}
byte[] bb = new byte[100];
int k = stm.Read(bb, 0, 100);
for (int i = 0; i < k; i++)
Console.Write(Convert.ToChar(bb[i]));
byte[] bb2 = new byte[100];
int k2 = stm.Read(bb2, 0, 100);
Console.Write("\n");
for (int i = 0; i < k2; i++)
Console.Write(Convert.ToChar(bb2[i]));
Console.WriteLine("\n");
tcpclnt.Close();
Thread.Sleep(1000);
}
Server getting the sockets:
This bit of code is on the loginserver, its because i can only accept 1 socket every time to keep the connection alive, that i put queueCount on a maximum of 1.
I want to be able to make a list of Sockets that i accepted to add to a User account.
while (loginServerOn)
{
if (queueCount < 1)
{
if (loginServer.getLoginListener().Pending())
{
loginQueue.Add(loginServer.getSocket());
ASCIIEncoding asen = new ASCIIEncoding();
Socket s = loginQueue.First();
try
{
s.Send(asen.GetBytes("true"));
queueCount++;
}
catch
{
loginQueue.Remove(s);
}
}
}
}
The function that returns the accepted socket.
public Socket getSocket()
{
return myList.AcceptSocket();
}
EDIT: Essence of the question
I want to add the socked or client recieved to my Account object, so every connection has an Account its linked to, when i want to send a message to a certain account, it should send a message to the socked or client bound to that account, can you help/show me how i can achieve this?
This is still c# and sockets but my approach is different to yours.
I went with the concept of a "connectedCleint" which is similar in purpose to what you've called an account.
I have a class called ServerTerminal which is responsible for accepting and top level management of socket connections. In this i've got:
public Dictionary<long, ConnectedClient> DictConnectedClients =
new Dictionary<long, ConnectedClient>();
So this is my list of connected clients indexed by the sockethandle.
To accept connections i've got a routine:
public void StartListen(int port)
{
socketClosed = false;
IPEndPoint ipLocal = new IPEndPoint(IPAddress.Any, port);
listenSocket = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
//bind to local IP Address...
//if ip address is allready being used write to log
try
{
listenSocket.Bind(ipLocal);
}
catch (Exception excpt)
{
// Deal with this.. write your own log code here ?
socketClosed = true;
return;
}
//start listening...
listenSocket.Listen(100); // Max 100 connections for my app
// create the call back for any client connections...
listenSocket.BeginAccept(new AsyncCallback(OnClientConnection), null);
}
So when a client connects it then fires off:
private void OnClientConnection(IAsyncResult asyn)
{
if (socketClosed)
{
return;
}
try
{
Socket clientSocket = listenSocket.EndAccept(asyn);
ConnectedClient connectedClient = new ConnectedClient(clientSocket, this, _ServerTerminalReceiveMode);
//connectedClient.MessageReceived += OnMessageReceived;
connectedClient.Disconnected += OnDisconnection;
connectedClient.dbMessageReceived += OndbMessageReceived;
connectedClient.ccSocketFaulted += ccSocketFaulted;
connectedClient.StartListening();
long key = clientSocket.Handle.ToInt64();
if (DictConnectedClients.ContainsKey(connectedClient.SocketHandleInt64))
{
// Already here - use your own error reporting..
}
lock (DictConnectedClients)
{
DictConnectedClients[key] = connectedClient;
}
// create the call back for any client connections...
listenSocket.BeginAccept(new AsyncCallback(OnClientConnection), null);
}
catch (ObjectDisposedException excpt)
{
// Your own code here..
}
catch (Exception excpt)
{
// Your own code here...
}
}
The crucial part of this for you is:
// create the call back for any client connections...
listenSocket.BeginAccept(new AsyncCallback(OnClientConnection), null);
This sets up the serverterminal to receive new connections.
Edit:
Cut down version of my connectedclient:
public class ConnectedClient
{
private Socket mySocket;
private SocketIO mySocketIO;
private long _mySocketHandleInt64 = 0;
// These events are pass through; ConnectedClient offers them but really
// they are from SocketIO
public event TCPTerminal_ConnectDel Connected
{
add
{
mySocketIO.Connected += value;
}
remove
{
mySocketIO.Connected -= value;
}
}
public event TCPTerminal_DisconnectDel Disconnected
{
add
{
mySocketIO.Disconnected += value;
}
remove
{
mySocketIO.Disconnected -= value;
}
}
// Own Events
public event TCPTerminal_TxMessagePublished TxMessageReceived;
public delegate void SocketFaulted(ConnectedClient cc);
public event SocketFaulted ccSocketFaulted;
private void OnTxMessageReceived(Socket socket, TxMessage myTxMessage)
{
// process your message
}
private void OnMessageSent(int MessageNumber, int MessageType)
{
// successful send, do what you want..
}
public ConnectedClient(Socket clientSocket, ServerTerminal ParentST)
{
Init(clientSocket, ParentST, ReceiveMode.Handler);
}
public ConnectedClient(Socket clientSocket, ServerTerminal ParentST, ReceiveMode RecMode)
{
Init(clientSocket, ParentST, RecMode);
}
private void Init(Socket clientSocket, ServerTerminal ParentST, ReceiveMode RecMode)
{
ParentServerTerminal = ParentST;
_myReceiveMode = RecMode;
_FirstConnected = DateTime.Now;
mySocket = clientSocket;
_mySocketHandleInt64 = mySocket.Handle.ToInt64();
mySocketIO = new SocketIO(clientSocket, RecMode);
// Register for events
mySocketIO.TxMessageReceived += OnTxMessageReceived;
mySocketIO.MessageSent += OnMessageSent;
mySocketIO.dbMessageReceived += OndbMessageReceived;
}
public void StartListening()
{
mySocketIO.StartReceiving();
}
public void Close()
{
if (mySocketIO != null)
{
mySocketIO.Close();
mySocketIO = null;
}
try
{
mySocket.Close();
}
catch
{
// We're closing.. don't worry about it
}
}
public void SendMessage(int MessageNumber, int MessageType, string Message)
{
if (mySocket != null && mySocketIO != null)
{
try
{
mySocketIO.SendMessage(MessageNumber, MessageType, Message);
}
catch
{
// mySocketIO disposed inbetween check and call
}
}
else
{
// Raise socket faulted event
if (ccSocketFaulted != null)
ccSocketFaulted(this);
}
}
}
}
Some useful links:
This is where I started:
http://vadmyst.blogspot.com.au/2008/01/how-to-transfer-fixed-sized-data-with.html
http://vadmyst.blogspot.com.au/2008/03/part-2-how-to-transfer-fixed-sized-data.html
And..
C# Sockets and Multithreading
Cause a connected socket to accept new messages right after .BeginReceive?
http://nitoprograms.blogspot.com.au/2009/04/tcpip-net-sockets-faq.html
http://www.codeproject.com/Articles/83102/C-SocketAsyncEventArgs-High-Performance-Socket-Cod
I can't post my entire solution just now; there is a flaw in my server code I need to debug; plus there are parts which my employer may not want published. But i based my code on what Vadym had for variable length messages.
When a server gets ready to accept TCP connections, it creates a new TCP socket, Bind() it to a port and uses the Listen() method. When a connection request comes in, the Listen() method returns a new socket that the server and client use for communication. The server and client can pass data back and forth using Send() and Receive() at this point. If the client disconnects, the server's Receive() terminates with 0 bytes of data.
If you want to wait for another connection request once you've accepted the first connection (i.e., while you are interacting with the first client) this can be done. At this point, you'll need to use something like threads or asynchronous methods so you can handle more than one connection. Basically, you will be able to Accept() connection requests from your listening socket.
Mike
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);
}
}
I'm trying to scan multiple ports at once using asynchronymous scanning. The problem is that I can only display the first working port and then waiting like 20 seconds my app is closing with out telling me that the port is closed.
What could be wrong with this code?
private void btnStart_Click(object sender, EventArgs e)
{
for (int port = 80; port < 100; port++)
{
ScanPort(port);
}
}
private void ScanPort(int port)
{
var client = new TcpClient();
try
{
client.BeginConnect(IPAddress.Parse("74.125.226.84"), port, new AsyncCallback(CallBack), client);
}
catch (SocketException)
{
client.Close();
}
}
private void CallBack(IAsyncResult result)
{
var client = (TcpClient)result.AsyncState;
client.EndConnect(result);
if (client.Connected)
{
this.Invoke((MethodInvoker)delegate
{
txtDisplay.Text += "open2" + Environment.NewLine;
});
}
else
{
this.Invoke((MethodInvoker)delegate
{
txtDisplay.Text += "closed2" + Environment.NewLine;
});
}
}
In your callback method, I would make sure close the connection and dispose of the TcpClient. Also TcpClient.EndConnect(IAsyncResult) can also throw exceptions. I also do not see where capturing the port number for display to the user. I would write the callback something like this.
Edit: I didn't actually compile or execute my code (sorry). I also found this other article that shows how to create a port scanner in C#, http://www.dijksterhuis.org/building-a-simple-portscanner-in-c/ There is a comment in this post stating,
There is a gotcha here : The .NET implementation of TCPClient.Close() function does not actually close the connection properly. So we need to do the additional steps of obtaining the stream representing the connection and closing this as well before calling TCPClient.Close.
private void CallBack(IAsyncResult result)
{
var client = (TcpClient)result.AsyncState;
bool connected = false;
try
{
client.EndConnect(result);
connected = client.Connected;
}
catch (SocketException)
{
}
catch (ObjectDisposedException)
{
}
finally
{
if (client.Connected)
{
client.Close();
}
client.Dispose();
}
if (connected)
{
this.Invoke((MethodInvoker)delegate
{
txtDisplay.Text += "open2" + Environment.NewLine;
});
}
else
{
this.Invoke((MethodInvoker)delegate
{
txtDisplay.Text += "closed2" + Environment.NewLine;
});
}
}