Java 与 C socket通信传输结构体消息/Java解析C语言结构体

Linux C与java实现 socket通信,网上部分例子通信消息格式都是传递的字节流消息,直接传递char型数组消息。而在网络协议、通信控制、嵌入式系统、驱动开发等地方,我们经常要传送的不是简单的字节流(char型数组),而是多种数据组合起来的一个整体,其表现形式是一个结构体。这时候传输的内容如果保存在char型数组中,编程复杂,易出错,而且一旦控制方式及通信协议有所变化,程序就要进行非常细致的修改,非常容易出错。这个时候只需要一个结构体就能搞定。当然,如果是都是C语言编写的client和server传输结构体,解析起来很简单。只要两端定义的结构体形式一样,就不会出问题。而Java里面没有结构体,要怎么才能正确解析出来结构体的数组呢?下面直接上代码看看.

下面的代码是我在Linux C测试的,结构体包含了几种基本类型变量,我们用Java解析出来结构体中各个基本类型的数据。

Linux C发送结构体/ client端

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
/**
* Linux C socket UDP通信,传输结构体。2017/8/20
*@author Lian
*/
#include <stdint.h>

typedef struct{
    uint8_t lxx;
    int nodeID;
    int updateFlg;
    uint16_t slot;
    uint32_t slotBitmap;
    float BitMap;
} test_stru,*p_stru;

