TCP套接字连接Java Client <-> C ++服务器中的意外行为

达里奥·瓦洛基(Dario Valocchi)

我在板上冲浪了很多有关tcp套接字,big-endian和little-endian格式的问题,但对我而言,这并没有什么适合的。

对不起我的英语不好,我正在努力:)

我对简单的客户端-服务器配置中的意外行为不满意。这是场景:

服务器(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通信的旧代码。以下是相关代码:

[...]
// 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)short被交换为little-endian 0a:00(2560)。我相信这会导致tcp将所有可用数据放入下一个数据包,并破坏后续的调用。

从代码中可以看到,我没有在服务器中采用从主机顺序到网络顺序的转换(这是因为它在第一个请求中工作正常),但是似乎在第二个请求中需要转换。DataOutputStream上的文档指定intshort用big-endian编写。服务器不应用转换。

因此,最后是一个问题:C ++是否有可能在执行过程中更改主机格式?这怎么可能发生?如何在Java客户端和C ++服务器之间的字节顺序上具有可预测的行为?

达里奥·瓦洛基(Dario Valocchi)

我找到了解决我的问题的解决方案,并且我认为这足够优雅。因为当服务器读取大于一个字节的原始类型时,我无法预测服务器的行为,所以我使用标准的合同机制。每当客户端希望将命令推送到服务器时,它都会发送一个已知的Integer代码。服务器读取整数并检查该值是否等于预定的整数,这样服务器就可以读取所有值而无需重新排序。否则,它将设置一个标志并将读取所有后续值,并将它们与函数ntohl()和ntohs()交换。

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

C#TCP套接字服务器客户端连接

来自分类Dev

客户端断开连接后,C 套接字 TCP 服务器出现故障

来自分类Dev

C#Xamarin UWP / Android服务器-客户端套接字TCP无法正确连接

来自分类Dev

不同服务器中的套接字TCP连接

来自分类Dev

C TCP / IP服务器绑定错误-非套接字上的套接字操作

来自分类Dev

通过 C/C++ 中的阻塞/非阻塞 TCP 套接字连接的超时问题

来自分类Dev

服务器可以在c中打开两个TCP套接字吗?

来自分类Dev

TCP服务器在C中,套接字仍然为负

来自分类Dev

PHP客户端套接字与C#套接字服务器的连接

来自分类Dev

套接字IO-服务器未与我的React Client连接

来自分类Dev

尝试将数据写入客户端时,C#中的简单套接字服务器意外关闭连接

来自分类Dev

尝试将数据写入客户端时,C#中的简单套接字服务器意外关闭连接

来自分类Dev

在C中运行客户端和服务器套接字连接-带线程

来自分类Dev

通过C中的套接字到服务器的多个连接

来自分类Dev

为 TCP 套接字服务器编写代码时,用于多个异步连接的最佳库 (C/C++) 是什么?

来自分类Dev

如何从Android Tcp客户端套接字连接到具有公共IP的Java TCP服务器套接字?

来自分类Dev

Java(客户端)和C#(服务器)TCP套接字。和服务器从客户端读取无限的最后数据

来自分类Dev

将输入从C套接字客户端读取到Java套接字服务器

来自分类Dev

无法从Java套接字服务器接收数据到C套接字客户端

来自分类Dev

将输入从C套接字客户端读取到Java套接字服务器

来自分类Dev

iOS:在套接字中读写(TCP连接)

来自分类Dev

C中的队列套接字连接

来自分类Dev

在C中的套接字服务器中,缓慢的accept()

来自分类Dev

用于 C# 中套接字标识符的 TCP 套接字服务器自定义类

来自分类Dev

通过套接字将Java客户端中的int写入c服务器

来自分类Dev

C 中的套接字编程,服务器代码出错

来自分类Dev

Java TCP 客户端/服务器套接字

来自分类Dev

C ++:接收SIGKILL时如何关闭TCP套接字(服务器)

来自分类Dev

在C ++中使用TCP套接字进行远程客户端和服务器通信

Related 相关文章

  1. 1

    C#TCP套接字服务器客户端连接

  2. 2

    客户端断开连接后,C 套接字 TCP 服务器出现故障

  3. 3

    C#Xamarin UWP / Android服务器-客户端套接字TCP无法正确连接

  4. 4

    不同服务器中的套接字TCP连接

  5. 5

    C TCP / IP服务器绑定错误-非套接字上的套接字操作

  6. 6

    通过 C/C++ 中的阻塞/非阻塞 TCP 套接字连接的超时问题

  7. 7

    服务器可以在c中打开两个TCP套接字吗?

  8. 8

    TCP服务器在C中,套接字仍然为负

  9. 9

    PHP客户端套接字与C#套接字服务器的连接

  10. 10

    套接字IO-服务器未与我的React Client连接

  11. 11

    尝试将数据写入客户端时,C#中的简单套接字服务器意外关闭连接

  12. 12

    尝试将数据写入客户端时,C#中的简单套接字服务器意外关闭连接

  13. 13

    在C中运行客户端和服务器套接字连接-带线程

  14. 14

    通过C中的套接字到服务器的多个连接

  15. 15

    为 TCP 套接字服务器编写代码时,用于多个异步连接的最佳库 (C/C++) 是什么?

  16. 16

    如何从Android Tcp客户端套接字连接到具有公共IP的Java TCP服务器套接字?

  17. 17

    Java(客户端)和C#(服务器)TCP套接字。和服务器从客户端读取无限的最后数据

  18. 18

    将输入从C套接字客户端读取到Java套接字服务器

  19. 19

    无法从Java套接字服务器接收数据到C套接字客户端

  20. 20

    将输入从C套接字客户端读取到Java套接字服务器

  21. 21

    iOS:在套接字中读写(TCP连接)

  22. 22

    C中的队列套接字连接

  23. 23

    在C中的套接字服务器中,缓慢的accept()

  24. 24

    用于 C# 中套接字标识符的 TCP 套接字服务器自定义类

  25. 25

    通过套接字将Java客户端中的int写入c服务器

  26. 26

    C 中的套接字编程,服务器代码出错

  27. 27

    Java TCP 客户端/服务器套接字

  28. 28

    C ++:接收SIGKILL时如何关闭TCP套接字(服务器)

  29. 29

    在C ++中使用TCP套接字进行远程客户端和服务器通信

热门标签

归档