2014-09-25 54 views
2

我上網了很多問題,在黑板上,約TCP套接字,big-endian和little-endian的格式,但對我來說沒有什麼apllies到我的情況。意外行爲<-> C++服務器

而且我對我的英語不好對不起,我的工作就可以了:)

我失去我的心在一個簡單的客戶端 - 服務器配置的意外行爲。這裏的情景:

服務器(C++)< --- TCP套接字--->客戶端(JAVA)。

這裏的客戶端代碼:

package NetServ.apps.bigServer.NSLPClient; 

import java.io.DataInputStream; 
import java.io.DataOutputStream; 
import java.io.IOException; 
import java.net.Socket; 
import java.net.SocketException; 
import java.net.UnknownHostException; 
import java.nio.charset.Charset; 
import java.util.ArrayList; 


public class Communicator { 

    private Socket sock; 
    private final int port = 6666; 
    private final String address="127.0.0.1"; 
    private DataOutputStream out; 
    private DataInputStream in; 
    public Communicator(){ 

    System.out.println("Creating communicator. Trying to bind to the tcp socket"); 

    try { 
     sock = new Socket(address, port); 
     out=new DataOutputStream(sock.getOutputStream()); 
     in=new DataInputStream(sock.getInputStream()); 
    } catch (UnknownHostException e) { 
     System.out.println("Unable to resolv host"); 
     e.printStackTrace(); 
    } catch (IOException e) { 
     System.out.println("Generic I/O exception"); 
     e.printStackTrace(); 
    } 
    System.out.println("Communicator created"); 
} 


    public void sendRequest(Request req) throws IOException{ 
    int cmd=0; 
    if(req.getCmd().equals(CommandType.tg_setup_message)) 
     cmd=0; 
    if(req.getCmd().equals(CommandType.tg_remove_message)) 
     cmd=1; 
    if(req.getCmd().equals(CommandType.tg_trigger_message)) 
     cmd=2; 
    if(req.getCmd().equals(CommandType.tg_probe_message)) 
     cmd=3; 
    byte[] buff; 
    Charset charset = Charset.forName("ISO-8859-1"); 

    out.writeInt(cmd); 

    //out.writeUTF(req.getDstAddr().toString().substring(1)); 
    buff = req.getDstAddr().toString().substring(1).getBytes(charset); 
    out.writeShort((short)buff.length); 
    out.write(buff, 0, buff.length); 

    out.writeInt(req.getProtocol()); 
    out.writeInt(req.getSecure()); 

    //out.writeUTF(req.getDataId()); 
    buff = req.getDataId().getBytes(charset); 
    out.writeShort((short)buff.length); 
    out.write(buff, 0, buff.length); 

    //out.writeUTF(req.getUser()); 
    buff = req.getUser().getBytes(charset); 
    out.writeShort((short)buff.length); 
    out.write(buff, 0, buff.length); 


    out.flush(); 
    out.writeInt(req.getOffpath_type()); 
    if(req.getOffpath_type()!=-1){ 
     out.writeInt(req.getMetric_type()); 

     String tmp = "" + req.getMetric(); 

     //out.writeUTF(tmp); 
     buff = tmp.getBytes(charset); 
     out.writeShort((short)buff.length); 
     out.write(buff, 0, buff.length); 

    } 

    switch (req.getCmd()){ 
    case tg_setup_message: 
     out.writeUTF(req.getUrl());   
     out.writeInt(req.getLifetime()); 
     out.writeUTF(req.getParameters().toString()); 
     break; 
    case tg_remove_message: 
     //TODO 
     break; 
    case tg_trigger_message: 
     //TODO 
     break; 
    case tg_probe_message: 
     for (Short s : req.getProbes()){ 
      //System.out.println("Writing probe code " + s.shortValue()); 
       out.writeShort(s.shortValue()); 
     } 
     break; 
    } 


    if(req.getSignature()!=null){ 
     out.writeInt(1); 
     out.writeUTF(req.getSignature()); 
    }else{   
     out.writeInt(0); 
    } 

    if(req.getDep()!=null){ 
     out.writeInt(1); 
     out.writeUTF(req.getDep()); 
    }else{ 
     out.writeInt(0); 
    } 

    if(req.getNotif()!=null){ 
     out.writeInt(1); 
     out.writeUTF(req.getNotif()); 
    }else{ 
     out.writeInt(0); 
    } 

    if(req.getNode()!=null){ 
     out.writeInt(1); 
     out.writeUTF(req.getNode()); 
    }else{ 
     out.writeInt(0); 
    } 
    out.flush(); 
    //out.close(); 
    System.out.println("request sent"); 
} 

public ArrayList<String> rcvProbeResponse() throws IOException, SocketException{ 
    ArrayList<String> response= new ArrayList<String>(); 
    System.out.println("Waiting for response..."); 
    boolean timeout=false; 

    int responseCode=-1; 
    responseCode=in.readInt(); 
    //responseCode = in.readInt(); 
    //System.out.println("Response code "+responseCode); 
    if(responseCode==1){ //response is ready! ! 
     System.out.println("Response arriving from NSLP (code 1)"); 

     int responseCmdCode = in.readInt(); 
     if(responseCmdCode!=2) 
      return null; 
     //System.out.println("Response Command Code " + responseCmdCode); 
     int probeSize = in.readInt(); 
     //System.out.println("Number of probes " + probeSize); 
     for(int i=0; i<probeSize; i++){ 
      //System.out.println("i: "+i); 
      String out = in.readUTF(); 
      response.add(out); 
     } 
    } 
    in.close(); 
    if(timeout) 
     return null; 
    else 
     return response; 
} 

} 

