2017-08-15 96 views
-1

我實際上是編程一個簡單的服務器,它是使用tcp協議的C++中的客戶端。由於這將被集成到多人遊戲中,所以每個客戶端都必須非常快速地發送數據。tcp緩衝區中的多條消息

問題:服務器的緩衝區有時會在其中獲取多條消息。

我嘗試了各種東西,如推遲nagle的算法,但我沒有設法解決這個問題。這裏是服務器的代碼:

#ifdef __linux__ 
    #include <sys/types.h> 
    #include <sys/socket.h> 
    #include <netinet/in.h> 
    #include <netinet/ip.h> 
    #include <netinet/tcp.h> 
    #include <unistd.h> 
    #include <stdlib.h> 
    #include <string.h> 
    #include <fcntl.h> 
    #include <errno.h> 
    #include <arpa/inet.h> 
    #define SOCKET int 
    #define SOCKADDR_IN struct sockaddr_in 
#endif 
#ifdef _WIN32 
    #include <winsock2.h> 
#endif 
#include <cstdio> 
#include <iostream> 
#include <thread> 
#include <vector> 
#include <string> 
#include "server.h" 
#include "../../Logger/logger.h" 
#include "../../AltisCraft.fr/Map/map.h" 
#include "../../StringPlus/string_plus.h" 
#include "../../AltisCraft.fr/Map/User/User.h" 
void connectEvent(), receive(), sendAllUsers(string), closeConnectio(),manageMsg(); 
vector<SOCKET> clients; 
vector<thread> clientsThreads; 
vector<string> msg; 
SOCKET socketId, newSocketId; 
SOCKADDR_IN source; 
thread connection; 
char buffer[65535] = {0}; 
int position; 

// TODO: crypt every data sendLog/receive 
// TODO: whitelist ip serv 
// TODO: Auth system 
// TODO: timer with packet ? (double receive...) 

int sendLog(SOCKET s, const char* c, int i0, int i1) 
{ 
    log("Send:"); 
    log(c); 
    send(s, c, i0, i1); 
} 

void initializeNetwork() 
{ 
    #ifdef _WIN32 
     WSADATA initWin32; 
     WSAStartup(MAKEWORD(2, 2),&initWin32); 
    #endif 
    socketId = socket(AF_INET, SOCK_STREAM, 0); 
    source.sin_family = AF_INET; 
    source.sin_addr.s_addr = INADDR_ANY; 
    source.sin_port = htons(33333); 
    bind(socketId, (struct sockaddr*)&source, sizeof(source)); 
    connection = thread(&connectEvent); 
    connection.join(); 
    closeConnection(); 
} 

void connectEvent() 
{ 
    int error; 
    while(1) 
    { 
     error = 99; 
     while(error != 0) 
     { 
      error = listen(socketId, 1); 
     } 
     #ifdef _WIN32 
      int tempo = sizeof(source); 
      newSocketId = accept(socketId, (struct sockaddr*)&source, &tempo); 
      clients.push_back(newSocketId); 
     #endif 
     #ifdef __linux__ 
      socklen_t tempo; 
      newSocketId = accept(socketId, (struct sockaddr *)&source, &tempo); 
      clients.push_back(newSocketId); 
     #endif 
     clientsThreads.push_back(thread(&receive)); 
    } 
} 

