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

查看:107
本文介绍了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);
    }
}

Client.cs

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


}

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

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天全站免登陆