沒有什麼特別之處在於:實體之間的協議是一個簡單的整數短褲一個交流,觸發服務器來執行一些信令任務(服務器是信令協議的守護進程)。

在另一側的服務器是我改性用java到comunicate遺留代碼。 下面是相關代碼:

[...] 
// Set the current socket 
communicator->setSocket(sockfd); 

// FSM data structure 
NetservNslpFsmData * data = new NetservNslpFsmData(); 

//give the address list of this node to all FSMs created by the client 
data->nodeAddressList = &(param.addresses); 
// Read from socket the parameters and use them 
int ret; 
NetservNslpCommunicator::command cmd; 
ret = communicator->recvCommandFromJava(&cmd); 
if (ret <= 0) { 
    logSocketError(sockfd, "Command"); 
    // free up the memory allocated 
    delete data; 
    return; 
} 

switch(cmd){ 
case NetservNslpCommunicator::tg_setup_message: 
    DLog(param.name, "cmd set: setup"); 
    break; 
case NetservNslpCommunicator::tg_remove_message: 
    DLog(param.name, "cmd set: remove"); 
    break; 
case NetservNslpCommunicator::tg_probe_message: 
    DLog(param.name, "cmd set: probe"); 
    break; 
case NetservNslpCommunicator::tg_trigger_message: 
    DLog(param.name, "cmd set: trigger"); 
    break; 
} 
ret = communicator->recvIPFromJava(&(data->destAddr)); 
DLog(param.name, "Dst Address set: "<< data->destAddr.get_ip_str()); 
if (ret <= 0) { 
    logSocketError(sockfd, "Destination IP"); 
    // free up the memory allocated 
    delete data; 
    return; 
} 

[...] 
int reliable = communicator->recvIntFromJava(); 
data->reliability = (reliable == NetservNslpCommunicator::TCP); 
DLog(param.name, "Reliability set : "<< data->reliability); 
int secure = communicator->recvIntFromJava(); 
data->security = (secure == NetservNslpCommunicator::TCP); 
DLog(param.name, "Security set : "<< data->security); 

data->dataId = communicator->recvStringFromJava(); 
DLog(param.name, "DataId : "<< data->dataId); 
if (data->dataId == NULL) { 
    logSocketError(sockfd, "dataId"); 
    // free up the memory allocated 
    delete data; 
    return; 
} 
data->user = communicator->recvStringFromJava(); 
DLog(param.name, "User : "<< data->user); 
if (data->user == NULL) { 
    logSocketError(sockfd, "user"); 
    // free up the memory allocated 
    delete data; 
    return; 
} 

//Receiving OffPath parameters 
data->offpath_type=communicator->recvIntFromJava(); 
DLog(param.name, "OffType : "<< data->offpath_type); 
if(data->offpath_type != -1){ 

    data->metric_type=communicator->recvIntFromJava(); 
    DLog(param.name, "MetricType : "<< data->metric_type); 
    if(data->metric_type>3|| data->metric_type<1){ 
     logSocketError(sockfd, "metric type"); 
     // free up the memory allocated 
     delete data; 
     return; 
    } 
    char * tmpStr = communicator->recvStringFromJava(); 
    if (tmpStr == NULL) { 
     logSocketError(sockfd, "metric"); 
     // free up the memory allocated 
     delete data; 
     return; 
    } 
    data->metric = tmpStr; 
    DLog(param.name, "MetricValue : "<< data->metric); 
    DLog(param.name, "MetricLength : "<< data->metric.length()); 
} 

