【发布时间】:2015-08-08 16:54:46
【问题描述】:
我正在开发一个粗略的 TCP 服务器/客户端。它是这样工作的: 客户端向服务器发送消息 > 服务器向每个客户端发送所有用户数据。我有这个循环,因为我将在游戏中使用这种数据传输进行多人游戏。但是,由于某种原因,我的服务器会将传入的数据组合成一个字符串,而不是逐行读取。例如,它应该是这样的 约翰:0|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);
}
}
客户端.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 类发送它的移动位置。
【问题讨论】: