j2me用socket模拟http做无线网络传输

j2me用socket模拟http做无线网络传输

程序感悟 2007-09-15 16:35:21 阅读157 评论0字号:

博客注册了好久,不知道有什么可写的,都没动过。一个字也没有写,如果是块铁估计早生锈了。最近听到几个朋友跟我说要我写点东西放到我的这个小窝内。所以趁着今天不上班,免强写点吧(感觉是被逼的,哈哈~~~)。

其实这个是最近才做的。所以记忆深刻,以前没对HTTP协议做过深刻的了解,所以这三个类写了我两个星期。虽然时候是花得多了点,但写出来后还是感觉学到了不少。为了记念我的这第一份J2ME代码,我决定贴在这。有空的时候再来看看。也为我的博客添上第一件家俱。。。。。

首先贴出的是一个基类。从这个基类派生两个类。一个做HTTP连接。另一个做SOCKET模拟HTTP请求服务端数据。其中有我们部份公司的消息处理机制在里面。

package com.eno.kjava.net;

import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;

import javax.microedition.io.HttpConnection;
import javax.microedition.io.StreamConnection;


public class ENONetConnection {
 protected String m_addr;

 protected String OpenUrl;

 protected String OnlineUrl;

 protected int m_port;

 protected boolean m_fKeepAlive;
 private NetNotifier m_notifier;
 private ByteArrayOutputStream m_baos = new ByteArrayOutputStream();

 protected String m_strErrMsg = "";

 protected int m_nContentLength = 0; // 当前需要要接收的数据长度

 protected boolean m_fProxyMode;

 private byte[] m_recvBuff = new byte[2048];

 private int m_recvLen = 0;

 protected StreamConnection m_conn = null;

 protected DataInputStream m_is = null;

 protected boolean isConnected = false;// 连接是否正常

 protected DataOutputStream m_os = null;

 protected static String CMCC_WAPADDR = "10.0.0.172:9201"; // 中国移动的WAP网关

 protected static String CMCC_HTTPPROXY = "10.0.0.172:80"; // 中国移动的HTTP代理网关

 protected static String CMCC_WAPPROXY = "10.0.0.172:8080"; // 中国移动的WAP代理网关

 public ENONetConnection(String addr, int port) {
  m_addr = addr;
  m_port = port;
  m_fKeepAlive = false;
  m_fProxyMode = true;
 }

 public ENONetConnection(String addr, int port, boolean fKeepAlive) {
  m_addr = addr;
  m_port = port;
  m_fKeepAlive = fKeepAlive;
  m_fProxyMode = true;
 }

 public boolean sendRequest(String strURL, byte[] byData) {
  return true;
 }

 public byte[] getResponse() {
  return null;
 }

 /**
  * 从url中取数据,若byData不为空,则采用post方式.
  * 
  * @param strURL
  * @param byData
  * @return
  */
 public byte[] getData(String strURL, byte[] byData) {
  byte[] byRtn = null;
  if (!isConnected()) {
   if (strURL != null && !strURL.equals(""))
    m_addr = m_addr + "/" + strURL;
   connect();
  }
  if (isConnected()) {
   if (sendRequest(strURL, byData)) {
    byRtn = getResponse();
   } else
    setErrMsg("发送服务器失败!");
  } else
   setErrMsg("连接服务器失败!");
  // 当返回错误或非keepAlive方式时,关掉连接.
  if (byRtn == null || !isKeepAlive() || !m_strErrMsg.equals("")) {
   setErrMsg(m_strErrMsg);
   close();
  }
  return byRtn;
 }

