1

我正在尝试在 C# 中实现一个基于 tcp 的小型应用程序。我是 TCP 编程的新手。

该应用程序非常简单。这是一个客户端/服务器方案,最多支持 2 个客户端。一个客户端将是“外部”IP,另一个将是主机本身。

每次客户端连接到服务器时,它都会开始从/向服务器接收和发送数据包。

我已经建立了一个小型控制台应用程序,它实现了 TCP 服务器端代码和 TCP 客户端代码。

服务器使用绑定到特定端口的侦听器并等待客户端连接。连接客户端后,它会生成一个新线程来处理与客户端的通信。

客户端只需连接到服务器,一旦连接建立,它就会开始接收和发送数据包。

正如我之前所说的“主机也是它自己的客户”,但这会导致一个问题。如果客户端向服务器发送一个数据包,服务器和客户端都声称收到了一个数据包。这是正常的?我错过了什么吗?

更新 2(修复问题)

我想我找到了问题所在。客户端完成发送操作后,我需要“重置”缓冲区

          case SocketAsyncOperation.Send:                        

              // Put in listen mode

              // Buffer reset AFTER send and BEFORE receive
              _receiver.SetBuffer(new byte[4096], 0, 4096);

              _client.ReceiveAsync(_receiver);                        

              break;

更新 1(添加代码)

服务器

    public void Host()
    {                        
        _listener = new TcpListener(new IPEndPoint(IPAddress.Any, this.ConnectionPort));
        _listener.Start();

        this.IsListening = true;            

        ThreadPool.QueueUserWorkItem((state) =>
        {
            while (true)
            {
                // Blocks until a client connections occours
                // If continue with no client then stop listening                    
                try
                {
                    TcpClient client = _listener.AcceptTcpClient();

                    // Add client to peer list
                    long assignedID = DateTime.UtcNow.Ticks;
                    _clients.Add(assignedID, client);

                    HandleClient(assignedID);

                    if (this.ConnectedClients == this.MaxClients)
                    {
                        _listener.Stop();

                        this.IsListening = false;

                        break;
                    }
                }
                catch
                {
                    // Server has stop listening
                    _stopListen.Set();

                    break;
                }
            }
        });
    }

    private void HandleClient(long assignedID)
    {
        ThreadPool.QueueUserWorkItem((state) =>
        {
            long clientID = (long)state;
            TcpClient client = _clients[clientID];        

            try
            {
                byte[] message = new byte[4096];
                byte[] buffer = new byte[4096];

                int readed = 0;
                int index = 0;

                NetworkStream clientStream = client.GetStream();

                while (true)
                {                        
                    readed = clientStream.Read(message, 0, message.Length);
                    if (readed == 0)
                    {
                        // Client disconnected, thorw exception
                        throw new SocketException();
                    }

                    Array.Copy(message, 0, buffer, index, readed);
                    index += readed;

                    if (readed == 4096
                        && !_packetHub.IsValidBufferData(buffer))
                    {
                        Array.Clear(buffer, 0, buffer.Length);
                        readed = 0;
                        index = 0;

                        continue;
                    }

                    if (_packetHub.IsValidBufferData(buffer))
                    {
                        NetPacket receivedPacket = _packetHub.DeserializePacket(buffer);
                        NetPacket answerPacket = null;

                        // Assign sender ID in case of unknown sender ID
                        if (receivedPacket.SenderID == NetPacket.UnknownID)
                            receivedPacket.SenderID = clientID;

                        // Handle received packet and forward answer packet
                        // to the client or to all other connected peers                            
                        answerPacket = HandlePacket(receivedPacket);                            
                        if (answerPacket != null)
                        {                                
                            if (answerPacket.RecipientID == clientID)
                            {
                                // Send answer packet to the client
                                SendPacket(client, answerPacket);
                            }
                            else
                            {
                                // Broadcast packet to all clients
                                foreach (TcpClient peer in _clients.Values)
                                    SendPacket(peer, answerPacket);
                            }
                        }

                        // Reset receive packet buffer
                        Array.Clear(buffer, 0, buffer.Length);
                        readed = 0;
                        index = 0;
                    }
                }
            }
            catch
            {
                System.Diagnostics.Debug.WriteLine("Network error occours!");
            }
            finally
            {
                // Close client connection
                client.Close();

                // Remove the client from the list
                _clients.Remove(clientID);

                // Raise client disconnected event
                OnClientDisconnected();
            }

        }, assignedID);
    }

客户

    public void Connect()
    {
        if (this.ConnectionAddress == null)
            throw new InvalidOperationException("Unable to connect. No server IP specified.");

        _receiver = new SocketAsyncEventArgs();
        _receiver.RemoteEndPoint = new IPEndPoint(this.ConnectionAddress, this.ConnectionPort);
        _receiver.SetBuffer(new byte[4096], 0, 4096);
        _receiver.Completed += new EventHandler<SocketAsyncEventArgs>(Socket_OperationComplete);

        _client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);            

        _client.ConnectAsync(_receiver);
    }

    private void Socket_OperationComplete(object sender, SocketAsyncEventArgs e)
    {
        if (e.SocketError == SocketError.Success)            
        {
            switch (e.LastOperation)
            {
                case SocketAsyncOperation.Connect:

                    _readyToSend = true;

                    this.IsConnected = true;

                    // Once connection is estabilished, send a join packet to
                    // retrive server side assigned informations

                    JoinPacket joinPacket = (JoinPacket)NetPacket.CreatePacket(this.ClientID, NetPacket.ToHost, NetPacket.JoinPacket);
                    joinPacket.ClientName = this.ClientName;
                    joinPacket.ClientAddress = this.LocalAddress.ToString();

                    SendPacket(joinPacket);

                    break;

                case SocketAsyncOperation.Receive:

                    _readyToSend = true;

                    byte[] buffer = _receiver.Buffer;

                    System.Diagnostics.Debug.WriteLine("Client received packet (" + _packetHub.GetBufferDataType(buffer) + ") with length of: " + buffer.Length);

                    if (_packetHub.IsValidBufferData(buffer))
                    {
                        NetPacket receivedPacket = _packetHub.DeserializePacket(buffer);
                        NetPacket answerPacket = null;

                        // Handle received packet and forward answer packet
                        // to the server
                        answerPacket = HandlePacket(receivedPacket);
                        if (answerPacket != null)
                            SendPacket(answerPacket);
                    }
                    else
                    {
                        _client.ReceiveAsync(_receiver);
                    }

                    break;

                case SocketAsyncOperation.Send:                        

                    // Put in listen mode
                    _client.ReceiveAsync(_receiver);                        

                    break;
            }
        }            
        else
        {
            OnClientDisconnected();

            System.Diagnostics.Debug.WriteLine(String.Format("Network error: {0}", e.SocketError.ToString()));
        }
    }
4

1 回答 1

0

由于您使用的是 TCP,因此您必须指定要发送/侦听的端口(服务器和客户端不同)。所以不,这是不正常的(但如果客户端和服务器在同一个端口上发送/监听)。

于 2012-10-17T10:31:11.767 回答