I created a simple network game that uses TCP sockets in order to get the other player's data. I have two different classes, a server and a client. Everything was perfectly fine when I was only sending one message at a time like so:
Client:
public void ClientTester()
{
thread = new Thread(SendPosition);
thread.Start();
}
private void SendPosition()
{
while (true)
{
using (TcpClient client = new TcpClient())
{
client.Connect("127.0.0.1", 82);
using (NetworkStream n = client.GetStream())
{
BinaryWriter w = new BinaryWriter(n);
w.Write("Position:" + GameFiles.Player.Player.position.ToString());
w.Flush();
string msg = new BinaryReader(n).ReadString();
parseString(msg, "Position:", 9);
}
}
Thread.Sleep(60);
}
}
Server:
public void ServerTester()
{
thread = new Thread(TheadedMethod);
thread.Start();
}
private void TheadedMethod()
{
while (true)
{
TcpListener listener = new TcpListener(IPAddress.Any, 82);
listener.Start();
using (TcpClient c = listener.AcceptTcpClient())
using (NetworkStream n = c.GetStream())
{
parseString(msg, "Position:", 9);
BinaryWriter w = new BinaryWriter(n);
w.Write("Position:" + GameFiles.Player.Player.position.ToString());
w.Flush();
}
listener.Stop();
}
}
Here is the new code:
Client:
public void ClientTester()
{
thread = new Thread(SendPosition);
thread.Start();
SendMousePosition();
}
private void SendPosition()
{
while (true)
{
using (TcpClient client = new TcpClient())
{
client.Connect("127.0.0.1", 82);
using (NetworkStream n = client.GetStream())
{
BinaryWriter w = new BinaryWriter(n);
w.Write("Position:" + GameFiles.Player.Player.position.ToString());
w.Flush();
string msg = new BinaryReader(n).ReadString();
parseString(msg, "Position:", 9);
}
}
Thread.Sleep(60);
}
}
private void SendMousePosition()
{
using (TcpClient client = new TcpClient())
{
client.Connect("127.0.0.1", 82);
using (NetworkStream n = client.GetStream())
{
BinaryWriter w = new BinaryWriter(n);
w.Write("MousePosition:" + cursor.mousePosition());
w.Flush();
string msg = new BinaryReader(n).ReadString();
parseString(msg, "MousePosition:", 14);
}
}
}
Server:
public void ServerTester()
{
thread = new Thread(TheadedMethod);
thread.Start();
}
private void TheadedMethod()
{
while (true)
{
TcpListener listener = new TcpListener(IPAddress.Any, 82);
listener.Start();
using (TcpClient c = listener.AcceptTcpClient())
using (NetworkStream n = c.GetStream())
{
string msg = new BinaryReader(n).ReadString();
if (msg == "Position:")
{
parseString(msg, "Position:", 9);
BinaryWriter w = new BinaryWriter(n);
w.Write("Position:" + GameFiles.Player.Player.position.ToString());
w.Flush();
}
if (msg == "MousePosition:")
{
parseString(msg, "MousePosition:", 14);
BinaryWriter w = new BinaryWriter(n);
w.Write("MousePosition:" + cursor.mousePosition());
w.Flush();
}
}
listener.Stop();
}
}
When I try to send two messages in I receive an error:
Unable to read beyond the end of the stream.
on this line from the client's method SendPosition():
string msg = new BinaryReader(n).ReadString();
Why doesn't this work even though I have created a new instance of BinaryReader? Shouldn't the server automatically respond to each message sent to it?
You are doing two things wrong: The fist is that you create and re-create the connection all the time. Instead create the listener once, enter the loop and read messages. Setting up a new connection in TCP ads a lot of overhead, especially if you're just sending small messages. Same thing in the client, connect once, and then send when needed.
The second problem is that TCP is a streaming protocol, and that there is no message boundaries. That means that when you read from a TCP connection you can't know beforehand how much data you will read, you need to provide a way to separate messages yourself: You can add message boundaries, you can prepend each message with a header containing the message size, of you have have all messages being the same fixed size. Either way, you might have to read multiple times to get a complete message, or one read could give you more than one message.
Regarding the second problem, you can't of course attempt to read more than have been received, which is probably what the error message is telling you.
Related
I am trying to do a client-server application that communicate through sockets and their messages are serialez and deserialised using BinaryFormatter. My code is freezing and does absolutely nothing when reaching deserialize, and I don't understand why since I have no exception. I also can not step into with debugger, everything is freezing. This is my code:
public class Serializer
{
public static MemoryStream ToStream(object obj)
{
var stream = new MemoryStream();
var formatter = new BinaryFormatter();
formatter.Serialize(stream, obj);
return stream;
}
public static object FromStream(MemoryStream stream)
{
Console.WriteLine("Starting from stream");
var formatter = new BinaryFormatter();
stream.Seek(0, SeekOrigin.Begin);
object rez = formatter.Deserialize(stream); //NEVER GOES OVER THIS
Console.WriteLine("Starting deserialization" + rez);
return formatter.Deserialize(stream);
}
}
public class Connection
{
private Socket socket;
public Connection(Socket socket)
{
this.socket = socket;
Console.WriteLine($"Connected to client: {socket.RemoteEndPoint}");
Task.Factory.StartNew(() => Execute(socket));
}
private void Execute(Socket socket)
{
while (true)
{
var buffer = new byte[2048];
var bytesCount = socket.Receive(buffer);
if(bytesCount != 0)
{
var msgReceived = (Message)Serializer.FromStream(new MemoryStream(buffer, 0, buffer.Length));
Console.WriteLine($"Received msg: {msgReceived.Content}");
}
/* var msg = new Message { Content = "Hello World2!" };
Console.WriteLine($"Sending msg with content: {msg.Content}");
MemoryStream stream = Serializer.ToStream(msg);
var bytesSent = socket.Send(stream.GetBuffer());*/
Console.WriteLine("Trying again");
Thread.Sleep(500);
}
}
client code:
var host = Dns.GetHostEntry("localhost");
var ipAddress = host.AddressList.First();
var serverEndpoint = new IPEndPoint(ipAddress, 9000);
Socket serverSocket = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
serverSocket.Connect(serverEndpoint);
Console.WriteLine($"Successfully connected to server on: {serverSocket.RemoteEndPoint}");
while (true)
{
var msg = new Message { Content = "Hello World!" };
Console.WriteLine($"Sending msg with content: {msg.Content}");
MemoryStream stream = Serializer.ToStream(msg);
var bytesSent = serverSocket.Send(stream.GetBuffer());
Console.WriteLine("Waiting to receive");
var buffer = new byte[2048];
int bytesReceived = serverSocket.Receive(buffer);
if (bytesReceived != 0)
{
var receivedMessage = (Message)Serializer.FromStream(new MemoryStream(buffer));
Console.WriteLine($"Received message: {receivedMessage.Content}");
}
Console.WriteLine("Received done");
}
server code :
var host = Dns.GetHostEntry("localhost");
var ipAddress = host.AddressList.First();
var localEndPoint = new IPEndPoint(ipAddress, 9000);
var serverSocket = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
serverSocket.Bind(localEndPoint);
serverSocket.Listen(1);
while (true)
{
Console.WriteLine("Waiting for client");
Socket clientSocket = serverSocket.Accept();
var connection = new Connection(clientSocket);
}
I also checked the byteCount and they are arriving to the server, also the buffer is not empty, I don't understand why deserialize does nothing..
You call Deserialize twice, I think problem here:
object rez = formatter.Deserialize(stream); //NEVER GOES OVER THIS
Console.WriteLine("Starting deserialization" + rez);
return formatter.Deserialize(stream);
I think you can use methods like that:
private byte[] SerializeMessage(Message msg)
{
var formatter = new BinaryFormatter();
byte[] buf;
using (MemoryStream stream = new MemoryStream())
{
formatter.Serialize(stream, msg);
buf = new byte[stream.Length];
return stream.ToArray();
}
}
private Message DeserializeMessage(byte[] buff)
{
var formatter = new BinaryFormatter();
ConnectingMessage msg;
using (Stream stream = new MemoryStream(buff))
{
msg = formatter.Deserialize(stream) as Message;
}
return msg;
}
Also methods Send/Receive are synchronous, they block thread execution.
The description of the asynchronous option is here https://learn.microsoft.com/en-us/dotnet/framework/network-programming/using-an-asynchronous-client-socket
Only one piece of code can run at the same time. While that infinute loop while (true) runs, no other code will ever be able to run.
You will need to add some Multitasking to this project. The minimum amount, that is required for every word processor. However you also seem to be programming in console. And from my experience, console and Multitasking do not mix that well. You need to keep the programm alive, without blocking input.
My advice for learning Multitasking, is a BackgroundWorker in Windows Forms. The BGW is dated, usese Threads wich would be unessary here and should not be used in productive code. But it is perhaps the best "Training Wheels" for multitasking and -threading I know off.
I'm trying to communicate with a server.
This is my simple code:
System.Net.Sockets.TcpClient sock = new System.Net.Sockets.TcpClient();
string outputString = string.Empty;
sock.Connect("stack.overflow", 80);
NetworkStream ns = null;
StreamReader sr = null;
StreamWriter sw = null;
ns = sock.GetStream();
sr = new StreamReader(ns);
sw = new StreamWriter(ns) { NewLine = "\r\n", AutoFlush = true };
ns.ReadTimeout = 10;
sw.WriteLine("send data to server");
sw.Flush();
outputString = sr.ReadLine();
while (!string.IsNullOrEmpty(outputString))
{
System.Diagnostics.Debug.WriteLine(outputString);
outputString = sr.ReadLine();
}
(I know my code has room for improvement. It kept it simple for my test)
Everything works fine beside the ReadLine().
It doesn't do what I'm expecting it to do.
It reads all lines from the server and after the last line it just endlessly waits for a new line. At least for me it seems like it is waiting.
I thought it would stop after the last line and return null or something else.
Can someone explain me what the best practice is to get all lines from the server and move on?
I solved it like this:
public class ServerHandler
{
private BackgroundWorker serverWorker = new BackgroundWorker();
public event EventHandler ServerResponse;
private TcpClient socket;
private NetworkStream networkStream;
private StreamReader streamReader;
private StreamWriter streamWriter;
public ServerHandler()
{
System.Net.Sockets.TcpClient sock = new System.Net.Sockets.TcpClient();
serverWorker.WorkerSupportsCancellation = true;
serverWorker.DoWork += new DoWorkEventHandler(serverWorker_DoWork);
}
public void ConnectToServer()
{
socket = new TcpClient();
socket.Connect("stack.overflow", 80);
networkStream = socket.GetStream();
streamReader = new StreamReader(networkStream);
streamWriter = new StreamWriter(networkStream) { NewLine = "\r\n", AutoFlush = true };
SendMessage("some data");
}
public bool IsConnected
{
get { return socket.Connected; }
}
public void StartListening()
{
if (ServerResponse == null)
throw new Exception("There is no event to intercept the response!");
serverWorker.RunWorkerAsync();
}
private void SendMessage(string message)
{
streamWriter.WriteLine(message);
streamWriter.Flush();
}
private void serverWorker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
while (true)
{
string serverResponse = streamReader.ReadLine();
ServerResponse(serverResponse, new EventArgs());
}
}
}
Like willaien already said, the stream doesn't know when it ends. As long as you are communicating with the server you can wait for a response.
I put the code that listens to the server in a background worker, that always listens and that sends an event, when a new response appears.
I am trying to program an ident sever to deal with the identity protocol requests from an irc server that I am programming an irc client for. The problem is I try to print to the screen the what I receive but nothing prints. I am not getting an error code.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;
namespace ConnectIRC
{
class IdentityClass
{
private const int bufSize = 32;
public void IdentityRequest() {
TcpListener listener = null;
int port = 113;
IPEndPoint hostInfo = new IPEndPoint(IPAddress.Any, 113);
listener = new TcpListener(hostInfo);
listener.Start();
byte[] rcvBuffer = new byte[bufSize];
int rec;
for (; ; )
{
TcpClient client = null;
NetworkStream netStream = null;
client = listener.AcceptTcpClient();
if (listener.Pending())
{
Console.WriteLine("Connection was made");
}
netStream = client.GetStream();
//byte[] rcvBuffer = new byte[bufSize];
rec = netStream.Read(rcvBuffer, 0, rcvBuffer.Length);
Array.Resize(ref rcvBuffer, rec);
Console.WriteLine(Encoding.ASCII.GetString(rcvBuffer));
netStream.Close();
client.Close();
}
}
}
}
This is a very basic implementation of an ident server
Obviously it only accepts one connection and closes
Note you'll need to have a port mapped through your router for this to work
public class Ident
{
private readonly TcpListener _listener;
private readonly string _userId;
public Ident(string userId)
{
_userId = userId;
_listener = new TcpListener(IPAddress.Any, 113);
}
public void Start()
{
Console.WriteLine("Ident started");
_listener.Start();
var client = _listener.AcceptTcpClient();
_listener.Stop();
Console.WriteLine("Ident got a connection");
using (var s = client.GetStream())
{
var reader = new StreamReader(s);
var str = reader.ReadLine();
var writer = new StreamWriter(s);
Console.WriteLine("Ident got: " + str + ", sending reply");
writer.WriteLine(str + " : USERID : UNIX : " + _userId);
writer.Flush();
Console.WriteLine("Ident sent reply");
}
Console.WriteLine("Ident server exiting");
}
}
Here is my server:
class Server
{
static void Main(string[] args)
{
TcpListener listener = new TcpListener(IPAddress.Any, 5004);
listener.Start();
TcpClient client;
while (true)
{
client = listener.AcceptTcpClient();
if (client.Connected)
{
Console.WriteLine("client connected");
break;
}
}
NetworkStream sr = client.GetStream();
while (true)
{
byte[] message = new byte[1024];
sr.Read(message, 0, message.Length);
sr.Close();
Console.WriteLine(message.ToString());
}
}
}
and here is my client:
class Program
{
static void Main(string[] args)
{
TcpClient client = new TcpClient();
Console.WriteLine("Insert server address");
string server = Console.ReadLine();
if (server == "")
{
server = "192.168.1.2";
}
client.Connect(IPAddress.Parse(server), 5004);
NetworkStream sw = client.GetStream();
while (true)
{
byte[] message = Encoding.ASCII.GetBytes(Console.ReadLine());
sw.Write(message, 0, message.Length);
sw.Close();
}
}
}
When I run my server and the client first everything works and a "client connected" message appears. The problem is when I am trying to send a message from client to the server, a System.ObjectDisposedException is raised at the server in the sr.Read(message, 0, message.Length); line. Any idea how can I solved it or what is the cause?
You are closing the stream in the while loop, for both client and server. Move the Close below outside of the loop, or better still, make use of fact that Close calls Dispose, which allows you to use the IDisposable interface features like using:
using (NetworkStream sw = client.GetStream())
{
while (true)
{
byte[] message = Encoding.ASCII.GetBytes(Console.ReadLine());
sw.Write(message, 0, message.Length);
// Todo : Implement some kind of termination to the loop
}
sw.Flush();
}
static void Main(string[] args)
{
Console.Title = "Socket Server";
Console.WriteLine("Listening for client messages");
Socket serverSocket = new Socket(AddressFamily.InterNetwork,
SocketType.Stream,
ProtocolType.Tcp);
IPAddress serverIp = IPAddress.Any;
IPEndPoint serverEP = new IPEndPoint(serverIp, 8000);
SocketPermission socketPermission = new SocketPermission(NetworkAccess.Accept,
TransportType.Tcp,
"127.0.0.1", 8000);
serverSocket.Bind(serverEP);
serverSocket.Listen(2);
while(true)
{
//Socket connection = serverSocket.Accept();
connection = serverSocket.Accept();
Thread clientThread = new Thread(new ParameterizedThreadStart(MultiUser));
clientThread.Start(connection);
}
}
public static void MultiUser(object connection)
{
byte[] serverBuffer = new byte[10025];
string message = string.Empty;
int bytes = ((Socket)connection).Receive(serverBuffer, serverBuffer.Length, 0);
message += Encoding.ASCII.GetString(serverBuffer, 0, bytes);
Console.WriteLine(message);
TcpClient client = new TcpClient();
client.Client = ((Socket)connection);
IntPtr handle = client.Client.Handle;
}
I want to write a chat program which has one server and 2 clients. The problem is that, I can not direct the message sent from the client1 to client2 via the server. How can the server distinguish threads so that it can send the received message from client1 to client2?
Each client has their own handle. You can access this via the Handle property. For example:
TcpClient client = tcpListener.AcceptTcpClient();
IntPtr handle = client.Client.Handle; //returns a handle to the connection
Then all you need to do is store this in a hashtable, and iterate through it, looking for available data. When you detect data on the wire for one of the connections, then save it and retransmit it to the other clients in the table.
Remember to make sure that you make this multithreaded so a listen request on one client does not block any send or receive functions on other clients!
I've added some code here you should be able to work with (tested it out on my system)
private void HandleClients(object newClient)
{
//check to see if we are adding a new client, or just iterating through existing clients
if (newClient != null)
{
TcpClient newTcpClient = (TcpClient)newClient;
//add this client to our list
clientList.Add(newTcpClient.Client.Handle, newTcpClient);
Console.WriteLine("Adding handle: " + newTcpClient.Client.Handle); //for debugging
}
//iterate through existing clients to see if there is any data on the wire
foreach (TcpClient tc in clientList.Values)
{
if (tc.Available > 0)
{
int dataSize = tc.Available;
Console.WriteLine("Received data from: " + tc.Client.Handle); //for debugging
string text = GetNetworkString(tc.GetStream());
//and transmit it to everyone else
foreach (TcpClient otherClient in clientList.Values)
{
if (tc.Client.Handle != otherClient.Client.Handle)
{
Send(otherClient.GetStream(), text);
}
}
}
}
}
public void Send(NetworkStream ns, string data)
{
try
{
byte[] bdata = GetBytes(data, Encoding.ASCII);
ns.Write(bdata, 0, bdata.Length);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
}
protected string GetNetworkString(NetworkStream ns)
{
if (ns.CanRead)
{
string receivedString;
byte[] b = GetNetworkData(ns);
receivedString = System.Text.Encoding.UTF8.GetString(b);
log.Info("Received string: " + receivedString);
return receivedString;
}
else
return null;
}
protected byte[] GetNetworkData(NetworkStream ns)
{
if (ns.CanRead)
{
log.Debug("Data detected on wire...");
byte[] b;
byte[] myReadBuffer = new byte[1024];
MemoryStream ms = new MemoryStream();
int numberOfBytesRead = 0;
// Incoming message may be larger than the buffer size.
do
{
numberOfBytesRead = ns.Read(myReadBuffer, 0, myReadBuffer.Length);
ms.Write(myReadBuffer, 0, numberOfBytesRead);
}
while (ns.DataAvailable);
//and get the full message
b = new byte[(int)ms.Length];
ms.Seek(0, SeekOrigin.Begin);
ms.Read(b, 0, (int)ms.Length);
ms.Close();
return b;
}
else
return null;
}
You will want to call HandleClients from a main thread that checks to see if there are any pending requests or not, and runs on a loop.