 /***************************************************************************
  * 单字节读取 DataInputStream 中的数据
  * 
  * @return 字节数组
  */
 protected byte[] readByteArrayFromStream() {
  m_baos.reset();
  try {
   m_recvLen = 0;
   while (true) {
    // 有些机种不支持批量读取数据,使用单字节读取的方式来进行
    int chr = m_is.read();
    if (chr > -1) {
      m_recvBuff[m_recvLen++] = (byte) chr;
      if (m_recvLen == m_recvBuff.length) {
       m_baos.write(m_recvBuff, 0, m_recvLen);
       m_recvLen = 0;
      }
    } else {
      if (m_recvLen > 0) {
       m_baos.write(m_recvBuff, 0, m_recvLen);
       m_recvLen = 0;
      }
     break;
    }
   }
  } catch (Exception e) {
   m_strErrMsg += "readByteArrayFromStream exception. ";
  }
  return m_baos.toByteArray();
 }

 /**
  * 连接到m_addr,m_port.
  * 
  * @return 成功与否标志?
  */
 public boolean connect() {
  return isConnected;
 }

 /**
  * 关闭连接.
  * 
  */
 public void close() {
  m_fKeepAlive = false;
  if (m_os != null) {
   try {
    m_os.close();
   } catch (Exception allerr) {
   }
   m_os = null;
  }
  if (m_is != null) {
   try {
    m_is.close();
   } catch (Exception allerr) {
   }
   m_is = null;
  }
  if (m_conn != null) {
   try {
    m_conn.close();
//    System.out.println("关闭连接!!!");
   } catch (Exception allerr) {
   }
   m_conn = null;
  }
 }

 /**
  * 判断连接是否正常?
  * 
  * @return 是否正常?
  */
 public boolean isConnected() {

  return isConnected;
 }

 /**
  * 当getData()返回空时,通过getErrMsg()取到错误信息.
  * 
  * @return
  */
 public String getErrMsg() {
  return m_strErrMsg;
 }

 public void setErrMsg(String strErrMsg) {
  m_strErrMsg = strErrMsg;
 }

 /**
  * 设置采用http或cmwap网关代理方式
  * 
  * @param fProxyMode
  */
 public void setProxyMode(boolean fProxyMode) {
  m_fProxyMode = fProxyMode;
 }

 public void ResetURL() {
  // 进来的地址是 http|socket:// ip:prot [test/index.jsp?XXXXXX]
  int nPos = m_addr.indexOf("://");
  if (nPos == -1)
   return;
  else
   nPos += 3;
  int nLength = m_addr.indexOf('/', nPos);
  if (nLength != -1 && nLength <= nPos)
   return;
  OpenUrl = CMCC_HTTPPROXY;
  String ConType = m_addr.substring(0, nPos);
  if (nLength != -1) {
   if (ConType.equals("http://")) {
    if (m_fProxyMode) { // 要OPEN url 和 X-ONLINE-HOST
     OpenUrl = ConType + CMCC_HTTPPROXY
       + m_addr.substring(nLength);
    } else { // 要OPEN url 和 HOST
     OpenUrl = m_addr;
    }
    OnlineUrl = m_addr.substring(nPos, nLength);
   } else if (ConType.equals("socket://")) {
    if (m_fProxyMode) { // 要OPEN url 和 X-ONLINE-HOST
     OpenUrl = ConType + CMCC_HTTPPROXY;
    } else { // 要OPEN url 和 HOST
     OpenUrl = ConType + m_addr.substring(nPos, nLength);
    }
    OnlineUrl = m_addr.substring(nPos, nLength);
   }
  } else // 没有后面的/
  {
   if (m_fProxyMode) { // 要OPEN url 和 X-ONLINE-HOST
    OpenUrl = ConType + CMCC_HTTPPROXY;
   } else { // 要OPEN url 和 HOST
    OpenUrl = ConType + m_addr.substring(nPos);
   }
   // if(ConType.equals("http://")){
   // OpenUrl += "/";
   // }
   OnlineUrl = m_addr.substring(nPos);
  }
 }

 public boolean isKeepAlive() {
  return m_fKeepAlive;
 }

 /***************************************************************************
  * 清空当前连接地址
  * 
  * @param rmUrl
  */
 public void setUrl(String rmUrl) {
  if (m_addr == null || rmUrl == null || m_addr.compareTo(rmUrl) != 0) {
   this.close();
   m_addr = rmUrl;
//   System.out.println("setUrl方法内尝试关闭连接");
  }
 }

