Java与C socket编程之间的结构数据通信

Java与C socket编程之间的结构数据通信,java,c,sockets,jakarta-ee,Java,C,Sockets,Jakarta Ee,我有一个Java套接字通道,我正在发送一个对象数据并在C套接字中接收它 Java代码:: //structure class data { public String jobtype; public String budget; public String time ; } //creating a Socket Channel and sending data through it in java Selector incomingMessageSe

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

Java代码::

//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代码::

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

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

在C语言中,您定义了一个150字节长的结构,其中包含3个
char
数组(字符串),每个数组长度为50字节。顺序为:
时间
作业类型
预算

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

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

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

public ByteBuffer createFixedLengthCString(字符串src,int len){
//如果字符串长于len-1,则会截断该字符串。
ByteBuffer cString=ByteBuffer.allocate(len);
if(src.length()>len-1){
//使用len-1可防止ByteBuffer中的最后0被删除
//覆盖。需要最后一个0:C使用以null(0)结尾的字符串。
cString.put(src.getBytes(“US-ASCII”)、0、len-1;
}否则{
//字符串长度不超过最大长度。
put(src.getBytes(“US-ASCII”);
}
//已具有空终止符。不希望翻转(将更改长度)。
//将位置重置为0。
cString.位置(0);
返回环;
}
int maxBufLen=256;
int payloadLen=150
int cStringLen=50;
ByteBuffer buf=ByteBuffer.allocate(maxBufLen);
//告诉C有效负载有150字节长。
buf.putInt(有效载荷);
//在缓冲区中写入对象数据
buf.put(createFixedLengthCString(对象时间,cStringLen));
buf.put(createFixedLengthCString(obj.jobtype,cStringLen));
基本投入(createFixedLengthCString(对象预算,cStringLen));
//在此处使用flip(),因为它会更改发送到正确位置的字节长度
//数字(整数加150)并将位置设置为0,准备读取。
buf.flip();
while(buf.haslaining()){
//对write()的单个调用可能不会
//写入整个缓冲区。因此,循环直到写入所有数据。
//应该有其他的条件导致我们脱离困境
//此循环(例如,最大写入尝试次数)。如果没有此循环,
//如果通道挂起,则代码将挂起在此循环中;有效地
//一个阻塞(对于此代码)写入循环。
sChannel.write(buf);
}
此答案仅用于解决您在问题中识别的特定故障。然而,所给出的代码实际上仅适用于在同一台机器上从一个进程向另一个进程传输有限数据的示例/测试。即使如此,这里也不包括异常和错误处理


正如他在评论中所暗示的,当通过比特管道进行通信时,使用现有的协议通常更好/更容易。这些协议旨在解决许多不同的相关问题,即使是简单的。

回答得很好,但使用
flip()
的原因不是因为“它将发送的bid beets的数量更改为正确的数量”,而是因为这是缓冲区API的设计要求。如果没有它,您不仅会发送错误的字节数,还会发送错误的数据。@EJP,谢谢。是的,长度注释是从一个实现中遗留下来的,它更加强调了
flip()
&
position(0)
之间的区别。由于缓冲区长度(限制)更改,我将其与关于在
createFixedLengthCString
中不使用
flip()
的注释结合在一起,并将其保留在中。我不会说
flip()
是“缓冲区API的设计要求”。此时,需要将限制设置为当前位置和
位置(0)
。我不认为使用
flip()
本身就有缓冲区API的固有要求(例如
buf.limit(buf.position());buf.position(0);
可以工作)。不要这样做。不要将结构用作网络协议。使用网络协议作为网络协议。一次发送一个字段的数据,并以相同的方式接收。除了@Makyen提到的实际bug之外,您完全忽略了对齐、填充和endian问题。