Java和C套接字编程之间的结构化数据通信

时间:2014-11-20 14:42:03

标签: java c sockets java-ee

我有一个Java套接字通道,我发送对象数据并在C套接字中接收它。

Java Code ::

//structure
class data
{
       public String jobtype;
       public String budget;
       public String time ;
}
//creating a Socket Channel and sending data through it in java

Selector incomingMessageSelector = Selector.open();
SocketChannel  sChannel = SocketChannel.open();           
sChannel.configureBlocking(false);
sChannel.connect(new InetSocketAddress("localhost", 5000));
sChannel.register(incomingMessageSelector, SelectionKey.OP_CONNECT);

if(sChannel.finishConnect()==true)
{
     sChannel.register(incomingMessageSelector, SelectionKey.OP_WRITE);
}
int len = 256;
ByteBuffer buf = ByteBuffer.allocate(len);
buf.putInt(len); 
// Writing object of data in socket
buf.put(obj.jobtype.getBytes("US-ASCII"));
buf.put(obj.budget.getBytes("US-ASCII"));
buf.put(obj.time.getBytes("US-ASCII"));
buf.put((byte) 0);
buf.flip();
sChannel.write(buf);

C Code ::

struct data
{
    char time[50];
    char jobtype[50];
    char budget[50];
};

n = read(newsockfd, &size, sizeof(size));
struct data *result = malloc(size);
n = read(newsockfd, result, size);

printf("\njobtype :: %s\nbudget :: %s\ntime :: %s\n",result->jobtype,result->budget,result->time);

在Java中输入后:

jobtype = h1
budget = 20
time = 12

我在C中获得这些输出:

jobtype :: 
budget :: 
time :: h1

1 个答案:

答案 0 :(得分:2)

从Java发送到C的缓冲区需要在两种语言中具有完全相同的定义(从字节的角度来看)。在你的代码中并非如此。您在Java中构造的缓冲区与您在C中用于解释该缓冲区的struct格式不同。字符串的长度和字符串的顺序在发送方(Java)和接收方(C)之间都不匹配。此外,发送的缓冲区大小与根据发送的长度信息预期的缓冲区大小不匹配(即,您没有发送正确长度的缓冲区)。

在C中,您定义了一个150字节长的结构,其中包含3个char数组(字符串),每个字节长50个字节。订单:timejobtypebudget

在Java中,您创建了一个可变长度的缓冲区,其中包含可变长度的字符串,顺序为:jobtypebudgettime。从根本上说,Java代码正在创建一个可变长度的缓冲区,其中C代码期望将其映射到固定长度的结构。

虽然它不是您想要的,但您的C程序正在获取您首先放在缓冲区中的jobtype字符串,并将其分配给time。这就是目前的写作方式。

假设您使C程序保持不变,那么创建和填充缓冲区的Java代码部分可能类似于:

public ByteBuffer createFixedLengthCString(String src, int len) {
    //If the string is longer than len-1 it is truncated.
    ByteBuffer cString = ByteBuffer.allocate(len);
    if(src.length() > len - 1) {
        //Using len-1 prevents the last 0 in the ByteBuffer from being
        //  overwritten. A final 0 is needed:C uses null (0) terminated strings.
        cString.put(src.getBytes("US-ASCII"), 0, len-1);
    } else {
        //The string is not longer than the maximum length.
        cString.put(src.getBytes("US-ASCII"));
    }
    //Already have null termination. Do not want to flip (would change length).
    //Reset the position to 0.
    cString.position(0);
    return cString;
}

int maxBufLen = 256;
int payloadLen = 150
int cStringLen = 50;

ByteBuffer buf = ByteBuffer.allocate(maxBufLen);
//Tell C that the payload is 150 bytes long.
buf.putInt(payloadLen); 

// Writing object data in the buffer
buf.put(createFixedLengthCString(obj.time,    cStringLen));
buf.put(createFixedLengthCString(obj.jobtype, cStringLen));
buf.put(createFixedLengthCString(obj.budget,  cStringLen));

//Use flip() here as it changes the length of bytes sent to the correct
//  number (an int plus 150) and sets the position to 0, ready for reading.
buf.flip();
while(buf.hasRemaining()) {
    //There is the possibility that a single call to write() will not
    //  write the entire buffer. Thus, loop until all data is written.
    //There should be other conditions which cause us to break out of
    //  this loop (e.g. a maximum number of write attempts). Without such,
    //  if the channel is hung this is code will hang in this loop; effectively
    //  a blocking (for this code) write loop.
    sChannel.write(buf);
}

此答案仅用于解决您在问题中发现的特定故障。但是,所提供的代码实际上仅适合作为在同一台机器上将有限数据从一个进程传输到另一个进程的示例/测试。即使这样,也应该有异常和错误处理,这里不包括。

正如EJP在他的评论中暗示的那样,在通过位管道进行通信时,通常更好/更容易地使用现有协议。这些协议旨在解决许多可能变得相关的不同问题,即使在简单的inter-process communications中也是如此。