 /**
  * Add request properties for the configuration, profiles, and locale of
  * this system.
  */
 protected void setRequestHeaders(HttpConnection c) {
  // String prof = System.getProperty("microedition.profiles");
  // String conf = System.getProperty("microedition.configuration");
  // String ua = "Profile/" + prof + " Configuration/" + conf;
  // Let's impersonate an iMode phone
  String ua = "ENO KJava Client";
  try {
   c.setRequestProperty("User-Agent", ua);
  } catch (Exception e) {
  }
  String locale = System.getProperty("microedition.locale");
  if (locale != null) {
   try {
    c.setRequestProperty("Content-Language", locale);
   } catch (Exception e) {
   }
  }
 }
 /**
  * 设置ENONETConection内的消息机制
  * @param notifier
  */
   public void setNotifier(NetNotifier notifier) {
      m_notifier = notifier;
    }

    public NetNotifier getNotifier() {
      return m_notifier;
    }

  protected void reportNetMsg(String s) {
      if (m_notifier != null)
        m_notifier.onNotify(s);
    }

}

具体实现HTTP连接方式,这个是一般用得最多的实现方式。                不用多说了。

package com.eno.kjava.net;

import javax.microedition.io.Connector;
import javax.microedition.io.HttpConnection;
//import javax.microedition.io.SocketConnection;
import javax.microedition.io.StreamConnection;

//import com.eno.kjava.system.ENOSystem;

public class ENOHttpConn extends ENONetConnection {

 public ENOHttpConn(String addr, int port) {
  super(addr, port);
  m_addr ="http://"+ addr; 
  if(m_port != -1){
   m_addr = m_addr +":"+ port;
  }
 }

 public ENOHttpConn(String addr, int port, boolean fKeepAlive) {
  super(addr, port, fKeepAlive);
 }

 /**
  * 连接到m_addr,m_port.
  * 
  * @return 成功与否标志?
  */
 public boolean connect() {
  setUrl(m_addr);
  if (m_conn == null) {
   try {
    ResetURL();
    if(m_fProxyMode){ 
     m_conn = (StreamConnection)Connector.open(OpenUrl,
       Connector.READ_WRITE, true);
        ((HttpConnection)m_conn).setRequestProperty("X-Online-Host", OnlineUrl); //这个是代理连接的关键所在
    }
    else{
        m_conn = (StreamConnection)Connector.open(OpenUrl,
      Connector.READ_WRITE, true);
    }
       setRequestHeaders((HttpConnection)m_conn); // 其它的Http头
       ((HttpConnection)m_conn).setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
    isConnected = true;
    reportNetMsg("成功建立连接....");
   } catch (Exception err) {
    m_strErrMsg += "open exception. "+err.toString();
    isConnected = false;
   }
  }
  return isConnected;
 }

 /**
  * 已经连接成功,发送请求.
  * 
  * @param strURL
  * @param byData
  * @return
  */
 public boolean sendRequest(String strURL, byte[] byData) {
  boolean sendSuccess = true;
  try {
   if (byData != null){
    ((HttpConnection)m_conn).setRequestMethod(HttpConnection.POST);
    }
   if (m_os == null) {
    m_os = m_conn.openDataOutputStream();
   }
  } catch (Exception e) {
   m_strErrMsg += "openDataOutputStream exception. " + e.toString();
  }
  if (m_os == null)
   sendSuccess = false;
  try {
   if (byData != null)
    m_os.write(byData);
//   m_os.flush();
   reportNetMsg("请求发送完毕....");
  } catch (Exception e) {
   m_strErrMsg += "write exception. " + e.toString();
   
   sendSuccess = false;
  }
  return sendSuccess;
 }

