简体   繁体   English

如何使用 tcp 连接发送大块数据

[英]How to send large chunks of data using a tcp connection

I am curently trying to add a multiplayer system to my game engine.我正在尝试将多人游戏系统添加到我的游戏引擎中。 To do so i decided to use the TCP approch.为此,我决定使用 TCP 方法。 Since i am already using the SDL2 library i decided to use the SDL2-net library for networking.因为我已经在使用 SDL2 库,所以我决定使用 SDL2-net 库进行联网。

I wish to send data to the server and vice versa.我希望将数据发送到服务器,反之亦然。 Sending small amounts of data isn't a problem however when i want to send a big chunk of data, a piece of terrain for example, the client will receiver will might get some other data mixed in between the terrain data which is of course not wanted.发送少量数据不是问题但是当我想发送大块数据时,例如一块地形,客户端将接收可能会在地形数据之间混合一些其他数据,这当然不是通缉。 I am no expert in networking so there might be some basic concepts that i do not know of, and i would be more than happy to know if there is a solution to this "problem".我不是网络专家,所以可能有一些我不知道的基本概念,我很乐意知道是否有解决这个“问题”的方法。

Thanks for your help!谢谢你的帮助!

In the following code a client sends a large array of numbers(in Client constructor) to a server and also a message when the client object gets destroyed(in Client destructor) but these two messages get mixed up.在下面的代码中,客户端向服务器发送大量数字(在客户端构造函数中),并在客户端对象被销毁时(在客户端析构函数中)发送一条消息,但这两条消息混淆了。

Client.h客户端.h

#ifndef CLIENT_H
#define CLIENT_H

#include <SDL2/SDL_net.h>

namespace gt
{
  namespace ns
  {
    namespace networking
    {
      class Client
      {
      private:
        TCPsocket socket;
        SDLNet_SocketSet socketSet;

        void sendData(const char*) const;
      public:
        Client(const char*, int);
        ~Client();

        void checkIncomingData();
      };
    }
  }
}

#endif

Client.cpp客户端.cpp

#include "Client.h"

#include <iostream>
#include <cstring>

namespace gt
{
  namespace ns
  {
    namespace networking
    {
      Client::Client(const char* address, int port)
      {
        SDLNet_Init();
        IPaddress ip;
        if(SDLNet_ResolveHost(&ip, address, port) == -1)
        {
          std::cout << "Client connection error!" << std::endl;
        }
        socket = SDLNet_TCP_Open(&ip);
        if(socket == NULL)
        {
          std::cout << "Client connection error! (wrong ip address?)" << std::endl;
        }
        socketSet = SDLNet_AllocSocketSet(1);
        SDLNet_TCP_AddSocket(socketSet, socket);
        sendData("<for the sake of simlicity i removed the numbers that were here>\n");
      }

      Client::~Client()
      {
        sendData("Client disconecting!\n");
        SDLNet_TCP_Close(socket);
        SDLNet_FreeSocketSet(socketSet);
        SDLNet_Quit();
      }

      void Client::sendData(const char* data) const
      {
        int size = strlen(data);
        int sentsize = 0;
        while(sentsize < size)
        {
          sentsize+=SDLNet_TCP_Send(socket, data + sentsize, size - sentsize);
        }
      }

      void Client::checkIncomingData()
      {
        while(SDLNet_CheckSockets(socketSet,0) > 0)
        {
          if(SDLNet_SocketReady(socket))
          {
            char data[1400];
            SDLNet_TCP_Recv(socket, data, 1400);
            std::cout << "data received: " << data << std::endl;
          }
        }
      }
    }
  }
}

Server.h服务器.h

#ifndef SERVER_H
#define SERVER_H

#include <SDL2/SDL_net.h>

#include "ClientData.h"

#include "../utils/ArrayList.h"

namespace gt
{
  namespace ns
  {
    namespace networking
    {
      class Server
      {
      private:
        int maxClients;

        IPaddress ip;
        TCPsocket socket;
        ArrayList<ClientData> clients;
        SDLNet_SocketSet clientsSet;

        int currentId;

        void removeClient(int);
        void checkNewConnection();
        void checkIncomingData();
        void checkTimeouts(float);
        bool readData(const TCPsocket&);
      public:
        Server(int, int);
        ~Server();

        void update(float);
      };
    }
  }
}

#endif

Server.cpp服务器.cpp

#include "Server.h"

#include <iostream>
#include <cstring>

namespace gt
{
  namespace ns
  {
    namespace networking
    {
      Server::Server(int maxClients, int port)
      {
        this->maxClients = maxClients;

        clientsSet = SDLNet_AllocSocketSet(maxClients);

        SDLNet_ResolveHost(&ip, NULL, port);
        socket = SDLNet_TCP_Open(&ip);

        currentId = 1;
      }

      Server::~Server()
      {
        SDLNet_FreeSocketSet(clientsSet);
        SDLNet_TCP_Close(socket);
      }

