Java 与 C socket通信传输结构体消息/Java解析C语言结构体
来源:互联网 发布:apache forbidden 编辑:程序博客网 时间:2024/05/20 06:53
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); }
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(); }}
上图:
如图就是解析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[]数组看出端倪。
- Java 与 C socket通信传输结构体消息/Java解析C语言结构体
- Linux C socket传输结构体
- C语言socket通信发送结构体的问题
- java和C结构体通信
- java和C结构体通信
- java和C结构体通信
- java和C结构体通信
- 【C语言】C语言结构体解析
- 解析C语言结构体
- C语言结构体解析
- 单片机C语言 串口传输 结构体
- java socket与C语言通信
- java解析C结构体框架
- java接收c语言的结构体
- C语言udp socket发送结构体
- 【C#】[C++] C#和C++结构体Socket通信
- 解析C语言结构体,位段
- 解析C语言结构体,位段。
- 事件处理程序的几种方式总结
- POJ 3461 Oulipo(KMP匹配)
- JAVA互联网架构学习之文件上传与下载
- yum install php-gd 安装php gd库报错Error: php56w-common conflicts with php-common-5.3.3-48.el6_8.x86_64 大
- FNOI_TEST_1 b(高精度除法)
- Java 与 C socket通信传输结构体消息/Java解析C语言结构体
- 欢迎使用CSDN-markdown编辑器
- hdu6128 Inverse of sum 2017多校第七场1009 数学
- HDU 6153 A Secret 经典扩展KMP
- JAVAEE之Spring学习(二)---通过ioc建立mvc模型
- 二分查找
- 8个超震撼的HTML5和纯CSS3动画源码
- 最大的最大公约数(容斥原理)
- 单向链表的基本操作(常见面试题详解)