 /**
  * 取服务端响应的数据.
  * 
  * @return
  */
 public byte[] getResponse() {
  byte[] resultData = null;
  // 打开 DataInputStream 对象,进行返回数据的读取
  if (m_is == null) {
   try {
    m_is = m_conn.openDataInputStream();
   } catch (Exception e) {
    m_strErrMsg += "openDataInputStream exception. " + e.toString();
   }
   if (m_is == null)
    return null;
  }
  m_nContentLength = 0;
//  reportNetMsg("接收完成,开始解析数据包。");
  // Only HTTP_OK (200) means the content is returned.
  int respCode = 0;
  try {
   respCode = ((HttpConnection) m_conn).getResponseCode();
  } catch (Exception e) {
   m_strErrMsg += "getResponseCode exception. " + e.toString();
  }
  if (respCode == HttpConnection.HTTP_OK) {
   m_nContentLength = (int) ((HttpConnection) m_conn).getLength();

   if (m_nContentLength > 0) {
    String strReadStream = null;
    if (strReadStream == null) {
     resultData = new byte[(int) m_nContentLength];
     int actual = 0;
     int bytesread = 0;
     int maxPerRead = 256;
     while ((bytesread < m_nContentLength) && (actual != -1)) {
      if ((m_nContentLength - bytesread) > 256)
       maxPerRead = 256;
      else
       maxPerRead = m_nContentLength - bytesread;
      try {
       actual = m_is.read(resultData, bytesread,
         maxPerRead);
      } catch (Exception e) {
       m_strErrMsg += "read exception. " + e.toString();
       break;
      }
      bytesread += actual;
     }
    } else {
     resultData = readByteArrayFromStream();
    }
   } else {
    resultData = readByteArrayFromStream();
   }
  }
  return resultData;
 }
}


下面这个是用socket模拟http发送请求,得到数据。发送的数据用HTTP头包裹出去,得到服务器返回的数据也是带有包头数据的数据包,然后对包进行解析。最后得到要的实际数据。这就是整个流程和思路。。

package com.eno.kjava.net;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
//import java.io.InputStreamReader;

import javax.microedition.io.Connector;
//import javax.microedition.io.HttpConnection;
import javax.microedition.io.SocketConnection;
//import javax.microedition.io.StreamConnection;


public class ENOHttpVSocketConn extends ENONetConnection {
 private byte crlf13 = (byte) 13; // ' '

 private byte crlf10 = (byte) 10; // ' '

// private StringBuffer socketHead = new StringBuffer(); // 用于保存包头内容

 private ByteArrayOutputStream chunkedData = new ByteArrayOutputStream();

 private boolean isEnd = true;// 读完头后,流是否还有数据可读

 private boolean isChunked = false;

 public ENOHttpVSocketConn(String addr, int port) {
  super(addr, port);
   m_addr = "socket://"+addr+":"+port;
  m_fKeepAlive = true;
 }


 public ENOHttpVSocketConn(String addr, int port, boolean fKeepAlive) {
  super(addr, port, fKeepAlive);
  m_fKeepAlive = true;
 }

 /**
  * 已经连接成功,发送请求.
  * 
  * @param strURL
  * @param byData
  * @return
  */
 public boolean sendRequest(String strURL, byte[] byData) {
  boolean sendSuccess = true;

  if (m_os == null) {
   try {
    m_os = m_conn.openDataOutputStream();
   } catch (Exception e) {
    m_strErrMsg += "openDataOutputStream exception. ";
   }
   if (m_os == null)
    sendSuccess = false;
  }
  try {
   // 设置请求头
   String requestHead = "";
   if (byData == null)
    requestHead = "GET /";
   else
    requestHead = "POST /";
   if (strURL != null)
    requestHead += strURL + " HTTP/1.1 ";
   else
    requestHead += " HTTP/1.1 ";

   // Host
   if (m_fProxyMode) {
    requestHead += "X-Online-Host: " + OnlineUrl 
      + " ";
   } else {
    requestHead += "Host: " + OnlineUrl + " ";
   }
   // 加其它的请求头
   requestHead += "User-Agent: ENO KJava Client ";
   requestHead += "Content-Language: "
     + System.getProperty("microedition.locale") + " ";
   requestHead += "Content-Type: application/x-www-form-urlencoded ";
   
   if (byData != null) {
    requestHead += "Content-Length:" + byData.length;
    requestHead += " ";
   }
   // 结束Http Header
   requestHead += " ";
//   System.out.println("请求头:" + requestHead);
   m_os.write(requestHead.getBytes());
   if (byData != null)
    m_os.write(byData);
//   m_os.flush();
   reportNetMsg("请求发送完毕....");
  } catch (Exception e) {
   m_strErrMsg += "write exception. " + e.toString();
   sendSuccess = false;
  }
  return sendSuccess;
 }

