Why server app receives 50% of client messages? - c#

Pre:
Client open socket to send data to the server:
private void Form1_Load(object sender, EventArgs e)
{
client = new TcpClient();
client.BeginConnect("127.0.0.1", 995, new AsyncCallback(ConnectCallback), client);
}
private void ConnectCallback(IAsyncResult _result) // it will send hello message from client
{
string data;
byte[] remdata = { };
IAsyncResult inResult = _result;
currentProcess = Process.GetCurrentProcess();
string currentProcessAsText = currentProcess.Id.ToString();
SetControlPropertyThreadSafe(proccessIdLabel, "Text", currentProcessAsText);
try {
sock = client.Client;
// send hello message
data = "Client with proccess id " + currentProcessAsText + " is connecting";
sock.Send(Encoding.ASCII.GetBytes(data));
SetControlPropertyThreadSafe(answersTextBox, "Text", answersTextBox.Text + "\n"+ GetCurrentTime() + " Connection established");
}
catch
{
SetControlPropertyThreadSafe(answersTextBox, "Text", answersTextBox.Text + "\n" + GetCurrentTime() + " Can't connect");
}
}
After that I have handler for click on some button (to send messages):
private void SendButton_Click(object sender, EventArgs e)
{
try
{
string data;
sock = client.Client;
data = "Some text";
sock.Send(Encoding.ASCII.GetBytes(data));
}
catch
{
SetControlPropertyThreadSafe(answersTextBox, "Text", "Can't connect");
}
}
Also handler for form close to send server exit command, so he will stop thread for this client:
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
try
{
sock.Send(Encoding.ASCII.GetBytes("exit"));
sock.Close();
}
catch
{
}
}
Server listening port and handles messages:
private void Listeners()
{
Socket socketForClient = Listener.AcceptSocket();
string data;
int i = 0;
if (socketForClient.Connected)
{
string remoteHost = socketForClient.RemoteEndPoint.ToString();
Console.WriteLine(Message("Client:" + remoteHost + " now connected to server."));
while (true)
{
// буфер данных
byte[] buf = new byte[1024];
try
{
int messageLength = socketForClient.Receive(buf);
if (messageLength > 0)
{
byte[] cldata = new byte[messageLength];
socketForClient.Receive(cldata);
data = "";
data = Encoding.ASCII.GetString(cldata).Trim();
if (data.Contains("exit"))
{
socketForClient.Close();
string message = Message("Client:" + remoteHost + " is disconnected from the server (client wish).");
Console.WriteLine(message);
return;
}
else
{
Console.WriteLine(Message("Recevied message from client " + remoteHost + ":\n"));
Console.WriteLine(data);
Console.WriteLine("\nEOF\n");
}
}
}
catch
{
string message = Message("Client:" + remoteHost + " is disconnected from the server (forced close).");
Console.WriteLine(message);
socketForClient.Close();
return;
}
}
}
}
private void ServStart()
{
Listener = new TcpListener(LocalPort);
Listener.Start(); // начали слушать
Console.WriteLine("Waiting connections [" + Convert.ToString(LocalPort) + "]...");
for (int i = 0; i < 1000; i++)
{
Thread newThread = new Thread(new ThreadStart(Listeners));
newThread.Start();
}
}
So on server start it creates 1000 threads, which are listens clients messages.
Problems:
I will describe some situation:
Start server
Server starts threads and ready to accept clients connections
Start client
Connection is establishing. Server says that client connected on some port. Client send "hello" message. Server doesn't handle this hello message.
Push the button, so client will send Some text to the server. Server handles this message.
Push the button. Client sends "some text" again. Server doesn't handles that message.
Push the button. Client sends "some text" again. Server handles that message.
If I will push again, it will not handle it obviously....
Server logs:
Why server receives/client sends only 1 of 2 messages? What can cause it?
Also I have problems with sending exit message to the server, when client form is closing. I send exit message on this action.
So situation:
I just pushed the button and server handled it (so server will not handle next message).
I close the form, message is sended, but either client sends wrong message or server receives wrong message.
Situation in console:
You can see, that when form was closed and client sended exit, server handled empty message. Why?
Situation when client exit command passed by server normally:
.....
Client sends data, server doesn't handled it
Now, server will handle client, so we try to close form:
Console:
So in 2nd item client had sended hello message and server failed to handle it. In 3rd item client sends exit command and server passed it correctly.
Main question: why server handles only 1 of 2 messages from client?
Another point: also I found, that when client send exit data, server receives exit\0\0\0\0\0\0\0\0\ (or more, or less \0 symbols). Why?
Good news I think, that server receives or not receives messages constantly. 1 message is received, 1 message is not. That says about my lack of knowledges, but not random error.

