我正在研究一个粗略的TCP服务器/客户端。它的工作原理如下: 客户端向服务器发送消息> Server将所有用户数据发送到每个客户端。我有一个循环,因为我将在游戏中使用这种多人游戏数据传输。但是,出于某种原因,我的服务器会将传入的数据组合成一个字符串,而不是逐行读取。例如,它应该读取类似的内容 约翰:0 | 对于用户数据,但它读起来像john:0 | 0john:0 | 0john:0 | 0john:0 | 0 我已经尝试设置发送和接收的延迟,但它似乎只有我延迟超过100毫秒。
服务器
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());
}
}
如何更改服务器以便一次读取一个传入数据,而不是将其混合成一个大字符串?
编辑:注意LocalPlayer类使用Client类发送它的移动位置。
答案 0 :(得分:1)
这是因为TCP / IP协议提供了数据流(字节)。如果字符串未明确分隔,则由于“流式传输”而将它们“连接”。
发送时需要使用“分隔符”“加入”消息,并在接收时使用“分隔符”“分割”消息。可以考虑使用以下替代方案之一来实现“分隔符”概念:
小文章TCP/IP client-server application: exchange with string messages可能有助于理解上述替代方案。
我想建议适当地实施“消息”。
虽然似乎没有理由比较源代码,因为它的主要缺陷(缺少“消息传递”机制),我用来测试客户端和服务器的源代码是附加的。
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<Socket> clientSockets = new List<Socket>();
private readonly byte[] buffer = new byte[BufferSize];
private readonly List<Player> players = new List<Player>();
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