// check if socket is still alive or some errors occured 
if (!communicator->isAlive(sockfd)) { 
    logSocketError(sockfd, "Socket not alive!"); 
    // free up the memory allocated 
    delete data; 
    return; 
} 
DLog(param.name,"Reading command-specific configuration"); 
switch(cmd) 
{ 
case NetservNslpCommunicator::tg_setup_message: 
    data->urlList.push_back(communicator->recvString()); 
    //check if the service data is exchanged together with signaling messages 
    if (data->urlList.front() != NULL && (strncmp(data->urlList.front(), "file://", 7) == 0)) 
     data->data_included = true; 
    data->lifetime = communicator->recvIntFromJava(); 
    data->setupParams = communicator->recvStringFromJava(); 
    break; 
case NetservNslpCommunicator::tg_remove_message: 
    break; 
case NetservNslpCommunicator::tg_probe_message: 
{ 
    DLog(param.name, "Reading probe codes list."); 
    short probe = 0; 
    do { 
     probe = communicator->recvShortFromJava(); 
     DLog(param.name,"Probe Code " << probe); 
     data->probes.push_back(probe); 
    } while (probe != 0); 
    data->probes.pop_back(); //delete the last 0 
    if (data->probes.empty()) { 
     logSocketError(sockfd, "Probe list is empty!"); 
     return; 
    } 
    break; 
} 
case NetservNslpCommunicator::tg_trigger_message: 
    data->triggerType = communicator->recvInt(); 

    switch (data->triggerType){ 
    case NETSERV_MESSAGETYPE_SETUP: 
     data->urlList.push_back(communicator->recvString()); 
     data->lifetime = communicator->recvInt(); 
     data->setupParams = communicator->recvString(); 
     break; 
    case NETSERV_MESSAGETYPE_REMOVE: 
     break; 
    case NETSERV_MESSAGETYPE_PROBE: 
    { 
     short probe = 0; 
     do { 
      probe = communicator->recvShortFromJava(); 
      data->probes.push_back(probe); 
     } while (probe != 0); 
     data->probes.pop_back(); //delete the last 0 
     break; 
    } 
    default: 
     ERRLog(param.name, "Trigger type not supported"); 
     closeSocket(sockfd); 
     return; 
    } 
    break; 
    default: 
     logSocketError(sockfd, "Trigger type not supported!"); 
     return; 
} 
DLog(param.name,"Reading optional parameters."); 
// Optional parameters passing 
bool addParam = 0; 
addParam = communicator->recvIntFromJava(); 
if (addParam) { 
    data->signature = communicator->recvStringFromJava(); 
    if (data->signature == NULL) { 
     logSocketError(sockfd, "signature"); 
     // free up the memory allocated 
     delete data; 
     return; 
    } 
    DLog(param.name, "Message signature : "<< data->signature); 
} 

addParam = communicator->recvIntFromJava(); 
if (addParam) { 
    data->depList.push_back(communicator->recvStringFromJava()); 
    if (data->depList.front() == NULL) { 
     logSocketError(sockfd, "dependency list"); 
     // free up the memory allocated 
     delete data; 
     return; 
    } 
    DLog(param.name, "Message dependency list : "<< data->depList.front()); 
} 

addParam = communicator->recvIntFromJava(); 
if (addParam) { 
    data->notification = communicator->recvStringFromJava(); 
    if (data->notification == NULL) { 
     logSocketError(sockfd, "notification"); 
     // free up the memory allocated 
     delete data; 
     return; 
    } 
    DLog(param.name, "Message notification : "<< data->notification); 
} 

addParam = communicator->recvIntFromJava(); 
if (addParam) { 
    data->node = communicator->recvStringFromJava(); 
    if (data->node == NULL) { 
     logSocketError(sockfd, "node"); 
     // free up the memory allocated 
     delete data; 
     return; 
    } 
    DLog(param.name, "Node destination : "<< data->node); 
} 
[...] 

通信器包裝套接字,並使用標準要求寫入和讀取類型:

int NetservNslpCommunicator::recvCommandFromJava(NetservNslpCommunicator::command * cmd){ 
    int code = recvIntFromJava(); 
    cout<<"received int "<<code<<endl; 
    if(code>=0){ 
     switch(code){ 
     case 0: 
      *cmd=NetservNslpCommunicator::tg_setup_message; 
      break; 
     case 1: 
      *cmd=NetservNslpCommunicator::tg_remove_message; 
      break; 
     case 2: 
      *cmd=NetservNslpCommunicator::tg_trigger_message; 
      break; 
     case 3: 
      *cmd=NetservNslpCommunicator::tg_probe_message; 
      break; 
     } 
    } 
    return code; 
} 