 /**
  * 连接到m_addr,m_port.
  * 
  * @return 成功与否标志?
  */
 public boolean connect() {
  setUrl(m_addr);
  if (m_conn == null) {
   try {
    ResetURL();
    m_conn = (SocketConnection) Connector.open(OpenUrl,Connector.READ_WRITE, true);
    ((SocketConnection)m_conn).setSocketOption(SocketConnection.LINGER, 5);//关闭连接前等待的秒数
    if(m_fKeepAlive)
     ((SocketConnection)m_conn).setSocketOption(SocketConnection.KEEPALIVE, 1);//非O为启动长连接属性
    isConnected = true;
    reportNetMsg("成功建立连接....");
   } catch (Exception err) {
    m_strErrMsg += "open exception. " + err.toString();
    isConnected = false;
   }
  } 
  return isConnected;
 }

 /**
  * 取服务端响应的数据.
  * 
  * @return
  */
 public byte[] getResponse() {
  byte[] resultData = null;
  if (m_is == null) {
   try {
    m_is = m_conn.openDataInputStream();
   } catch (Exception e) {
    m_strErrMsg += "openDataInputStream exception. "
      + e.toString();
   }
   if (m_is == null)
    return null;
  }
  m_nContentLength = 0;
  // 开始解析数据包
//  reportNetMsg("接收完成,开始解析数据包。");
  try {
   this.ReadSocketHeader();// 读包头并为 m_nContentLength 赋值。
   if (isEnd)
    return null;
  } catch (Exception e) {
   m_strErrMsg += "ReadSocketHeader exception. " + e.getMessage();
   return null;
  }
  if (m_nContentLength > 0) {
   String strReadStream = null;
   if (strReadStream == null) {
    resultData = new byte[(int) m_nContentLength];
    int actual = 0;
    int bytesread = 0;
    int maxPerRead = 256;
    while ((bytesread < m_nContentLength) && (actual != -1)) {
     if ((m_nContentLength - bytesread) > 256)
      maxPerRead = 256;
     else
      maxPerRead = m_nContentLength - bytesread;
     try {
      actual = m_is.read(resultData, bytesread, maxPerRead);
     } catch (Exception e) {
      m_strErrMsg += "read exception. " + e.getMessage();
      break;
     }
     bytesread += actual;
    }
   } else {
    resultData = readByteArrayFromStream();
   }
  } else if (isChunked) {
   resultData = getchunkedResponse();
  } else {
   resultData = readByteArrayFromStream();
  }
  return resultData;
 }