void receive() 
{ 
    int val = 1; 
    position = clients.size() - 1; 
    bool connected = 1; 
    while(connected) 
    { 
     buffer[65535] = {0}; 
     if(recv(clients[position], buffer, 1515, 0) > 0) 
     { 
      string msg = buffer; 
      bool isEmpty = false; 
      log(string(inet_ntoa(source.sin_addr)) + ": " + msg); 
      if(startsWith(msg, "Connect ")) 
       addUser(replace(msg, "Connect ", "")); 
      else if(msg == "MAJ Map") 
      { 
       log(elements); 
       string toSend = "MAJ Map\n" + elements; 
       sendLog(clients[position], toSend.c_str(), strlen(toSend.c_str()), 0); 
      } 
      else if(startsWith(msg, "MAJ User ")) /// optimize: don't sendLog pos to player who sendLog 
      { 
       msg = replace(msg, "MAJ User ", ""); 
       if(startsWith(msg, "Pos ")) 
       { 
        msg = replace(msg, "Pos ", ""); 
        vector<string> elements = split(msg, " "); 
        User user = *getUserByName(elements[0] + " " + elements[1]); 
        user.updateView(user.getView().updatePosition(Position(convertStrToDouble(elements[2]), convertStrToDouble(elements[3]), convertStrToDouble(elements[4])))); 
       } 
       else if(startsWith(msg, "ViewAngle ")) 
       { 
        msg = replace(msg, "ViewAngle ", ""); 
        vector<string> elements = split(msg, " "); 
        User user = *getUserByName(elements[0] + " " + elements[1]); 
        user.updateView(user.getView().updateViewAngle(ViewAngle(convertStrToDouble(elements[2]), convertStrToDouble(elements[3])))); 
       } 
      } 
      else 
       sendAllUsers(string(string(inet_ntoa(source.sin_addr)) + ": " + msg).c_str()); 
     } 
     else 
      connected = 0; 
    } 
    shutdown(clients[position], 2); 
    for(int i=0;i<msg.size();i++) 
     cout << msg[i] << endl; 
    #ifdef _WIN32 
     closesocket(clients[position]); 
    #endif 
    #ifdef __linux__ 
     close(clients[position]); 
    #endif 
    clients.erase(clients.begin() + position); 

} 


void sendAllUsersWithoutOne(string msg, string name) 
{ 
    for(int j = 0; j < (int)clients.size(); j++) 
    { 
     // only linux here (MSG_DONTWAIT) 
     #ifdef __linux__ 
     if(recv(clients[j], NULL, 1, MSG_PEEK | MSG_DONTWAIT) == 0) 
     { 
      clients.erase(clients.begin() + j); 
      continue; 
     } 
     #endif 
     sendLog(clients[j], msg.c_str(), strlen(msg.c_str()), 0); 
    } 
} 

void sendAllUsers(string msg) 
{ 
    for(int j = 0; j < (int)clients.size(); j++) 
    { 
     // only linux here (MSG_DONTWAIT) 
     #ifdef __linux__ 
     if(recv(clients[j], NULL, 1, MSG_PEEK | MSG_DONTWAIT) == 0) 
     { 
      clients.erase(clients.begin() + j); 
      continue; 
     } 
     #endif 
     sendLog(clients[j], msg.c_str(), strlen(msg.c_str()), 0); 
    } 
} 

void closeConnection() 
{ 
    for(int i = 0; i < (int)clients.size(); i++) 
    { 
     shutdown(clients[i], 2); 
     #ifdef _WIN32 
      closesocket(clients[i]); 
     #endif 
     #ifdef __linux__ 
      close(clients[i]); 
     #endif 
    } 
    #ifdef _WIN32 
     closesocket(socketId); 
     WSACleanup(); 
    #endif 
    #ifdef __linux__ 
     close(socketId); 
    #endif 
} 

void freeNetwork() 
{ 
    closeConnection(); 
}` 
+0

嗯......你忘了提問題了。 –

+0

@Error - 句法懊悔我把它放在粗體中。 –

+0

首先,如果您需要非常快速地發送/接收數據,您不應該使用TCP - 嘗試使用UDP。如果緩衝區中有多條消息,則應該嘗試從緩衝區讀取,直到它爲空 - 另一件事是,您還應該準備好緩衝區可能只包含消息的一部分,這種情況下您將無法讀取直到收到其餘的消息。您還應該考慮使用非阻塞套接字和/或微線程協程。 – olgierdh

回答

2

對Barmar的評論擴大

TCP是流協議,而不是一個消息協議。只保證發送n個字節,您將以相同的順序接收n個字節。

你可能send 1塊爲100個字節和接收100 1字節recv S,或者您可能會收到20 5個字節recv小號

您可以發送1001個字節的塊和接收4點25字節的信息

你必須自己處理消息邊界。要麼有一個標記值來標記開始和結束,或者預先確定一個本身是固定大小的長度(所以你知道你已經讀了整個長度)。然後在recv上循環,直到收到全部消息