通讯中的字节网络顺序和字节主机顺序

http://blog.csdn.net/sergeycao/archive/2009/02/24/3933149.aspx

 

 

C/C++写网络程序的时候,往往会遇到字节的网络顺序和主机顺序的问题。

 
其实数据的顺序是由 cpu 决定的 , 与操作系统无关。 
Intel   x86 结构下 ,short 型数 0x1234 表示为 34   12,int 型数 0x12345678 表示为 78   56   34   12  
IBM   power PC 结构下 ,short 型数 0x1234 表示为 12   34,int 型数 0x12345678 表示为 12   34   56   78
  
由于这个原因不同的机器之间无法通信 , 所以要转换成一种约定的数序 , 也就是网络字节顺序 , 其实就是如同 power   pc 那样的顺序 
PC 开发中有 ntohl htonl 函数可以用来进行网络字节和主机字节的转换,但是 Symbian 开发中没有这两个函数,那就要自己写接口来进行转换了。
下面是两个进行转换的接口:
 
// 主机顺序转换成网络顺序 网络顺序转换成主机顺序
inline   unsigned   long   HTONL(unsigned   long   h)  
    {  
      return   (h>>24)+((h>>16)<<8)+((h>>8)<<16)+(h<<24);  
    }
 
// 主机顺序转换成网络顺序 网络顺序转换成主机顺序  
    inline   unsigned   short   HTONS(unsigned   short   h)  
    {  
      return   (h>>8)+(h<<8);  
    }
 
 
这些问题在 Java 做为 Server 端, Symbian 做为 Client 端时表现的更为明显,因为 Java 中的通讯传输的都是网络字节。到了 Symbian 端要转换成主机字节。
比如你要发送一个结构  
  struct test{  
    short   a;  
    int      b;
    long   c;  
    float   d;   
    double  f;  
  };  
  test st;  
  char *p   =   (char*)&st;//
看看 p 中的字节顺序(就是发送的字节顺序)  
   
  java
端你相应写些函数进行转换就行了。  
 
举一个例子:  
      //
c 对应的 ntohl 函数  
      public   static   long   ntohl(long   in){  
          long   out   =   0;  
          out     =   (in&0xff)<<24;    
          out   |=   (in&0xff00)<<8;  
          out   |=   (in&0xff0000)>>8;  
          out   |=   (in&0xff000000)>>24;  
          return   out;  
      }
 
下面再转几篇不错的文章:
原文地址:http://blog.csdn.net/kingfish/archive/2005/03/29/333635.aspx

近几天看到csdn上问c/c++和java通信的问题比较多,特别是c特有的数据结构(如struct)。

特地根据网友的一个问题举个例子,希望对初学者有所帮助。

原问题见:http://community.csdn.net/Expert/topic/3886/3886989.xml?temp=.3527033

这类问题通常是为了利用原有Server或者Server不能做修改(通常是c/c++)造成。

比如Server端只接收一个结构Employee,定义如下:

struct UserInfo {
               char UserName[20];
               int UserId;
        };
       struct Employee {
               UserInfo user;
               float salary;
      };
当然也可以定义为

struct Employee {
              char name[20];
              int    id;
            float salary;
};

java client 测试源码(为说明问题,假设struct字节对齐,sizeof(Employee)=28)

import java.net.*;

/*
          * 与C语言通信(java做Client,c/c++做Server,传送一个结构)
          * @author kingfish
          * @version 1.0
       */
class Employee {
  private byte[] buf = new byte[28];  //为说明问题,定死大小,事件中可以灵活处理

  /*
           * 将int转为低字节在前,高字节在后的byte数组
         */
  private static byte[] toLH(int n) {
    byte[] b = new byte[4];
    b[0] = (byte) (n & 0xff);
    b[1] = (byte) (n >> 8 & 0xff);
    b[2] = (byte) (n >> 16 & 0xff);
    b[3] = (byte) (n >> 24 & 0xff);
    return b;
  }

  /*
           * 将float转为低字节在前,高字节在后的byte数组
         */
  private static byte[] toLH(float f) {
    return toLH(Float.floatToRawIntBits(f));
  }

  /*
           * 构造并转换
         */
  public Employee(String name, int id, float salary) {
    byte[] temp = name.getBytes();
    System.arraycopy(temp, 0, buf, 0, temp.length);

    temp = toLH(id);
    System.arraycopy(temp, 0, buf, 20, temp.length);

    temp = toLH(salary);
    System.arraycopy(temp, 0, buf, 24, temp.length);
  }

  /**
   * 返回要发送的数组
   */
  public byte[] getBuf() {
    return buf;
  }

  /**
   * 发送测试
   */
  public static void main(String[] args) {
    try {
      Socket sock = new Socket("127.0.0.1", 8888);
      sock.getOutputStream().write(new Employee("kingfish", 123456789, 8888.99f).
                                   getBuf());
      sock.close();
    }
    catch (Exception e) {
      e.printStackTrace();
    }

} //end

当然,也可以利用writeInt,writeFloat方法发送,但字节顺序需要改为低在前。
这个问题稍后在讨论。

第一部分请见http://blog.csdn.net/kingfish/archive/2005/03/29/333635.aspx

本部分提出另外一种做法, 供参考。


import java.net.*;
import java.io.*;

/**
 * 与C语言通信(java做Client,c/c++做Server,传送一个结构)
 * @author kingfish
 * @version 1.0
 */
public class Employee2 {
  private String name;
  private int id;
  private float salary;

  /**
   * 将int转为低字节在前,高字节在后的int
   */
  private static int toLH(int in) {
    int out = 0;
    out = (in & 0xff) << 24;
    out |= (in & 0xff00) << 8;
    out |= (in & 0xff0000) >> 8;
    out |= (in & 0xff000000) >> 24;
    return out;
  }

  /**
   * 将float转为低字节在前,高字节在后的int
   */
  private static int toLH(float f) {
    return toLH(Float.floatToRawIntBits(f));
  }

  /**
   * 构造并转换
   */
  public Employee2(String name, int id, float salary) {
    this.name = name;
    this.id = id;
    this.salary = salary;
  }

  /**
   * 取得名字,定长byte数组
   */
  public byte[] getName() {
    byte[] b = new byte[20];
    System.arraycopy(name.getBytes(), 0, b, 0, name.getBytes().length);
    return b;
  }

  /**
   * 取得编号(低字节在前)
   */
  public int getId() {
    return toLH(id);
  }

  /**
   * 取得工资(低字节在前)
   */
  public int getSalary() {
    return toLH(salary);
  }

  /**
   * 发送测试
   */
  public static void main(String[] args) {
    try {
      Employee2 p = new Employee2("kingfish", 123456789, 8888.99f);

      Socket sock = new Socket("127.0.0.1", 8888);
      DataOutputStream dos = new DataOutputStream(sock.getOutputStream());
      dos.write(p.getName());
      dos.writeInt(p.getId());
      dos.writeInt(p.getSalary());

      sock.close();
    }
    catch (Exception e) {
      e.printStackTrace();
    }
  }
} //end

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值