      void Server::removeClient(int index)
      {
        SDLNet_TCP_DelSocket(clientsSet, clients[index].getSocket());
        clients.remove(index);
      }

      void Server::checkNewConnection()
      {
        TCPsocket clientSocket = SDLNet_TCP_Accept(socket);
        if(clientSocket)
        {
          if(clients.size() < maxClients)
          {
            SDLNet_TCP_AddSocket(clientsSet, clientSocket);
            ClientData data(currentId, clientSocket);
            clients.add(data);
            std::cout << "new connection id: " << currentId << std::endl;
            currentId++;
          }
          else
          {
            //no more space!
          }
        }
      }

      void Server::checkIncomingData()
      {
        while(SDLNet_CheckSockets(clientsSet, 0) > 0)
        {
          for(int i=0;i<clients.size();i++)
          {
            if(SDLNet_SocketReady(clients[i].getSocket()))
            {
              if(readData(clients[i].getSocket()))
              {
                clients[i].timer.reset();
              }
              else
              {
                removeClient(i);
              }
            }
          }
        }
      }

      void Server::checkTimeouts(float timePassed)
      {
        for(int i=0;i<clients.size();i++)
        {
          clients[i].timer.update(timePassed);
          if(clients[i].timer.getTime() > 5.0)
          {
            removeClient(i);
          }
        }
      }

      void Server::update(float timePassed)
      {
        checkNewConnection();
        checkIncomingData();
        checkTimeouts(timePassed);
      }

      bool Server::readData(const TCPsocket& clientSocket)
      {
        char data[1400];
        int size = SDLNet_TCP_Recv(clientSocket, data, 1400);
        if(size <= 0)
        {
          std::cout << "RECEIVING DATA FAILED: " << size << std::endl;
          return false;
        }
        std::cout << data << std::endl;

        while(data[strlen(data) - 1] != '\n')
        {
          size = SDLNet_TCP_Recv(clientSocket, data, 1400);
          if(size <= 0)
          {
            std::cout << "RECEIVING DATA FAILED: " << size << std::endl;
            return false;
          }
          std::cout << data << std::endl;
        }
        return true;
      }
    }
  }
}

what i get in the server terminal when i create the Client object and delete right after(the array in code is bigger i just chose the interesting part of the output):当我创建客户端对象并在之后立即删除时,我在服务器终端中得到了什么(代码中的数组更大,我只是选择了输出中有趣的部分):

 0.1519227 -0.002553641 -0.3744464 0.1526676 0 -0.06999236 0.1529192 0.002553701 -0.06999236 0.1526677 0.005009293 -0.06999236 0.1519228 0.007272362 -0.06999236 0.1507131 0.009255945 -0.06999236 0.149085
2 0.01088386 -0.06999236 0.1471016 0.01209348 -0.06999236 0.1448385 0.01283836 -0.06999236 0.142383 0.01308989 -0.06999236 0.1398292 0.01283836 -0.06999236 0.1372755 0.01209348 -0.06999236 0.13482 0.01088386 -0.06999236 0.1325569 0.009255945 -0.06999236 0.1305733 0.007272362 -0.06999236 0.1289454 0.005009293 -0.06999236 0.1277357 0.002553701 -0.06999236 0.1269909 0 -0.06999236 0.1267393 -0.002553701 -0.06999236 0.1269909 -0.005009293 -0.06999236 0.1277357 -0.007272362 -0.06999236 0.1289454 -0.009255945 -0.06999236 0.1305733 -0.01088386 -0.06999236 0.1325569 -0.01209348 -0.06999236 0.13482 -0.01283836 -0.06999236 0.1372755 -0.01308989 -0.06999236 0.1398293 -0.01283836 -0.06999236 0.142383 -0.01209348 -0.06999236 0.1448386 -0.01088386 -0.06999236 0.1471017
.1269907 -0.005009293 -0.3744465 0.1277357 -0.007272362 -0.3744464 0.1289453 -0.009255945 -0.3744465 0.1305732 -0.01088386 -0.3744464 0.1325568 -0.01209348 -0.3744465 0.1348199 -0.01283836 -0.3744465 0.1372755 -0.01308989 -0.3744465 0.1398292 -0.01283836 -0.3744464 0.1423829 -0.01209348 -0.3744464 0.1448385 -0.01088386 -0.3744465 0.1471015 -0.009255945 -0.3744464 0.1490852 -0.007272303 -0.3744464 0.150713 -0.005009233 -0.3744464 0.1519227 -0.002553641 -0.3744464 0.1526676 0 -0.06999236 0.1529192 0.002553701 -0.06999236 0.1526677 0.005009293 -0.06999236 0.1519228 0.007272362 -0.06999236 0.1507131 0.009255945 -0.06999236 0.149085
Client disconecting! <- this message is received in the number array!
236 0.1471016 0.01209348 -0.06999236 0.1448385 0.01283836 -0.06999236 0.142383 0.01308989 -0.06999236 0.1398292 0.01283836 -0.06999236 0.1372755 0.01209348 -0.06999236 0.13482 0.01088386 -0.06999236 0.1325569 0.009255945 -0.06999236 0.1305733 0.007272362 -0.06999236 0.1289454 0.005009293 -0.06999236 0.1277357 0.002553701 -0.06999236 0.1269909 0 -0.06999236 0.1267393 -0.002553701 -0.06999236 0.1269909 -0.005009293 -0.06999236 0.1277357 -0.007272362 -0.06999236 0.1289454 -0.009255945 -0.06999236 0.1305733 -0.01088386 -0.06999236 0.1325569 -0.01209348 -0.06999236 0.13482 -0.01283836 -0.06999236 0.1372755 -0.01308989 -0.06999236 0.1398293 -0.01283836 -0.06999236 0.142383 -0.01209348 -0.06999236 0.1448386 -0.01088386 -0.06999236 0.1471017
.1269907 -0.005009293 -0.3744465 0.1277357 -0.007272362 -0.3744464 0.1289453 -0.009255945 -0.3744465 0.1305732 -0.01088386 -0.3744464 0.1325568 -0.01209348 -0.3744465 0.1348199 -0.01283836 -0.3744465 0.1372755 -0.01308989 -0.3744465 0.1398292 -0.01283836 -0.3744464 0.1423829 -0.01209348 -0.3744464 0.1448385 -0.01088386 -0.3744465 0.1471015 -0.009255945 -0.3744464 0.1490852 -0.007272303 -0.3744464 0.150713 -0.005009233 -0.3744464 0.1519227 -0.002553641 -0.3744464 0.1526676 0 -0.06999236 0.1529192 0.002553701 -0.06999236 0.1526677 0.005009293 -0.06999236 0.1519228 0.007272362 -0.06999236 0.1507131 0.009255945 -0.06999236 0.149085