int NetservNslpCommunicator::recvIPFromJava(protlib::hostaddress * addr){ 
    cout<<"receiving an IP"<<endl; 
    char* str = recvStringFromJava(); 
    cout<<"String received "<< str << endl; 
    addr->set_ipv4(str); 
    return 1; 
} 

char * NetservNslpCommunicator::recvStringFromJava(){ 
    short length = recvShortFromJava(); 
    cout<< "receiving a string..."<<endl<<"String length "<<length<<endl; 
    char * string = new char[length]; 
    int r = 0; 
    int orLength=length; 
    while(length) 
     { 
      int r = recv(sock, string, length, 0); 
      if(r <= 0) 
       break; // Socket closed or an error occurred 
      length -= r; 
     } 
    string[orLength]='\0'; 

    if(orLength==0) 
     return NULL; 
    else 
     return string; 
} 

int NetservNslpCommunicator::recvIntFromJava(){ 
    int x = 0; 
    recvBuffer(sock, &x, 4); 
    return x; 
} 

short NetservNslpCommunicator::recvShortFromJava() 
{ 
    short x = 0; 
    recvBuffer(sock, &x, 2); 
    return x; 
} 


int NetservNslpCommunicator::recvBuffer(int sock, void * buf, size_t size) 
{ 
    int counter = 0; 
    // Create a pollfd struct for use in the mainloop 
    struct pollfd poll_fd; 
    poll_fd.fd = sock; 
    poll_fd.events = POLLIN | POLLPRI; 
    poll_fd.revents = 0; 

    int r; 
    while (size && !stop) 
    { 
     /* Non-blocking behavior */ 
     // wait on number_poll_sockets for the events specified above for sleep_time (in ms) 
     int poll_status = poll(&poll_fd, 1/*Number of poll socket*/, 100); 
     if (poll_fd.revents & POLLERR) // Error condition 
     { 
      if (errno != EINTR) 
       cout << "NetservNslpCommunicator : " << "Poll caused error " << strerror(errno) << " - indicated by revents" << endl; 
      else 
       cout << "NetservNslpCommunicator : " << "poll(): " << strerror(errno) << endl; 
     } 
     //ignore hangups when reading from a socket 
     if (poll_fd.revents & POLLHUP) // Hung up 
     { 
      cout << "NetservNslpCommunicator : " << "Poll hung up" << endl; 
      //   return -1; 
     } 
     if (poll_fd.revents & POLLNVAL) // Invalid request: fd not open 
     { 
      cout << "NetservNslpCommunicator : " << "Poll Invalid request: fd not open" << endl; 
      return -1; 
     } 

     switch (poll_status) 
     { 
     case -1: 
      if (errno != EINTR) 
       cout << "NetservNslpCommunicator : " << "Poll status indicates error: " << strerror(errno) << endl; 
      else 
       cout << "NetservNslpCommunicator : " << "Poll status: " << str error(errno) << endl; 
      break; 

     case 0: 

      if (isTriggerTimerEnabled){ 
       counter++; 
       if (counter == triggerTimerValue){ 
        isTriggerTimerEnabled = false; 
        return -1; 
       } 
      } 
      continue; 
      break; 

     default: 
      r = recv(sock, buf, size, 0); 
      if (r <= 0) 
      { 
       if (r == 0) { // connection closed 
        r = -1; // return an error if socket closes 
        cout << "NetservNslpCommunicator : " << "No data received from socket!" << endl; 
        stop=true; 
        break; 
       } 
       if (r == -1 && errno == EINTR) // received interrupt during recv, continuing 
        continue; 
       if (r == -1 && errno != EINTR) // socket error, raise exception 
        break; 
      } 

      if (r != -1) 
       size -= r; 
      break; 
     } 
    } 

    counter = 0; 
    isTriggerTimerEnabled = false; 
    return r; 
} 

我請你只注重tg_probe_message一部分。其他消息仍然需要執行。

奇怪的行爲是:第一次客戶端發送到服務器的所有請求順利的話,所有的值是完全讀取。因此服務器應答回送一些整數和一串字符串。 這是我的插座上捕獲跟蹤(應用層每行僅一個TCP數據包。):