So many bugs. :(
That said, the biggest one I noticed was this one:
int messageLength = socketForClient.Receive(buf);
if (messageLength > 0)
{
byte[] cldata = new byte[messageLength];
socketForClient.Receive(cldata);
data = "";
data = Encoding.ASCII.GetString(cldata).Trim();
First, understand that in TCP, you have no guarantees about the number of bytes any given receive operation will receive. No matter how the remote endpoint sends the data, you could receive all of the data at once, or only parts of it, in separate receive operations. TCP guarantees the bytes will be received in the same order in which they were sent, and nothing more.
But the above code not only fails to take that into account, it's just completely wrong. The number of bytes received in the first operation is how many bytes were received in that operation. But you are using that number as if it would tell you something about the number of bytes received in the next call to Receive(). It does nothing of the sort. At the same time, you ignore the data you received in the first operation.
Instead, your code should look more like this:
int messageLength = socketForClient.Receive(buf);
if (messageLength > 0)
{
data = Encoding.ASCII.GetString(buf, 0, messageLength).Trim();
That's still not quite right, because of course you could receive just a partial message in the call to Receive(), or even more than one message concatenated together. But at least you're likely to see all of the text.
That change will address the specific question you've asked about. If you have trouble figuring out how to address the other bugs, please feel free to post concise, specific questions and code examples to ask for help on those. See https://stackoverflow.com/help/mcve and https://stackoverflow.com/help/how-to-ask for advice on better ways to present your question.

Related

How to communicate between two Unity apps with TCP?

Update
I figured out what the problem was. I was trying to move too much data over TCP, and it was causing freeze-ups. For some reason, this wasn't manifesting in the editor...who knows for what reason. If anyone else stumbles upon this problem (in a program like Unity, where functions are looping constantly and data is always being processed), consider that you're moving too much irrelevant data.
Original Post
I've run into quite the problem, and I'm hoping I can receive some guidance.
In short, I'm wondering how to use TCP to communicate two Unity apps over the same computer. I've gotten it functioning in editor, but when both apps are built, communication quickly breaks down.
This is really stumping me, because I don't understand why an app would work in the Editor environment, but not in the official build.
When I use TCP to communicate between two Unity apps (on the same computer), it works so long as one of them is kept in the Unity environment. That is, if I build one app, and open the other in the Unity editor, TCP communication works flawlessly.
Here is some more background: One of my apps is functioning as a User Interface, and the other is interfacing with a Looking Glass to provide a holographic display of in-game objects. Originally, they were combined into one App - but I had a lot of trouble getting Unity's multidisplay support to function between two monitors of different resolutions. Looking Glass factory even provides a prefab to do just this, but it is broken in the current SDK. So I have resorted to using sockets to interface between two apps, one for each monitor.
I'm using C#'s TCP listener class: https://learn.microsoft.com/en-us/dotnet/api/system.net.sockets.tcplistener?view=netframework-4.8
And TCP client class: https://learn.microsoft.com/en-us/dotnet/api/system.net.sockets.tcpclient?view=netframework-4.8
Presently, the UI is acting as the TCPListener, and the application that produces holograms is the TCPClient. Within each of these applications, I'm using two Queues - an IncomingMessages queue and an Outgoing Messages queue - which are global variables shared between the main thread and the networking thread.
TCP Listener:
private void Start()
{
incomingMessages = new Queue();
outgoingMessages = new Queue();
Application.runInBackground = true;
thread = new Thread(new ThreadStart(Receive));
thread.Start();
//stuff happens that's irrelevant to this question. And then...
}
void Receive()
{
TcpListener server = null;
try
{
// Set the TcpListener on port 13000.
Int32 port = 13000;
IPAddress localAddr = IPAddress.Parse("127.0.0.1");
// TcpListener server = new TcpListener(port);
server = new TcpListener(localAddr, port);
// Start listening for client requests.
server.Start();
// Buffer for reading data
Byte[] bytes = new Byte[256];
String data = null;
// Enter the listening loop.
Debug.Log("About to reenter main while in Server...");
while (threadContinue)
{
Debug.Log("Waiting for a connection... ");
// Perform a blocking call to accept requests.
// You could also user server.AcceptSocket() here.
TcpClient client = server.AcceptTcpClient();
Debug.Log("Connected!");
data = null;
// Get a stream object for reading and writing
NetworkStream stream = client.GetStream();
int i;
// Loop to receive all the data sent by the client.
while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
{
// Translate data bytes to a ASCII string.
data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
Debug.Log("Received from Client: " + data);
lock (this)
incomingMessages.Enqueue(data);
string response = supplyData();
byte[] msg = System.Text.Encoding.ASCII.GetBytes(response);
// Send back a response.
stream.Write(msg, 0, msg.Length);
Debug.Log("Sent to Client: " + response);
}
// Shutdown and end connection
client.Close();
}
}
catch (SocketException e)
{
Debug.Log("SocketException: ");
Debug.Log(e);
}
finally
{
// Stop listening for new clients.
server.Stop();
}
Debug.Log("Exiting 'Receive'");
}
And here is the TCP Client. It attempts to connect a regular intervals, and also whenever new data is available. This is so that it can receive information from the server regularly and share new data whenever it is available:
void Start()
{
//prepare networking
Application.runInBackground = true;
outgoingMessages = new Queue();
incomingMessages = new Queue();
thread = new Thread(new ThreadStart(Connect));
thread.Start();
//stuff happens that's irrelevant to this question...
}
private void Connect()
{
String server = "127.0.0.1";
Int32 port = 13000;
string message = "";
while (threadContinue == true)
{
if (timeToConnect())
{
lastConnection = ourTime;
if (outgoingMessages.Count > 0)
message = outgoingMessages.Dequeue().ToString();
else
message = "Nothing to report.";
try
{
// Create a TcpClient.
// Note, for this client to work you need to have a TcpServer
// connected to the same address as specified by the server, port
// combination.
client = new TcpClient(server, port);
// Translate the passed message into ASCII and store it as a Byte array.
Byte[] data = System.Text.Encoding.ASCII.GetBytes(message);
// Get a client stream for reading and writing.
// Stream stream = client.GetStream();
stream = client.GetStream();
// Send the message to the connected TcpServer.
stream.Write(data, 0, data.Length);
Debug.Log("Sent to Server: " + message);
// Buffer to store the response bytes.
data = new Byte[256];
// String to store the response ASCII representation.
String responseData = String.Empty;
// Read the first batch of the TcpServer response bytes.
Int32 bytes = stream.Read(data, 0, data.Length);
responseData = System.Text.Encoding.ASCII.GetString(data, 0, bytes);
lock (this)
incomingMessages.Enqueue(responseData);
Debug.Log("Received from Server: " + responseData);
stream.Close();
client.Close();
}
catch (ArgumentNullException e)
{
Debug.Log("ArgumentNullException: ");
Debug.Log(e);
outgoingMessages.Enqueue(message);
}
catch (SocketException e)
{
Debug.Log("SocketException: ");
Debug.Log(e);
outgoingMessages.Enqueue(message);
}
}
}
}
private bool timeToConnect()
{
if ((ourTime - lastConnection > NETWORK_DELAY) || (outgoingMessages.Count > 0))
return true;
return false;
}
Instantiated in separate threads so that Unity's main thread can continue unhindered.
Again - it works in Editor, but when I build it, it breaks.
Update
I figured out what the problem was. I was trying to move too much data over TCP, and it was causing freeze-ups. For some reason, this wasn't manifesting in the editor...just in the exported app. Who knows for what reason. If anyone else stumbles upon this problem...where you're bypassing Unity's multidisplay functionality by building multiple apps that communicate over network...consider that you're burdening your queues with too much data.

C# thread not listening for incoming messages from server

Every time a client connects to my TCP server, it creates a new thread specifically for that client (there are few clients so there's no worry about overflow of threads). However, after lots of debugging, I have found that once 2 clients join, the first client can send about ~3 messages and then will no longer function, however the second will keep functioning no matter how many times you send a message (and the first client will receive it). If a 3rd client joins the party, then the second client will have the same fate as the first one, will be able to send about ~3 messages, and then suddenly stop working without explanation. If someone could explain why this is happening, it would be much appreciated! Here's the code for the server (which I think is where the error is occuring):
s = listener.AcceptSocket();
clients.Add(s);
NewListener();
byte[] b = new byte[100];
while (running)
{
try
{
int k = s.Receive(b);
string message = "";
for (int i = 0; i < k; i++)
{
message += Convert.ToChar(b[i]);
}
if (message == "exitprogram") //break loop
{
break;
}
ASCIIEncoding asen = new ASCIIEncoding();
foreach (Socket client in clients)
{
client.Send(asen.GetBytes(message));
}
} catch
{
Console.WriteLine("Error occured on thread " + Thread.CurrentThread.Name);
break;
}
}

error after receiving data over TCP/IP connection using Asynchronous socket communication in c#

i have tried to make TCP/IP connection for a server/client application with asynchronous socket communication and it was successful. However, i am not able to display all the message received by client. i have run time error, may i know where is the problem.
private void onreceive(IAsyncResult ar)
{
Socket clientsocket = (Socket)ar.AsyncState;
int byteread = clientsocket.EndReceive(ar);
data msgreceived = new data(byteData,byteread);
data msgtosend = new data();
byte[] message;
ClientInfo clientinfo = new ClientInfo();
clientinfo.socket = clientsocket;
IPEndPoint remote = clientsocket.RemoteEndPoint as IPEndPoint;
remoteip = remote.Address;
clientinfo.IMEI = msgreceived.IMEI;
clientlist.Add(clientinfo);
msgtosend.strMessage = "client with ip address " + remoteip + " with this IMEI " + clientinfo.IMEI;
txtLog.Text += msgreceived.strMessage + "\r\n";
if (clientinfo.socket.Connected == true)
{
clientsocket.BeginReceive(byteData, 0, byteData.Length, SocketFlags.None, new AsyncCallback(onreceive), clientsocket);
}}
in data class:
public data(byte[] Data, int msglen)
{
//int imeilen = BitConverter.ToInt32(Data, 0);
//int msglen = BitConverter.ToInt32(Data, 4);
//if (imeilen > 0)
// this.IMEI = Encoding.ASCII.GetString(Data, 8, imeilen);
//else this.IMEI = "unknown";
if (msglen > 0)
this.strMessage = Encoding.ASCII.GetString(Data,0, msglen);
else
this.strMessage = "no message";
}
You are somehow magically expecting to know when you have received a "message" despite not even having decided what a message is or writing any code to determine whether one has been received.
There is an old saying in the TCP programming community -- nobody is allowed to write any code that uses TCP until they can 100% explain what every word in this true statement means:
"TCP is a reliable, byte-stream protocol that does not preserve application message boundaries."
You have to code to implement a "message". TCP is a byte-stream protocol. If you want to send and receive messages, you'll need to create a protocol that does that.
Start by defining what a message is, how one is delimited, and so on. Then write code to send and receive messages according to your definition. When your onReceive function is called, that just means the TCP/IP stack received some bytes -- you need to decide whether that's a message or not by following the rules for a the messaging protocol.

C# sockets server unable to receive data from same client connection more than once

I had recently asked a related question In C# how do I have a socket continue to stay open and accept new data? and somewhat solved it, but now I'm having trouble getting my server to receive data from the same client more than once.
I have written a simple socket server using a windows form in VS that has a button that calls a receive function
public void Receive()
{
try
{
byte[] bytes = new byte[256];
received = s1.Accept().Receive(bytes);
receivedText.Text += System.Text.ASCIIEncoding.ASCII.GetString(bytes);
}
catch (SocketException e)
{
Console.WriteLine("{0} Error code: {1}.", e.Message, e.ErrorCode);
return;
}
}
It works if the first time I send from my client, but if I try sending anything else and click on receive, my server just loops and never picks up the new data. However, if I send from somewhere else, or restart the connection from my client, I'm able to send data.
I would like to have my server able to receive any amount of data from the same client(s) at a time. Please ask if you need more code/details. Not sure what's relevant as I'm pretty new to socket programming.
You must call Accept() only once per client, not every time you want to receive new data. Accept() basically waits for a client to connect to your server socket, s1 (and returns a new socket to send/receive data with this client), so here each time your Receive() function is called your socket waits for another client to connect, that's why it works only once.
Here is an example (the code comes from your previous question) :
s1.Bind(endP);
s1.Listen(10);
Socket s2 = s1.Accept(); // Waits for a client to connect and return a socket, s2, to communicate with him
while (true) {
Receive(s2);
}
...
Receive() function :
public void Receive(Socket s)
{
try
{
byte[] bytes = new byte[256];
received = s.Receive(bytes);
receivedText.Text += System.Text.ASCIIEncoding.ASCII.GetString(bytes);
}
catch (SocketException e)
{
Console.WriteLine("{0} Error code: {1}.", e.Message, e.ErrorCode);
return;
}
}

C# Web Server Connection Problem

I'm trying to make a simple C# web server that, at this stage, you can access via your browser and will just do a "Hello World".
The problem I'm having is that the server can receive data fine - I get the browser's header information - but the browser doesn't receive anything I send. Furthermore, I can only connect to the server by going to localhost (or 127.0.0.1). I can't get to it by going to my IP and it's not a network setting because Apache works fine if I run that instead. Also, I'm using a port monitoring program and after I attempt a connection from a browser, the process's port gets stuck in a TIME_WAIT state even though I told the connection to close and it should be back to LISTEN.
Here's the relevant code. A couple calls might not make sense but this is a piece of a larger program.
class ConnectionHandler
{
private Server server;
private TcpListener tcp;
private ArrayList connections;
private bool listening;
private Thread listeningThread;
public Server getServer()
{
return server;
}
private void log(String s, bool e)
{
server.log("Connection Manager: " + s, e);
}
private void threadedListen()
{
while (listening)
{
try
{
TcpClient t = tcp.AcceptTcpClient();
Connection conn = new Connection(this, t);
}
catch (NullReferenceException)
{
log("unable to accept connections!", true);
}
}
log("Stopped listening", false);
}
public void listen()
{
log("Listening for new connections", false);
tcp.Start();
listening = true;
if (listeningThread != null && listeningThread.IsAlive)
{
listeningThread.Abort();
}
listeningThread = new Thread(new ThreadStart(
this.threadedListen));
listeningThread.Start();
}
public void stop()
{
listening = false;
if (listeningThread != null)
{
listeningThread.Abort();
log("Forced stop", false);
}
log("Stopped listening", false);
}
public ConnectionHandler(Server server)
{
this.server = server;
tcp = new TcpListener(new IPEndPoint(
IPAddress.Parse("127.0.0.1"), 80));
connections = new ArrayList();
}
}
class Connection
{
private Socket socket;
private TcpClient tcp;
private ConnectionHandler ch;
public Connection(ConnectionHandler ch, TcpClient t)
{
try
{
this.ch = ch;
this.tcp = t;
ch.getServer().log("new tcp connection to "
+ this.tcp.Client.RemoteEndPoint.ToString(), false);
NetworkStream ns = t.GetStream();
String responseString;
Byte[] response;
Int32 bytes;
responseString = String.Empty;
response = new Byte[512];
bytes = ns.Read(response, 0, response.Length);
responseString =
System.Text.Encoding.ASCII.GetString(response, 0, bytes);
ch.getServer().log("Received: " + responseString);
String msg = "<html>Hello World</html>";
String fullMsg = "HTTP/1.x 200 OK\r\n"
+ "Server: Test Server\r\n"
+ "Content-Type: text/html; "
+ "charset=UTF-8\r\n"
+ "Content-Length: " + msg.Length + "\r\n"
+ "Date: Sun, 10 Aug 2008 22:59:59 GMT"
+ "\r\n";
nsSend(fullMsg, ns);
nsSend(msg, ns);
ns.Close();
tcp.Close();
}
catch (ArgumentNullException e)
{
ch.getServer().log("connection error: argument null exception: " + e);
}
catch (SocketException e)
{
ch.getServer().log("connection error: socket exception: " + e);
}
}
private void nsSend(String s, NetworkStream ns)
{
Byte[] data = System.Text.Encoding.ASCII.GetBytes(s);
ns.Write(data, 0, data.Length);
ns.Flush();
ch.getServer().log("Sent: " + s);
}
}
Does anyone have any ideas? It feels like it's gotta be something stupid on my part but I just don't know what. I'd really appreciate any insight
You might like to know that you can use HttpListener to write a basic web-server very easily - this deals with most of the painful bits, letting you concentrate on writing the actual code. The MSDN page gives an example.
Note that this uses HTTP.SYS, which is good - but means that non-admins need to be explicitly given access to open ports; on xp you can do this with httpcfg; on vista you can use netsh. See here for more.
One thing which isn't a problem at the moment but might be later on is that your content length is based on the Unicode length of the message string, not the binary length.
There's also rather a lot going on in the Connection constructor - stuff that really doesn't belong in a constructor at all, IMO. It also doesn't close things if exceptions occur.
Have you looked at what's going on in a network tracer like WireShark? That would be the easiest way of seeing whether any data is getting sent back to the client.
Alternatively, post a short but complete version of your code which we can compile and run ourselves.
Maybe I'm just missing something but the reason you can only connect on localhost is because the IP you are listening on is 127.0.0.1, this makes the TCPListener only listen on that IP address. And I don't see anywhere you are calling any client disconnect, the TCPListener is a blocking call, so it sits there forever until a connection is made. In my past experience with TCP/IP and the TCPListener, TCPClient classes there is not much of a way to force the listener to drop it's connection until you drop the client connection. Calling TCPListener.Abort() doesn't drop the client connection which keeps the port blocked up.
For anyone in similar situation where you want to access you C# server from the local network ip address then you will need to listen to address 0.0.0.0 meaning listen on all ip addresses not a specific one like 127.0.0.1 etc.

Categories

Resources