The problem was comming from the read function.问题出在读取功能上。 I thought that if i gave it a NULL char array it would fill it up with the incoming data and then stop.我想如果我给它一个 NULL 字符数组,它会用传入的数据填充它然后停止。 However it fills in the char array using the incoming data and then fills up the remaining space of the char array with the prévious data received.然而,它使用传入的数据填充 char 数组,然后用接收到的先前数据填充 char 数组的剩余空间。 (if the incoming data was shorter then the previous). (如果传入数据比前一个短)。 As pointed out user207421 using the size of received data is needed.正如 user207421 所指出的那样,需要使用接收到的数据的大小。 The fix was actualy simple, the read function returns the size of the received data and 0 or less if there was a connection error.修复实际上很简单,读取函数返回接收到的数据的大小,如果出现连接错误则返回 0 或更小。

The fix:修复:

(Although this code will print the data correcly in order packet seperation might still be needed for data processing) (虽然此代码将正确打印数据,以便数据处理可能仍需要数据包分离)

bool Server::readStream(char* data, int* size, const TCPsocket& clientSocket)
      {
        *size = SDLNet_TCP_Recv(clientSocket, data, 1400);
        return *size > 0;
      }

      bool Server::addStream(Pointer<char>* pointer, const TCPsocket& clientSocket)
      {
        std::cout << "adding stream" << std::endl;
        char stream[1400];
        int streamSize;
        if(!readStream(stream, &streamSize, clientSocket))
        {
          return false;
        }

        int currentSize = 0;
        if(pointer->value() != NULL)
        {
          currentSize = strlen(pointer->value());
        }

        std::cout << "creating ptr: " << currentSize << ":" << streamSize << std::endl;
        Pointer<char> newPointer(new char[currentSize + streamSize + 1]);
        std::cout << "copying old values" << std::endl;
        for(int i=0;i<currentSize;i++)
        {
          *(newPointer.value() + i) = *(pointer->value() + i);
        }

        std::cout << "copying new values" << std::endl;
        for(int i=0;i<streamSize;i++)
        {
          *(newPointer.value() + currentSize + i) = *(stream + i);
        }
        std::cout << "adding end point" << std::endl;
        *(newPointer.value() + currentSize + streamSize) = '\0';
        *pointer = newPointer;
        std::cout << "done stream" << std::endl;
        return true;
      }

      bool Server::readData(const TCPsocket& clientSocket)
      {

        std::cout << "START DATA READING" << std::endl;
        std::cout << "-------------------------------------------" << std::endl;
        Pointer<char> pointer;
        if(!addStream(&pointer, clientSocket))
        {
          std::cout << "RECEIVING DATA FAILED" << std::endl;
          return false;
        }

        while(*(pointer.value() + strlen(pointer.value()) - 1) != '\n')
        {
          if(!addStream(&pointer, clientSocket))
          {
            std::cout << "RECEIVING DATA FAILED" << std::endl;
            return false;
          }
        }
        std::cout << "data received: " << pointer.value() << std::endl;
        std::cout << "-------------------------------------------" << std::endl;
        return true;
      }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM