Just testing a simple example program, for sending an XML Serialized object over TCP. I'm finding that unless I close the stream or TcpClient on the side that sends a serialized object, the receiver never receives it.
class Program
{
private static XmlSerializer positionSerializer = new XmlSerializer(typeof(GazePosition));
static void Main(string[] args)
{
new Thread(Server).Start();
new Thread(Client).Start();
Console.ReadLine();
}
static void Server()
{
TcpListener listener = new TcpListener(IPAddress.Loopback, 30000);
listener.Start();
TcpClient client = listener.AcceptTcpClient();
Stream stream = client.GetStream();
StreamReader reader = new StreamReader(stream);
var cmd = reader.ReadLine();
if (cmd == "GetPosition")
{
Console.WriteLine("Received GetPosition Command");
GazePosition pos = new GazePosition(-5, 5);
positionSerializer.Serialize(stream, pos);
}
//client.Close();
listener.Stop();
}
static void Client()
{
TcpClient client = new TcpClient();
client.Connect(IPAddress.Loopback, 30000);
Stream stream = client.GetStream();
StreamWriter writer = new StreamWriter(stream);
writer.WriteLine("GetPosition");
writer.Flush();
var pos = (GazePosition)positionSerializer.Deserialize(stream);
Console.WriteLine("Received: {0}", pos);
stream.Close();
client.Close();
}
}
[Serializable]
[XmlRoot("GazePosition")]
public class GazePosition
{
[XmlElement("X")]
public float X;
[XmlElement("Y")]
public float Y;
public GazePosition()
{
}
public GazePosition(float x, float y)
{
X = x;
Y = y;
}
public override string ToString()
{
return string.Format("{0},{1}", X, Y);
}
}
This prints out only:
Received GetPosition command
However, if I uncomment client.Close()
This prints out:
Received GetPosition command
Received -5, 5
I would like this example be able to scale to receiving multiple commands, but this is painful while I'm forced to close the stream each time I receive a command. If someone could enlighten me why this is happening, I would be very thankful!
Here what you are missing is that you are supposed to read from the TcpClient's stream on the client side. Please have a look what I simply did to get it to work. Please keep in mind that this is simply an example.
writer.WriteLine("GetPosition");
writer.Flush();
//Just like you did you should seperate the payload
//Either by size or new line or some special key you set
byte[] buffer = new byte[1024];
stream.Read(buffer, 0, buffer.Length);
using (var ms = new MemoryStream(buffer))
{
var gazePos = (GazePosition)positionSerializer.Deserialize(ms);
//DOSTUFF
}
//Added code finished
Here is some simple beta level library. You may want to have a look→
Simple Server
Edit: I further looked at NetworkStream's Dispose method. It has native calls to Windows API so I am not able to further into implementation details.However, as you can see here, during a graceful shutdown, which occurs when you dispose a network stream, sends a FD_CLOSE
Related
Currently I'm trying to develop a multiplayer game with Unity. I checked the Unity's built-in Network and NetworkManager but I need the server dedicated. Hence I developed a java server and implements the protolbuf which is provided by Google. It works with my cocos project but not with this Unity one.
Two problems, first, no matter what did I send to server, the server side has a InvalidProtolBufferException :
While parsing a protocol message, the input ended unexpectedly in the middle > of a field. This could mean either than the input has been truncated or that > embedded message misreported its own length.
second, the stream.read method always makes my unity not responding. Here is my client-side code:
public class SocketClient : MonoBehaviour {
public Text send;
public Text read;
const string ipAddress = "192.168.0.233";
const int port = 8080;
TcpClient socket;
NetworkStream stream;
BinaryWriter bw;
BinaryReader br;
// Use this for initialization
void Start () {
SetupSocket();
}
void SetupSocket() {
socket = new TcpClient(ipAddress, port);
stream = socket.GetStream();
bw = new BinaryWriter(socket.GetStream());
br = new BinaryReader(socket.GetStream());
}
// Update is called once per frame
void Update () {
ReadMessage();
}
public void SendMessage() {
//NetworkStream stream = new NetworkStream(socket.Client);
MessagesProto msg = new MessagesProto();
msg.id = int.Parse(send.text);
using (MemoryStream ms = new MemoryStream())
{
Serializer.Serialize<MessagesProto>(ms, msg);
ms.Position = 0;
byte[] data = ms.ToArray();
ms.Position = 0;
//foreach (byte d in data) {
// Debug.Log(d);
//}
stream.Write(data, 0 , data.Length);
}
}
public void ReadMessage() {
if (stream.CanRead) {
//byte[] receiveData = new byte[socket.ReceiveBufferSize];
byte[] receiveData = new byte[4];
socket.GetStream().Read(receiveData, 0, 4);
Debug.Log("Loading...");
using (MemoryStream ms = new MemoryStream()) {
ms.Write(receiveData, 0, receiveData.Length);
ms.Position = 0;
var msg = Serializer.Deserialize<MessagesProto>(ms);
read.text = msg.data + "";
}
}
}
}
I tried to set the stream.CanRead to stream.DataAvailable, no more crash but not reading anything either, these .Net sockets problems drive me crazy, anyone can help me with this please?
It is not responding because your socket code is synchronous(blocking). You can solve this by either using asynchronous version of the socket functions or use Thread to do all your receiving stuff. This has been answered many times and I will just link to the answers.
TCP Server in Unity
Just port the code to Client.
UDP.
I have made a file receiving server in c# using socket programming. I have made a GUI . There is a button named 'connect' which will start the server on clicking it and there is a text box which will show a message when the server starts. But when I click on the button, the GUI freezes.
Here is my sample code:
using System;
using System.Drawing;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Collections.Generic;
class sampleserver : Form
{
private TextBox newText;
public TcpClient tcpClient;
public TcpListener tcpListener;
public sampleserver()
{
Text = " TCP Server";
Size = new Size(400, 380);
newText = new TextBox();
newText.Parent = this;
newText.Size = new Size(200, 2 * Font.Height);
newText.Location = new Point(10, 55);
Button connect = new Button();
connect.Parent = this;
connect.Text = "Connect";
connect.Location = new Point(295, 20);
connect.Size = new Size(6 * Font.Height, 2 * Font.Height);
connect.Click += new EventHandler(ButtonConnectOnClick);
}
void ButtonConnectOnClick(object obj, EventArgs ea)
{
tcpListener = new TcpListener(IPAddress.Any, 1234);
tcpListener.Start();
newText.Text = "Server started"; //**This line is not working**
//Infinite loop to connect to new clients
while (true)
{
// Accept a TcpClient
TcpClient tcpClient = tcpListener.AcceptTcpClient();
string address = ((IPEndPoint)tcpClient.Client.RemoteEndPoint).Address.ToString();
byte[] data = new byte[1024];
NetworkStream ns = tcpClient.GetStream();
int recv = ns.Read(data, 0, data.Length);
StreamReader reader = new StreamReader(tcpClient.GetStream());
// The first message from the client is the file size
string cmdFileSize = reader.ReadLine();
int length = Convert.ToInt32(cmdFileSize);
byte[] buffer = new byte[length];
int received = 0;
int read = 0;
int size = 1024;
int remaining = 0;
// Read bytes from the client using the length sent from the client
while (received < length)
{
remaining = length - received;
if (remaining < size)
{
size = remaining;
}
read = tcpClient.GetStream().Read(buffer, received, size);
received += read;
}
}
}
public static void Main()
{
Application.Run(new sampleserver());
}
}
which change will I need to make to run this properly?
I would suggest you to use Asynchronous sockets but you can also make only that button click method Asynchronous , simply like this.
async void ButtonConnectOnClick(object obj, EventArgs ea)
{
tcpListener = new TcpListener(IPAddress.Any, 1234);
tcpListener.Start();
newText.Text = "Server started"; //**This line is not working**
await Task.Run(() =>
{
//Infinite loop to connect to new clients
while (true)
{
// Accept a TcpClient
TcpClient tcpClient = tcpListener.AcceptTcpClient();
string address = ((IPEndPoint)tcpClient.Client.RemoteEndPoint).Address.ToString();
byte[] data = new byte[1024];
NetworkStream ns = tcpClient.GetStream();
int recv = ns.Read(data, 0, data.Length);
StreamReader reader = new StreamReader(tcpClient.GetStream());
// The first message from the client is the file size
string cmdFileSize = reader.ReadLine();
int length = Convert.ToInt32(cmdFileSize);
byte[] buffer = new byte[length];
int received = 0;
int read = 0;
int size = 1024;
int remaining = 0;
// Read bytes from the client using the length sent from the client
while (received < length)
{
remaining = length - received;
if (remaining < size)
{
size = remaining;
}
read = tcpClient.GetStream().Read(buffer, received, size);
received += read;
}
}
});
}
This would make the entire method asynchronous , and now you can read from the socket without freezing your main UI.
Goodluck.
UI events usually are meant to run and then return. Your program is launching code that should be started in a thread -- by the button code -- and then properly stopped when the application exits.
As Ron Beyer stated, you have a while(true), but I was more interested in whether the read times out or not and how you return to the main UI thread after the button is pushed. It looks like you never return to the main UI thread. As my comment stated, I'd test the network code, including the while loop in a console application, and then add on the complexities of a UI.
By the way, as other comments have pointed out, you cannot allow for a second click of your launch button, when you set up this code so the network setup (launch) succeeds. You'll have to disable (grey-out) the button, so it cannot be double-clicked, and only enabled if the network start (launch) failed. A second button, not greyed-out would allow you to shut everything down.
Threads and UIs are not the simplicity some would have you believe. They take quite a bit of work.
I'm trying to pass a custom class from one tcp client to another (using TcpListener, TcpClient and XmlSerializer).
I got 1 problem that breaks into 2 problems.
when i'm using this code, the client hangs while Serilization is being done
(i think the other side doesnt know when the data is fully sent):
public void SendData(myMessage messageClass)
{
NetworkStream serverStream = tcpServer.GetStream();
XmlSerializer xmlSrlz = new XmlSerializer(typeof(myMessage));
xmlSrlz.Serialize(serverStream, messageClass);
serverStream.Flush();
}
public myMessage ReadData()
{
while (true)
{
NetworkStream clientStream = client.GetStream();
XmlSerializer xmlSrlz = new XmlSerializer(typeof(myMessage));
int bytesToread;
byte[] inStream = new byte[10025];
myMessage msg;
do
{
msg = (myMessage)xmlSrlz.Deserialize(clientStream);
clientStream.Flush();
}
while (((bytesToread = clientStream.Read(inStream, 0, (int)client.ReceiveBufferSize)) > 0));
return msg;
}
}
So, when i searched around i noticed i can dispose the stream (or use using) and that way let the other side know that the Data has fully sent and theres no more Data to send:
public void SendData(myMessage messageClass)
{
using (NetworkStream serverStream = tcpServer.GetStream())
{
XmlSerializer xmlSrlz = new XmlSerializer(typeof(myMessage));
xmlSrlz.Serialize(serverStream, messageClass);
serverStream.Flush();
}
}
public myMessage ReadData()
{
while (true)
{
using (NetworkStream clientStream = client.GetStream())
{
XmlSerializer xmlSrlz = new XmlSerializer(typeof(myMessage));
int bytesToread;
byte[] inStream = new byte[10025];
myMessage msg;
do
{
msg = (myMessage)xmlSrlz.Deserialize(clientStream);
clientStream.Flush();
}
while (((bytesToread = clientStream.Read(inStream, 0, (int)client.ReceiveBufferSize)) > 0));
return msg;
}
}
}
But now i lost the connection of them 2 clients.
when i'm trying to send another Message i get the exception:
The operation is not allowed on non-connected sockets.
So how can i send the Serilized object without breaking the connection?
First off, using a using like that is closing your socket by disposing the underlying stream. Bad idea.
Second, consider serializing to and from a string, instead of serializing directly into the stream. I'm not sure if there are side-effects of doing that, but there certainly could be.
You can use this question to serialize/deserialize from string: Deserialize from string instead TextReader
I am using utility in c# that helps me connect via telnet on a remote unix server ( http://www.codeproject.com/KB/IP/MinimalisticTelnet/MinimalisticTelnet.zip ) This utility, uses a function called Write to write in the shell.
TcpClient tcpSocket;
public void Write(string cmd)
{
if (!tcpSocket.Connected)
return;
byte[] buf = System.Text.ASCIIEncoding.ASCII.GetBytes(cmd.Replace("\0xFF","\0xFF\0xFF"));
tcpSocket.GetStream().Write(buf, 0, buf.Length);
}
An other function is called to read the output :
public string Read()
{
if (!tcpSocket.Connected)
return null;
StringBuilder sb=new StringBuilder();
do
{
ParseTelnet(sb);
System.Threading.Thread.Sleep(TimeOutMs);
} while (tcpSocket.Available > 0);
return sb.ToString();
}
This function does return the output of the command, but since the commands can last very long time, it cannot return all the output that I need since it quits before the execution finishes. I can't do Thread.Sleep(time) because the execution time is not constant for all the commands.
Is there any way to force the code to read the data till the end with TcpClient ? Thank you!
The solution was this :
public string WriteCommandOverTcp(string cmd)
{
string response = "";
using (NetworkStream stream = tcpSocket.GetStream())
{
using (StreamWriter writer = new StreamWriter(stream))
{
writer.AutoFlush = true;
using (StreamReader reader = new StreamReader(stream))
{
writer.WriteLine(cmd);
// If I comment the line below, the server receives the first message
// otherwise it keeps waiting for data
response = reader.ReadLine();
}
}
}
return response;
}
I stumbled on the solution when I heard about the NetworkStream API and it was the solution to read a stream till the end from the Network.
I have two threads. One threads reads requests and passes it to server using message queue and other thread reads the response from message queue and sends it back. In the same process the caller class method writes the request on the pipe (using the server pipe stream shared by first thread) and then reads the response using the client pipe stream shared by the second thread. This can be easily done using Java PipeInputStream and PipeOutputStream as follows. Essentially I am looking for equivalent of following Java logic in C#. I tried unsuccessfully using anonymous pipes in C#.
RequestHandlerThread (Thread1 as mentioned above)
out = new PipedOutputStream();
readPipeIs = new PipedInputStream(out);
readDataIs = new DataInputStream(readPipeIs);
// read data from readDataIs
// Send it to server over message queue
// Share 'out' so that other class method can write to it.
Response Handler (Thread 2 as mentioned above)
in = new PipedInputStream();
writePipeOs = new PipedOutputStream(in);
writeDataOs = new DataOutputStream(writePipeOs);
// Wait and read from message queue
// write received data to 'writeDataOs'
// Share 'in' so that other class method can read from it.
I am not sure if C# pipes are restricted for communicating between two processes. All the above logic is in same process just that there are two threads to communicate with message server.
I tried pair of AnonymousPipeServerStream and AnonymousPipeClientStream pair in both threads. I shared the server stream for writing and client stream for reading by other class method.
Any obvious flaw in above logic or any suggestions with choice of IPC ?
Adding source code
Here is the Test class
class Test
{
private static byte[] ret;
private static bool ready;
Stream outStream;
Stream inStream;
private void clientConnReqHandler()
{
AnonymousPipeServerStream pipeServer = new
AnonymousPipeServerStream(PipeDirection.Out);
outStream = pipeServer;
string pipeHandle =
pipeServer.GetClientHandleAsString();
AnonymousPipeClientStream pipeClient =
new AnonymousPipeClientStream(PipeDirection.In,
pipeHandle);
pipeServer.DisposeLocalCopyOfClientHandle();
ready = false;
BinaryReader binReader = new BinaryReader(pipeClient);
int mesgSize = binReader.ReadInt32();
System.Console.WriteLine("Message Lenght To read: " +
mesgSize);
byte[] buffer = binReader.ReadBytes(mesgSize);
System.Console.WriteLine("Message read: " +
buffer.ToString());
// Simulate some processing
Thread.Sleep(5000);
mesgProcessing(buffer);
}
private static void mesgProcessing(byte[] buffer)
{
System.Text.UTF8Encoding encoding = new
System.Text.UTF8Encoding();
byte[] extra = encoding.GetBytes("Echo : ");
ret = new byte[buffer.Length + extra.Length];
System.Buffer.BlockCopy(extra, 0, ret, 0, extra.Length);
System.Buffer.BlockCopy(buffer, 0, ret, extra.Length,
buffer.Length);
ready = true;
}
private void clientConnRespHandler()
{
AnonymousPipeServerStream pipeServer = new
AnonymousPipeServerStream(PipeDirection.Out);
string pipeHandle =
pipeServer.GetClientHandleAsString();
AnonymousPipeClientStream pipeClient =
new AnonymousPipeClientStream(PipeDirection.In,
pipeHandle);
inStream = pipeClient;
pipeServer.DisposeLocalCopyOfClientHandle();
while (ready)
{
BinaryWriter binWriter = new
BinaryWriter(pipeServer);
binWriter.Write(ret.Length);
binWriter.Write(ret);
ready = false;
}
}
public static void Main()
{
Test setup = new Test();
setup.threadTest();
Test2 threadTest = new Test2();
// This method will do actuall read and write.
threadTest.runTest(setup.inStream, setup.outStream);
}
public void threadTest()
{
Thread reqHandlerThread = new Thread(new
ThreadStart(clientConnReqHandler));
Thread respHandlerThread = new Thread(new
ThreadStart(clientConnRespHandler));
reqHandlerThread.Start();
respHandlerThread.Start();
}
}
The class that does read/write:
class Test2
{
internal void runTest(System.IO.Stream inStream,
System.IO.Stream outStream)
{
BinaryWriter writer = new BinaryWriter(outStream);
System.Text.UTF8Encoding encoding = new
System.Text.UTF8Encoding();
byte[] mesg = encoding.GetBytes("Hello World!!!");
writer.Write(mesg.Length);
writer.Write(mesg);
BinaryReader reader = new BinaryReader(inStream);
int mesgSize = reader.ReadInt32();
System.Console.WriteLine("Message Lenght To read: " +
mesgSize);
byte[] buffer = reader.ReadBytes(mesgSize);
System.Console.WriteLine("Message read: " +
buffer.ToString());
}
}
thanks
OK. It worked after getting rid of DisposeLocalCopyOfClientHandle(). Of course had to fix some new-by mistakes of while loop condition to check if the data is ready and printing the string correctly from byte array.