C# TCP Server 组合接收到的数据 [英] C# TCP Server combining received data

查看:50
本文介绍了C# TCP Server 组合接收到的数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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.

服务器

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());
    }


}

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

How can I change the server so it reads incoming data one at a time, instead of mashing it into one big string?

注意 LocalPlayer 类使用 Client 类发送它的移动位置.

Note the LocalPlayer class is sending it's movement position using the Client class.

推荐答案

这是因为TCP/IP协议提供了数据流(字节).如果字符串没有明确分隔,它们会因为流式传输"而被连接".

This is because the TCP/IP protocol provides the stream of the data (bytes). If the strings are not separated explicitly, they are "concatenated" because of "streaming".

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

It is required to "join" the messages using the "separator" when sending and "split" the messages using the "separator" when receiving. One of the following alternatives could be considered to implement the "separator" concept:

  • 引入消息结束"标记.
  • 引入包含消息长度的消息头.

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

The small article, TCP/IP client-server application: exchange with string messages, may be useful to understand the mentioned alternatives.

我想建议适当地实施消息传递".

I would like to recommend implementing the "messaging" appropriately.

虽然似乎没有理由比较源代码,因为它的主要缺陷(缺乏消息传递"机制),我用来测试客户端和服务器的源代码已附上.

Although it seems there is no the reason to compare the source code because it of its principal flaw (lack of "messaging" mechanism), the source code I have used to test the Client and the Server is attached.

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 Server 组合接收到的数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