C#TCP服务器组合接收的数据

我正在研究一个粗略的TCP服务器/客户端。 它的工作方式如下:客户端向服务器发送消息>服务器将所有用户数据发送到每个客户端。 我有一个循环,因为我将在游戏中使用这种多人游戏数据传输。 但是,出于某种原因,我的服务器会将传入的数据组合成一个字符串,而不是逐行读取。 例如,对于用户数据,它应该读取类似john:0 | 0的内容,但它读起来像john:0 | 0john:0 | 0john:0 | 0john:0 | 0我尝试设置发送和接收延迟,但是如果我延迟超过100毫秒,它似乎只能起作用。

服务器

class Program { private static Socket _serverSocket; private static readonly List _clientSockets = new List(); 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 players = new List(); 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()); } } 

如何更改服务器以便一次读取一个传入数据,而不是将其混合成一个大字符串?

编辑:注意LocalPlayer类使用Client类发送它的移动位置。

这是因为TCP / IP协议提供数据流(字节)。 如果字符串未明确分隔,则由于“流式传输”而将它们“连接”。

在接收时,使用“分隔符”发送和“拆分”消息时,需要使用“分隔符”“加入”消息。 可以考虑使用以下替代方案之一来实现“分隔符”概念:

  • 介绍“消息结束”标记。
  • 引入包含消息长度的消息头。

小文章, TCP / IP客户端 – 服务器应用程序:与字符串消息交换 ,可能有助于理解上述替代方案。

更新

我想建议适当地实施“消息”。

虽然似乎没有理由比较源代码,因为它的主要缺陷(缺少“消息传递”机制),我用来测试客户端和服务器的源代码是附加的。

客户

 using System; using System.Net; using System.Net.Sockets; using System.Text; internal sealed class Program { private const int Port = 100; private readonly Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); private bool hasLoggedin; private int dataSent; public static void Main() { var client = new Program(); client.ConnectToServer(); client.Exit(); } private void ConnectToServer() { int attempts = 0; while (!clientSocket.Connected) { try { attempts++; string ip = "127.0.0.1"; Console.WriteLine("Port[default]: " + Port); Console.WriteLine("Connection attempt to " + ip + ": " + attempts + " attempts"); var address = IPAddress.Parse(ip); clientSocket.Connect(address, Port); } catch (SocketException e) { Console.Clear(); Console.WriteLine(e.Message); Console.ReadLine(); } } Console.WriteLine("Connected!"); SendLoginPacket(); } private void SendLoginPacket() { if (hasLoggedin == false) { SendString("newuser:" + Guid.NewGuid()); hasLoggedin = true; } RequestLoop(); } private void RequestLoop() { while (true) { SendData(); ReceiveResponse(); } } private void Exit() { clientSocket.Shutdown(SocketShutdown.Both); clientSocket.Close(); } private void SendData() { const string Data = "username:100|200"; SendString(Data); } private void SendString(string text) { byte[] buffer = Encoding.ASCII.GetBytes(text); Console.WriteLine("Sent: " + text); clientSocket.Send(buffer, 0, buffer.Length, SocketFlags.None); dataSent++; Console.WriteLine(dataSent); } private 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:", string.Empty); Console.WriteLine(str + " has joined the game."); } Console.WriteLine("Clients connected."); } } 

服务器

 using System; using System.Collections.Generic; using System.Net; using System.Net.Sockets; using System.Text; internal sealed class Program { private const int BufferSize = 2048; private const int Port = 100; private readonly List clientSockets = new List(); private readonly byte[] buffer = new byte[BufferSize]; private readonly List players = new List(); private Socket serverSocket; private Socket current; private int dataSent; public static void Main() { var program = new Program(); program.SetupServer(); Console.ReadLine(); program.CloseAllSockets(); } private 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 void CloseAllSockets() { foreach (Socket socket in clientSockets) { socket.Shutdown(SocketShutdown.Both); socket.Close(); } serverSocket.Close(); } private 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, BufferSize, SocketFlags.None, ReceiveCallback, socket); Console.WriteLine("Client connected: " + socket.RemoteEndPoint); serverSocket.BeginAccept(AcceptCallback, null); } private 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:", string.Empty); Player newPlayer = new Player(newuser, current.RemoteEndPoint.ToString()); players.Add(newPlayer); SendString("newuser:" + newuser); Console.WriteLine(newuser + " has joined the game."); } else { // This is where the client text gets mashed together. Console.WriteLine(text); } current.BeginReceive(buffer, 0, BufferSize, SocketFlags.None, ReceiveCallback, current); } private void SendString(string message) { try { byte[] data = Encoding.ASCII.GetBytes(message.ToString()); current.Send(data); // current.BeginReceive(buffer, 0, BufferSize, SocketFlags.None, ReceiveCallback, current); dataSent++; } catch (Exception ex) { Console.WriteLine("Client disconnected!" + ex.Message); } Console.WriteLine(dataSent); } } internal sealed class Player { public Player(string newuser, string toString) { } } 

客户输出

 Port[default]: 100 Connection attempt to 127.0.0.1: 1 attempts Connected! Sent: newuser:6b06f0a6-bdb0-4471-ac58-fa9c490b7555 1 Sent: username:100|200 2 6b06f0a6-bdb0-4471-ac58-fa9c490b7555username:100|200 has joined the game. Clients connected. Sent: username:100|200 3 

服务器输出

 Setting up server... Server setup complete Listening on port: 100 Client connected: 127.0.0.1:1082 1 6b06f0a6-bdb0-4471-ac58-fa9c490b7555username:100|200 has joined the game. username:100|200