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

时间:2015-05-26 20:31:08

标签: c# tcp client server sync

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

1 个答案:

答案 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