00 // 
00 // 
00 // 
03 // First integer 

00 // 
0a // Short representing string length 

31:37:32:2e:31:36:2e:33:2e:32 //the string: "172.16.3.2" 

00 
00 
00 
01 

00 
00 
00 
00 

00 
1b 

4e:65:74:53:65:72:76:2e:61:70:70:73:2e:4c:6f:69:62:46:61:6b:65:5f:30:2e:30:2e:31 //The string "NetServ.apps.LoibFake_0.0.1" 

00 
03 

6a:61:65 //the string "jae" 

00 
00 
00 
03 

00 
00 
00 
01 

00 
01 

31 //the string "1" 

00 
02 

00 
00 

00 // 
00 // 
00 // 4 times 
00 // 

服務器答案:

00:00:00:01 //response code 
00:00:00:02 //response type 
00:00:00:04 //number of strings to read 
00:12 //str length 
31:30:2e:31:30:2e:30:2e:35:20:41:43:54:49:56:45:20:31 
00:12 //str length 
31:30:2e:31:30:2e:30:2e:34:20:41:43:54:49:56:45:20:31 
00:12 //str length 
31:30:2e:31:30:2e:30:2e:33:20:41:43:54:49:56:45:20:32 
00:12 //str length 
31:30:2e:31:30:2e:30:2e:36:20:41:43:54:49:56:45:20:32 

第二時間的客戶端發送請求(相同的請求)發生了一些奇怪的事情。這是我第二個連接過程中使用tcpdump捕獲:

00 // 
00 // 
00 // 
03 // First integer 

00 // 
0a // Short representing string length 

31:37:32:2e:31:36:2e:33:2e:32 //the string: "172.16.3.2" 

00 
00 
00 
01 

00 
00 
00 
00 

00:1b:4e:65:74:53:65:72:76:2e:61:70:70:73:2e:4c:6f:69:62:46:61:6b:65:5f:30:2e:30:2e:31:00:03:6a:61:65:00:00:00:03:00:00:00:01:00:01:31:00:02:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 

有一點耐心,你能夠認識到的最後一個包包含請求(第一個請求的相同位)的所有位。

對於一些調試,我可以看到命令communicator->recvCommandFromJava(&cmd)返回數字50331648(03:00:00:00)而不是3(00:00:00:03),並且當執行命令communicator->recvIPFromJava(&(data->destAddr))時調用recvStringFromJava(),它使用recvShortFromJava(),表示字符串長度00 :0A(10)被交換到小端0A:00(2560)。我相信這會導致tcp將所有可用的數據放入下一個數據包中,並破壞後續的調用。

正如你從代碼中可以看到的,我沒有在服務器中採用從主機命令到網絡命令的轉換(這是因爲它對第一個請求工作正常),但似乎需要進行轉換第二個請求。 DataOutputStream上的文檔指定intshort是用big-endian編寫的。服務器不適用轉換。

因此,最後,這是一個問題: C++是否有可能在執行期間更改主機格式? 這怎麼可能發生? 我可以做些什麼來預測java客戶端和c + +服務器之間的字節順序行爲?

回答

0

我找到了解決我的問題的方法,我認爲它夠高雅。 由於我無法預測服務器讀取大於一個字節的原始類型時的行爲,因此我使用標準合同機制。 每次客戶端想要將命令推送到服務器時,它都會發送一個已知的Integer代碼。服務器讀取整數並檢查該值是否等於預定義的Integer,比它可以讀取所有值而不重新排序它們。否則,它將設置一個標誌,並將讀取所有隨後使用函數ntohl()和ntohs()交換它們的值。

0

Endian-ness與將數據放入下一個數據包沒什麼關係。這只是因爲它是一個字節流協議。

你有兩個獨立的問題解決:一個再用ntohl有()和朋友,其他的通過繼續閱讀,直到你擁有所有你期待中的數據。

+0

我明白你的觀點。沒關係,但最後一個數據包包含所有數據的事實是由於服務器讀取_short_以瞭解後續字符串的長度。 NTOHL()問題交換了這個短的字節,以便服務器嘗試讀取2560個字節而不是10個。這會觸發TCP堆棧將數據打包到單個數據包中,就像處理其他字符串一樣。至少這是我給第二個問題的解釋。所以我堅信他們是有聯繫的。你是否同意我的觀點? – 2014-09-25 13:28:21

+0

我重複它儘可能清楚。 ntohl和ntohs問題僅在第二次請求期間發生。 – 2014-09-25 13:56:18