int main(){
    test_stru test1 = {200,121,127,4,4294967279,5648.525};//结构体初始化
    printf("test_stru size is  %d\n",sizeof(test_stru));
    p_stru p_test = &test1;
    size_t inlen = sizeof(test_stru);
    char outbuf[1024]={};
    size_t outlen = sizeof(outbuf);
    int brdcFd;
    if((brdcFd=socket(PF_INET,SOCK_DGRAM,0))<0){
        printf("socket fail\n");
        return -1;
    }
    int optval =1;
    setsockopt(brdcFd,SOL_SOCKET,SO_BROADCAST|SO_REUSEADDR,&optval,sizeof(int));
    struct sockaddr_in theirAddr;
    memset(&theirAddr,0,sizeof(struct sockaddr_in));
    theirAddr.sin_family =AF_INET;
    theirAddr.sin_addr.s_addr =inet_addr("192.168.16.36");//设定接收的地址
    theirAddr.sin_port = htons(8887);//设定接收的端口号
    int sendBytes;
     while(1){
        if((sendBytes=sendto(brdcFd,p_test,sizeof(test_stru),0,(struct sockaddr *)&theirAddr,sizeof(struct sockaddr)))==-1){//sendto将结构体发送出去
            printf("sendto fail,errno=%d\n",errno);
            return -1;
        }
        printf("send success!!!\n");
        //printf("msg=%s,msgLen=%d,sendBytes=%d\n",msg,(int)strlen(msg),sendBytes);
        sleep(5);
    }
    close(brdcFd);  
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52

Java server端

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
class Dgram {

    public static DatagramPacket toDatagram(String s, InetAddress destIA,
            int destPort) {
        byte[] buf = new byte[s.length() + 1];
        s.getBytes(0, s.length(), buf, 0);
        return new DatagramPacket(buf, buf.length, destIA, destPort);
    }

    public static String toString(DatagramPacket p) {
        return new String(p.getData(), 0, p.getLength());
    }
}

 class ChatterServer {
    static final int INPORT = 8887;
    private byte[] buf = new byte[1000];
    private DatagramPacket dp = new DatagramPacket(buf, buf.length);
    private DatagramSocket socket;

    public ChatterServer() {
        try {
            socket = new DatagramSocket(INPORT);//
            System.out.println("Server started");
            while (true) {
                socket.receive(dp);

                String rcvd = Dgram.toString(dp) + ",from address:"
                        + dp.getAddress() + ",port:" + dp.getPort();
                System.out.println("From Client:"+rcvd);
                //dp.getData()获取消息内容
                MAC_struct mac_data = new MAC_struct(dp.getData());
                mac_data.parseFrame();
                String echoString = "From Server Echoed:" + rcvd;
                DatagramPacket echo = Dgram.toDatagram(echoString,
                        dp.getAddress(), dp.getPort());

                socket.send(echo);
            }
        } catch (SocketException e) {
            System.err.println("Can't open socket");
            System.exit(1);
        } catch (IOException e) {
            System.err.println("Communication error");
            e.printStackTrace();
        }
    }
 }
class MAC_struct{
    byte[] recv = new byte[2048];
    public int nodeID;
    public int nodeID2;
    public int stampFlags;
    public long tmp;
    public MAC_struct(byte[] recv){
        this.recv = recv;
    }
    /**
    解析收到的结构体消息。
    */
    public void parseFrame(){
        InputStream in_withcode;
        try {
            for(int i=0;i<24;i++)
                System.out.println("source["+i+"]="+recv[i]);
            in_withcode = new ByteArrayInputStream(recv);
            DataInputStream inputStream  =  new DataInputStream(in_withcode);
            int read=0;
            int updateFlg=0;
            int nodeID=0;
            int[] Bits_4 = new int[4];
            read = inputStream.readUnsignedByte();//读取一个无符号字节
            System.out.println("read= "+read);
            inputStream.skipBytes(3);//跳过N个字节
            updateFlg = inputStream.readInt();//readInt()读取一个4个字节。
            System.out.println("updateFlg= "+int_EndianBigtoLittle(updateFlg));
            nodeID = inputStream.readInt();
            System.out.println("nodeID= "+int_EndianBigtoLittle(nodeID));
            int slotBitMap = inputStream.readUnsignedShort();//读取2个无符号字节。
            System.out.println("slotBitMap= "+uShort_EndianBigtoLittle(slotBitMap));
            inputStream.skipBytes(2);
            System.out.println("Long_num= "+Long_EndianBigtoLittle(inputStream.readInt()));
            for(int i=0;i<4;i++)
                Bits_4[i] = inputStream.readUnsignedByte();
            System.out.println("Float_num= "+Float_EndianBigtoLittle(Bits_4));
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    /**
     * 将byte[]数组转为String
     * @param valArr 待转换byte数组,
     * @param startpoint 待转换byte数组开始位置
     * @param maxLen 待转换byte数组长度
     * @return
     */
    private static String toStr(byte[] valArr,int startpoint,int maxLen) {
        int index = 0;
        while(index + startpoint < valArr.length && index < maxLen) {
            if(valArr[index+startpoint] == 0) {
                break;
            }
            index++;
        }
        byte[] temp = new byte[index];
        System.arraycopy(valArr, startpoint, temp, 0, index);
        return new String(temp);
    }
    /**
     * 读取4位,并转化为无符号的4位数据
     * @param value
     * @return 将读取的数据转为小端模式下存储的数据
     */
    private static long Long_EndianBigtoLittle(int value)
    {
        byte[] src = new byte[4];
        src[0] = (byte) ((value>>24)&0xFF);
        src[1] = (byte) ((value>>16)&0xFF);
        src[2] = (byte) ((value>>8)&0xFF);
        src[3] = (byte) ((value)&0xFF);
        return (long) ((src[0]&0xFF)|((src[1]&0xFF)<<8)|((src[2]&0xFF)<<16)|((src[3]&0xFF)<<24))&0xFFFFFFFFl;
    }
    /**
     * 大端数据转小端数据
     * @param value
     * @return 将读取的数据转为小端模式下的数据,
     */
    private static float Float_EndianBigtoLittle(int[] value)
    {
        if (value == null || value.length != 4) {
            throw new IllegalArgumentException("value数组必须不为空,并且是4位!");
        }
        int i = ((value[0]&0xFF)|((value[1]&0xFF)<<8)|((value[2]&0xFF)<<16)|((value[3]&0xFF)<<24));
        return Float.intBitsToFloat(i);
    }
    /**
     * 大端数据转小端数据
     * @param value
     * @return 将读取的数据转为小端模式下的数据,
     */
    private static int int_EndianBigtoLittle(int value)
    {
        byte[] src = new byte[4];
        src[0] = (byte) ((value>>24)&0xFF);
        src[1] = (byte) ((value>>16)&0xFF);
        src[2] = (byte) ((value>>8)&0xFF);
        src[3] = (byte) ((value)&0xFF);
        return (int) ((src[0]&0xFF)|((src[1]&0xFF)<<8)|((src[2]&0xFF)<<16)|((src[3]&0xFF)<<24));
    }
    /**
     * 读物2位数据,转为无符号数据
     * @param value
     * @return 将读取的数据转为小端模式下的数据,
     */
    private static int uShort_EndianBigtoLittle(int value)
    {
        byte[] src = new byte[2];
        src[0] = (byte) ((value>>8)&0xFF);
        src[1] = (byte) ((value)&0xFF);
        return (int) ((src[0]&0xFF)|((src[1]&0xFF)<<8));
    }
    /**
     * 
     */
    public static int vtolh(byte[] bArr) {
        int n = 0;
        for(int i=0;i<bArr.length&&i<4;i++){
            int left = i*8;
            n+= (bArr[i] << left);
        }
        return n;
    }
}
public class JavaCstruct {
    public static void main(String[] args){
        new ChatterServer();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191

上图: 
这里写图片描述 
如图就是解析C语言传递的结构体数据。 
接下来说一下过程,Java server中dp.getData()获取C语言传来的消息内容。将接收到的消息包装成DataInputStream, 接下来就是调用类里面的readXXXX(),获取对应的变量了。这里需要注意的是,要能分析出C语言中,结构体里面的变量在内存里面的存储形式,明白结构体的对齐方式,只有清楚了C的结构体中变量位置,才能正确解析出来里面的数据. 
比如在C语言的client程序中,uint8_t是占一个字节,而第二个变量类型是int占四个字节,因为有字节对齐的存在,这两个变量加一起是占了8字节的空间,而不是紧密排列占了5字节空间,uint8_t虽然占了1字节,但是后面的3字节是空着的没有使用,仍然占着空间。假设uint8_t起始地址是0x0000,则结构体中第一个int变量 nodeID占的起始地址是0x0004.所以我们在Java server端中,读取到第1个无符号变量(inputStream.readUnsignedByte();)后,要跳过3个字节(inputStream.skipBytes(3);),再继续读才是下一个变量的开始位置。 
还有一个就是大小端转换的问题了,如果client端与server端两者之间存储格式不一样,一端是大端模式,一端是小端模式,那读上来的肯定会不同,我在测验时就是client是小端模式,server是大端模式。如果用我的Java代码读取上来的数据与我的结果不一样,有可能就是这个问题。 
当然我们可以从server打印的source[]数组看出端倪。

猜你喜欢

转载自blog.csdn.net/qq_32440951/article/details/81024733