I'm working on a rough TCP Server/Client. It works like this: Client sends message to server > Server sends all user data to each client. I have this in a loop as I'm going to use this transfer of data for multiplayer in a game. However, for some reason my server will combine incoming data into one string instead of reading it line by line. For example, it should read something like john:0|0 for user data, but instead it reads like john:0|0john:0|0john:0|0john:0|0 I've tried setting a delay on sending and receiving, but it only seems to work if I delay more than 100ms.
Server
class Program
{
private static Socket _serverSocket;
private static readonly List<Socket> _clientSockets = new List<Socket>();
private const int _BUFFER_SIZE = 2048;
private const int _PORT = 100;
private static readonly byte[] _buffer = new byte[_BUFFER_SIZE];
public static bool clientConnected = false;
public static Socket current;
public static int delay = 100; //65
public static string username = "";
public static int dataSent = 0;
public static int dataReceived = 0;
public static List<Player> players = new List<Player>();
static void Main()
{
Console.Title = "Server";
SetupServer();
Console.ReadLine(); // When we press enter close everything
CloseAllSockets();
}
private static void SetupServer()
{
Console.WriteLine("Setting up server...");
_serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_serverSocket.Bind(new IPEndPoint(IPAddress.Any, _PORT));
_serverSocket.Listen(5);
_serverSocket.BeginAccept(AcceptCallback, null);
Console.WriteLine("Server setup complete");
Console.WriteLine("Listening on port: " + _PORT);
}
private static void CloseAllSockets()
{
foreach (Socket socket in _clientSockets)
{
socket.Shutdown(SocketShutdown.Both);
socket.Close();
}
_serverSocket.Close();
}
private static void AcceptCallback(IAsyncResult AR)
{
Socket socket;
try
{
socket = _serverSocket.EndAccept(AR);
}
catch (ObjectDisposedException) // I cannot seem to avoid this (on exit when properly closing sockets)
{
return;
}
_clientSockets.Add(socket);
socket.BeginReceive(_buffer, 0, _BUFFER_SIZE, SocketFlags.None, ReceiveCallback, socket);
Console.WriteLine("Client connected: " + socket.RemoteEndPoint);
_serverSocket.BeginAccept(AcceptCallback, null);
clientConnected = true;
}
private static void ReceiveCallback(IAsyncResult AR)
{
current = (Socket)AR.AsyncState;
int received = 0;
try
{
received = current.EndReceive(AR);
}
catch (SocketException)
{
Console.WriteLine("Client forcefully disconnected");
current.Close(); // Dont shutdown because the socket may be disposed and its disconnected anyway
_clientSockets.Remove(current);
return;
}
byte[] recBuf = new byte[received];
Array.Copy(_buffer, recBuf, received);
string text = Encoding.ASCII.GetString(recBuf);
if (text.Contains("newuser:"))
{
string newuser = text.Replace("newuser:", "");
Player newPlayer = new Player(newuser, current.RemoteEndPoint.ToString());
players.Add(newPlayer);
SendString("newuser:" + newuser);
Console.WriteLine(newuser + " has joined the game.");
}
else
{
Console.WriteLine(text); //This is where the client text gets mashed together
}
current.BeginReceive(_buffer, 0, _BUFFER_SIZE, SocketFlags.None, ReceiveCallback, current);
}
public static void SendString(string message)
{
try
{
byte[] data = Encoding.ASCII.GetBytes(message.ToString());
current.Send(data);
//current.BeginReceive(_buffer, 0, _BUFFER_SIZE, SocketFlags.None, ReceiveCallback, current);
dataSent++;
}
catch (Exception ex)
{
Console.WriteLine("Client disconnected!" + ex.Message);
}
Console.WriteLine(dataSent);
}
}
Client.cs
public static class Client
{
public static string Username = "null";
public static int delay = 85;
public static bool hasLoggedin = false;
public static int dataSent = 0;
private static readonly Socket _clientSocket = new Socket
(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
private const int port = 100;
public static void Init()
{
Console.Title = "Client";
ConnectToServer();
Exit();
}
private static void ConnectToServer()
{
int attempts = 0;
while (!_clientSocket.Connected)
{
try
{
attempts++;
Console.Write("Username: ");
//Username = Console.ReadLine();
Username = "Matt";
LocalPlayer.username = Username;
Console.Write("IP: ");
string ip = "192.168.0.2";
Console.WriteLine("Port[default]: " + port);
Console.WriteLine("Connection attempt to " + ip + ": " + attempts + " attempts");
IPAddress ipAd = IPAddress.Parse(ip);
_clientSocket.Connect(ipAd, port);
}
catch (SocketException e)
{
Console.Clear();
Console.WriteLine(e.Message);
Console.ReadLine();
}
}
Console.WriteLine("Connected!");
SendLoginPacket();
}
public static void SendLoginPacket()
{
if (hasLoggedin == false)
{
SendString("newuser:" + Username);
Thread.Sleep(delay);
hasLoggedin = true;
}
RequestLoop();
}
private static void RequestLoop()
{
while (true)
{
//SendData();
ReceiveResponse();
}
}
private static void Exit()
{
_clientSocket.Shutdown(SocketShutdown.Both);
_clientSocket.Close();
Environment.Exit(0);
}
public static void SendData()
{
string data = LocalPlayer.username + ":" + LocalPlayer.velocity.X + "|" + LocalPlayer.velocity.Y;
SendString(data);
}
private static void SendString(string text)
{
byte[] buffer = Encoding.ASCII.GetBytes(text);
Console.WriteLine("Sent: " + text);
_clientSocket.Send(buffer, 0, buffer.Length, SocketFlags.None);
dataSent++;
Thread.Sleep(delay);
Console.WriteLine(dataSent);
}
private static void ReceiveResponse()
{
var buffer = new byte[2048];
int received = _clientSocket.Receive(buffer, SocketFlags.None);
if (received == 0) return;
var data = new byte[received];
Array.Copy(buffer, data, received);
string text = Encoding.ASCII.GetString(data);
if (text.Contains("newuser:"))
{
string str = text.Replace("newuser:", "");
if (!(str == LocalPlayer.username))
{
Player player = new Player(str, Globals.Content);
Globals.players.Add(player);
Console.WriteLine(str + " has joined the game.");
}
}
Console.WriteLine("Clients connected: " + Globals.players.Count());
}
}
How can I change the server so it reads incoming data one at a time, instead of mashing it into one big string?
EDIT: Note the LocalPlayer class is sending it's movement position using the Client class.