2012-01-24 21 views
0

我在C++中有一個套接字服務器,我正在使用epoll。我正在向服務器發送一個包含HeaderPacket和NormalPacket的字符。 首先我正在閱讀HeaderPacket,然後閱讀NormalPacket。我現在的問題是,當我關閉客戶端(我試圖使用關閉和關閉 - 相同的輸出)時,我在第一個recv(讀取頭數據包的那個)上得到了一些奇怪的字節,並且在分段錯誤。另外,當我將我的內容字符的大小從HeaderPacket更改爲另一個值(例如120)時,我沒有得到分段錯誤,但是當我將它設置爲40或其他值時,我得到了分段錯誤。帶epoll的套接字服務器在斷開時給出未知字節

#define BUFFERSIZE 256 
#define CHARSIZE 40 

這裏是我用它來閱讀功能:

void PacketHandler::ReadBytes(int fd, struct HeaderPacket &hp, char buffer[]) 

{ 

    int reading = 0; 

    ssize_t hpCount, cpCount; 

    char hpBuffer[6]; 



    hpCount = recv(fd, hpBuffer, 6, 0); 

    if(hpCount <= 0) 

    { 

     reading = 1; 

    } else { 

     this->UnserializeHeaderPacket(hpBuffer, hp); 

     print(DEBUG, Helpers::IntegerToString(hp.length)); 

     cpCount = hp.length; 

     char cpBuffer[cpCount]; 

     memset(cpBuffer, 0, sizeof(cpBuffer)); 

     char* iterator = cpBuffer; 

     int bytesLeft = sizeof(cpBuffer) - sizeof(char); 

     print(DEBUG, Helpers::IntegerToString(bytesLeft)); 

     if(bytesLeft < 0) 

     { 

      reading = 1; 

     } 

     while(bytesLeft > 0) 

     { 

      ssize_t curr; 

      curr = recv(fd, iterator, bytesLeft, 0); 



      if(curr == -1) 

      { 

       if(errno != EAGAIN) 

       { 

        reading = 1; 

        print(WARNING, "reading error at content packet"); 

       } 

       break; 

      } else if(curr == 0) { 

       reading = 1; 

       break; 

      } 



      iterator += curr; 

      bytesLeft -= curr;     





     } 



      memcpy(buffer, cpBuffer, sizeof cpBuffer);  

    } 



    if(reading) 

    { 

     print(NOTICE, "Closed connection with the descriptor " + Helpers::IntegerToString(fd)); 

     close(fd); 

    } 

} 

這裏是epoll的功能

void EventHandler::RunningLoop(int fd) 

{ 

    while(1) 

    { 

     int availableEvents, i; 



     availableEvents = epoll_wait(this->efd, this->events, MAXEVENTS, -1); 

     for(i = 0; i < availableEvents; i++) 

     { 

      if(this->events[i].data.fd == fd) 

      { 

       // Accepting new connection 

       this->AcceptClient(fd); 

       continue; 

      } 

      else if((this->events[i].events & EPOLLERR) || (this->events[i].events & EPOLLHUP) || (!(this->events[i].events & EPOLLIN))) 

      { 

       print(WARNING, "epoll error on reading from fd"); 

       close (this->events[i].data.fd); 

       continue; 

      } 

      else if(this->events[i].events & EPOLLRDHUP) 

      { 

       print(WARNING, "intern close socket"); 

       close (this->events[i].data.fd); 



      } else { 

       // Reading packets 

       this->run->InitializePacket(this->events[i].data.fd); // cals the read function 

      } 

     } 

    } 



    free(this->events); 

    close(fd); 

} 

我的包:

struct HeaderPacket 

{ 

    uint16_t opcode; 

    uint32_t length; 

}; 



struct HelloWorldPacket 

{ 

    uint16_t byteOrder; 

    char content[CHARSIZE]; 

}; 

功能序列化:

void PacketHandler::SerializeHeaderPacket(HeaderPacket packet, char buffer[]) 

{ 

    uint16_t u16; 

    uint32_t u32; 



    u16 = htons(packet.opcode); 

    memcpy(buffer+0, &u16, 2); 

    u32 = htonl(packet.length); 

    memcpy(buffer+2, &u32, 4); 

} 



void PacketHandler::UnserializeHeaderPacket(char buffer[], HeaderPacket &packet) 

{ 

    uint16_t u16; 

    uint32_t u32; 



    memcpy(&u16, buffer+0, 2); 

    packet.opcode = ntohs(u16); 

    memcpy(&u32, buffer+2, 4); 

    packet.length = ntohl(u32); 

} 



void PacketHandler::SerializeHelloWorldPacket(HelloWorldPacket packet, char buffer[]) 

{ 

    uint16_t u16; 



    u16 = htons(packet.byteOrder); 

    memcpy(buffer+0, &u16, 2); 

    memcpy(buffer+2, &packet.content, sizeof packet.content); 

} 



void PacketHandler::UnserializeHelloWorldPacket(char buffer[], HelloWorldPacket &packet) 

{ 

    uint16_t u16; 



    memcpy(&u16, buffer+0, 2); 

    packet.byteOrder = ntohs(u16); 



    strcpy(packet.content, buffer+2); 



} 

這就是我如何將數據發送到服務器:

int EventHandler::SendHelloWorld(int fd) 

{ 

    HeaderPacket hp; 

    HelloWorldPacket hc; 

    char buffer[256]; 

    int sendResult; 

    char message[] = "hello_first_message\r\n"; 





    hp.opcode = HELLOWORLD; 

    hp.length = sizeof message; 

    memcpy(hc.content, message, sizeof message); 





    packets->SerializeHeaderPacket(hp, buffer); 

    packets->SerializeHelloWorldPacket(hc, buffer+6); 



    sendResult = write(fd, buffer, sizeof buffer); 







    return sendResult; 



} 

感謝您的幫助。

+0

它編譯沒有錯誤和警告? – stefanB

+0

當連接關閉('recv'返回0)時,您是否從epoll集中移除套接字? –

+0

@Joachim PileBorg我使用close(fd)是不夠的?謝謝。 – cemycc

回答

0

我發現了這個問題。 當我寫服務器時,我使用sizeof緩衝區和緩衝區是256長度,我不讀256字節。

感謝您的幫助。

+1

你的意思是客戶端可能會崩潰你的服務器?我希望你打算在服務器端解決這個問題。您的「unserializeHelloWorld」函數始終是不安全的。我需要詳細說明原因嗎? 此外,「valgrind」是一個非常有用的工具來診斷這種神祕的崩潰。它可能會在崩潰前在代碼中的特定位置標記緩衝區溢出。 – selbie

+0

@selbie你能解釋爲什麼不安全嗎?謝謝,我會嘗試valgrind。 – cemycc

+0

錯誤#1:「ReadBytes」功能。它讀取6個字節並將其提供到UnserializeHeaderPacket中。一個糟糕的買(用他自己的客戶端代碼)連接到你的服務器,並將這6個字節作爲headerpacket字節流發送:{0xff,0xff,0xff,0xff,0xff,0xff}。 UnserializeHeaderPacket將初始化「hp.length」成員爲4294967295(即4GB)。然後,您將通過調用「char cpBuffer [cpCount];」來分配該內存量我很肯定會讓你的服務器崩潰。如果沒有,函數底部的「memcpy(buffer,cpBuffer,sizeof cBBuffer)」語句肯定會。 – selbie