 /***************************************************************************
  * 这个方法处理返回的是chunked的数据
  * 
  * @return
  */
 public byte[] getchunkedResponse() {
  chunkedData.reset();
  int chunkedSize = 0;
  boolean readFrist = true;// 是不是第一次在包头后读chuck包
  // 循环连续读取chunk包
  do {
   int chunkcrlf;
   int chunkcrlfNum = 0; // 已经连接的回车换行数 crlfNum=4为头部结束
   StringBuffer temp = new StringBuffer();
   try {
    // 从现在起,读chunk包的大小。
    while ((chunkcrlf = m_is.read()) != -1) // 读取头部
    {
     if (chunkcrlf == crlf13 || chunkcrlf == crlf10) {
      chunkcrlfNum++;
     }
     temp = temp.append((char) chunkcrlf); // byte数组相加
     // 去掉HTTP包头后。整个通信chunk包结构是:chunk大小内容包大小内容、、、、、
     if (readFrist == true && chunkcrlfNum == 2) {
      break;
     } else if (readFrist == false && chunkcrlfNum == 4) {
      break;
     }
    }
   } catch (IOException ioe) {
    m_strErrMsg += "Read chunked Data Error!" + ioe.getMessage();
    return null;
   }
   try {
    // 本次包的大小为:(十六进制)
    chunkedSize = Integer.parseInt(temp.toString().trim(), 16);
    // 接着块读取本次包内容
    if (chunkedSize != 0) {
     int readed = 0; // 已经读取数
     int available = 0;// input.available(); //可读数
     while (readed < chunkedSize) {
      available = chunkedSize - readed; // size-readed--剩余数
      if (available > 256)
       available = 256; // size-readed--剩余数
      byte[] buffer = new byte[available];
      int reading = m_is.read(buffer);
      chunkedData.write(buffer, 0, reading);
      readed += reading; // 已读字符
     }
    }
    // 第一个chunk包读完
    readFrist = false;
   } catch (Exception e) {
    m_strErrMsg += "Read chunked Data Error!" + e.getMessage();
    return null;
   }

  } while (chunkedSize != 0);
  // 返回读得的内容
  return chunkedData.toByteArray();
 }

 /***************************************************************************
  * 读取返回的 包头信息。得到包头内 Content-Length的长度 如果包头内没有 Content-Length 的信息。那么
  * DataInputStream 也把头信息读完了。接下来就让readByteArrayFromStream方法读后面的数据
  */
 private void ReadSocketHeader() // 读取头部 并获得大小
 {
  int crlf;
  int crlfNum = 0; // 已经连接的回车换行数 crlfNum=4为头部结束
  StringBuffer socketHead = new StringBuffer(); // 用于保存包头内容
  try {
   while ((crlf = m_is.read()) != -1) // 读取头部
   {
    if (crlf == crlf13 || crlf == crlf10) {
     crlfNum++;
    } else {
     crlfNum = 0;
    } // 不是则清
    socketHead = socketHead.append((char) crlf); // byte数组相加
    if (crlfNum == 4) {
     isEnd = false;// 读完头后还有数据可读。
     break;
    }
   }
  } catch (IOException e) {
   m_strErrMsg += "Read Http Header Error!" + e.getMessage();
   return;
  }

  String tempStr = (new String(socketHead)).toUpperCase();
  String resp = "200 OK";
  int reIndex = tempStr.indexOf(resp);
  if (reIndex == -1) {
   isEnd = true;
   return;
  }
  // 判断包是不是以chunked返回的。Transfer-Encoding: chunked
  String chu = "TRANSFER-ENCODING: CHUNKED";
  int chIndex = tempStr.indexOf(chu);
  if (chIndex != -1) {
   isChunked = true;
   return;
  }
  String ss1 = "CONTENT-LENGTH:";
  int clIndex = tempStr.indexOf(ss1);
  try {
   byte requst[] = tempStr.getBytes();
   if (clIndex != -1) { // 从clIndex+1起至 
    StringBuffer sb = new StringBuffer();

    for (int i = (clIndex + 16);; i++) {
     if (requst[i] != (byte) 13 && requst[i] != (byte) 10) {
      sb.append((char) requst[i]);
     } else
      break;
    }
    m_nContentLength = Integer.parseInt(sb.toString()); // 正式的HTML文件的大小
   }
  } catch (Exception e) {
   m_strErrMsg += "得到content-length时发生异常!" + e.getMessage();
  }
 }
}

具体调用:

  ENONetConnection conn = new ENOHttpConn("wap.guosen.cn",8001);
  conn.setProxyMode(false);
  byte[] byRtn = conn.getData(null,new String("1").getBytes());
  
//  ENONetConnection conn = new ENOHttpVSocketConn("wap.guosen.cn",8001);
//  conn.setProxyMode(false);
//  byte[] byRtn = conn.getData("upload/soft/gettest.jsp?re_length=120",new String("").getBytes());

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值