gpt4 book ai didi

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

转载 作者:可可西里 更新时间:2023-11-01 02:51:40 27 4
gpt4 key购买 nike

我正在研究一个粗略的 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 类发送它的移动位置。

最佳答案

这是因为 TCP/IP 协议(protocol)提供了数据流(字节)。如果字符串没有明确分开,它们会因为“流”而“连接”起来。

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

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

小文章,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

关于C# TCP 服务器合并接收到的数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30468329/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com