`
fishermen
  • 浏览: 386012 次
社区版块
存档分类
最新评论

HTTP协议与sockt读取http包

阅读更多
很好的http介绍:)

一、超文本传输协议及HTTP包
    HTTP协议用于在Internet上发送和接收消息。HTTP协议是一种请求-应答式的协议——客户端发送一个请求,服务器返回该请求的应答,所有的请求与应答都是HTTP包。HTTP协议使用可靠的TCP连接,默认端口是80。HTTP的第一个版本是HTTP/0.9,后来发展到了HTTP/1.0,现在最新的版本是HTTP/1.1。HTTP/1.1由RFC 2616 定义。
    在HTTP中,Client/Server之间的会话总是由客户端通过建立连接和发送HTTP请求包初始化,服务器不会主动联系客户端或要求与客户端建立连接。浏览器和服务器都可以随时中断连接,例如,在浏览网页时你可以随时点击“停止”按钮中断当前的文件下载过程,关闭与Web服务器的HTTP连接。
  1 HTTP请求包
  HTTP请求包(GET、POST等请求方法)由三个部分构成,分别是:方法-URI-协议/版本,请求头,请求正文。下面是一个HTTP请求包(GET)的例子:
GET /index.jsp HTTP/1.1
Accept-Language: zh-cn
Connection: Keep-Alive 
Host: 192.168.0.106
Content-Length: 37 
userName=new_andy&password=new_andy

    请求包的第一行是方法-URI-协议/版本:
    GET就是请求方法,根据HTTP标准,HTTP请求可以使用多种请求方法。HTTP 1.1支持七种请求方法:GET、POST、HEAD、OPTIONS、PUT、DELETE和TRACE等,常用的为请求方法是GET和POST。
    /index.jsp表示URI。URI指定了要访问的网络资源。
    HTTP/1.1是协议和协议的版本。
    最后一行userName=new_andy&password=new_andy为正文,正文与HTTP头部有一个空行(\r\n)分隔。这里需要说明的一点,其中Content-Length说明正文的长度,有的正文长度没有在头部说明,只是标明Transfer-Encoding: chunked。关于chunked类型的长度计算方法,见RFC 1626。
    请求包的头部还会包含许多有关客户端环境和请求正文的有用信息,这里不再描述。
  2 HTTP应答包

  和HTTP请求包相似,由三个部分构成,分别是:协议-状态代码-描述,应答头,应答正文。下面是一个HTTP应答的例子:

HTTP/1.1 200 OK
Server: Microsoft-IIS/4.0
Date: Mon, 3 Jan 2005 13:13:33 GMT
Content-Type: text/html
Last-Modified: Mon, 11 Jan 2004 13:23:42 GMT
Content-Length: 90

<html>
<head>
<title>解读HTTP包示例</title></head><body>
Hello WORLD!
</body>
</html>


  HTTP应答包的第一行类似于HTTP请求的第一行,表示所用的协议是HTTP 1.1,服务器处理请求的状态码200。
  应答头也和请求头一样包含许多有用的信息,例如服务器类型、日期时间、内容类型和长度等。应答的正文就是服务器返回的HTML页面。应答头和正文之间也用CRLF分隔。
二、Socket类与ServerSocket类
  在Java中,通信端点由java.net.Socket类(客户端)或java.net.ServerSocket类(服务器端)表示。应用程序通过端点向网络发送或从网络读取数据。位于两台不同机器上的应用软件通过网络连接发送和接收字节流,从而实现通信。要把HTTP包发送给另一个应用,首先要知道对方的IP地址以及其通信端点的端口号。
   Socket类代表的是客户端,它是一个连接远程服务器应用时临时创建的端点。
   ServerSocker类代表的是服务器端,它启动后等待来自客户端的连接请求;一旦接收到请求,ServerSocket创建一个Socket实例来处理与该客户端的通信。对于服务器应用,我们不知道客户端应用什么时候会试图连接服务器,服务器必须一直处于等待连接的状态。

  下面是ServerSocket提供了四个构造函数,常用的构造函数的的一种形式为:
  public ServerSocket(int port, int backLog, InetAddress bindingAddress);
  参数:port指定服务器端监听客户端的端口;
  backlog为连接请求的最大队列长度,一旦超越这个长度,服务器端点开始拒绝客户端的连接请求。
  bindingAddress是一个java.net.InetAddress的实例,指定绑定IP地址。
   创建好ServerSocket实例之后,调用它的accept方法,要求它等待传入的连接请求。只有出现了连接请求时,accept方法才会返回,它的返回值是一个Socket类的实例。随后,这个Socket对象就可以用来与客户端应用通信。
 
  Socket类有许多构造函数,常用的为:
  public Socket(String host, int port)。参数是主机名称(IP地址或域名)和端口号。
   参数host是远程机器的名字或IP地址,port是远程应用的端口号。
  成功创建了Socket类的实例之后,我们就可以用它来发送和接收字节流形式的数据,数据一般为HTTP包。
  
   要发送字节流,首先要调用Socket类的getOutputStream方法获得一个java.io.OutputStream对象;要从连接的另一端接收字节流,首先要调用Socket类的getInputStream方法获得一个java.io.InputStream对象。
  下面的代码片断创建一个与本地HTTP服务器(127.0.0.1代表本地主机的IP地址)通信的Socket,发送一个HTTP请求包,准备接收服务器的应答。
  Socket socket    = new Socket("127.0.0.1", "80");
  OutputStream os  = socket.getOutputStream();
  InputStream  ins = socket.getInputStream();
  StringBuffer sb=new StringBuffer();
  sb.append("GET /index.jsp HTTP/1.1\r\n");//注意\r\n为回车换行
  sb.append("Accept-Language: zh-cn\r\n");
  sb.append("Connection: Keep-Alive\r\n");
  sb.append("Host: 192.168.0.106\r\n");
  sb.append("Content-Length: 37\r\n");
  sb.append("\r\n");
  sb.append("userName=new_andy&password=new_andy\r\n");
  sb.append("\r\n");
 
  //向Web服务器发送一个HTTP请求包
  os.write(sb.toString().getBytes()); 
 
  服务器端的代码在大致结构为:
  while (!shutdown) {
        Socket socket = null;
        try {
            socket = serverSocket.accept(); //等待客户以送HTTP请求包
            // 创建HTTP请求包处理线程
            RequestThread request = new RequestThread(socket);
            request.start();
            if(shutdown) System.exit(0);
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
  RequestThread线程分析HTTP请求包,跟根据请求包内容在服务端生成一个HTTP应答包。下一节说明怎样分析HTTP包。
  InputStream  input = socket.getInputStream();  //从此字节数据流获得HTTP请求包内容
    OutputStream output= socket.getOutputStream(); //向此字节流写入HTTP应答包内容
   
三、读取HTTP包
  以下我自己设计的一个读取HTTP包的类SocketRequest。
  public class SocketRequest {  //从指定的Socket的InputStream中读取数据

  private InputStream  input;
  private String     uri;
  private StringBuffer  request=new StringBuffer();  //用于保存所有内容
  private int       CONTENT_LENGTH=0;  //实际包内容数据长
  private boolean    bePost = false;
  private boolean    beHttpResponse = false;
  private boolean    beChucked = false;
  private boolean    beGet = false;
  private byte       crlf13 = (byte)13; //'\r'
  private byte       crlf10 = (byte)10;  //'\n'

  public SocketRequest(InputStream input) {
    this.input = input;
  }
public SocketRequest(Socket socket) {
    this.input = socket.getInputStream();
  }

  public void ReadData() {  //解析 获得InputStream的数据

  ReadHeader();  //头部

  if(beChucked) //为Chucked
  {
   int ChuckSize=0;
   while((ChuckSize=getChuckSize())>0) //多个Chucked
   {
    readLenData(ChuckSize+2);//读取定长数据
   }
   readLenData(2); //最后的2位 
  }
 
  if(CONTENT_LENGTH>0)
  {
   readLenData(CONTENT_LENGTH);//读取定长数据
  }
 
    uri = "";//parseUri(new String(request));
  }
 
  private void readLenData(int size)  //读取定长数据
  {
   int readed=0;  //已经读取数
   try{
    int available=0;//input.available(); //可读数
    if(available>(size-readed)) available=size-readed;
    while( readed<size )
     {
       while(available==0){  //等到有数据可读
         available = input.available(); //可读数
        }
        if(available>(size-readed)) available= size-readed; //size-readed--剩余数
        if(available>2048) available= 2048; //size-readed--剩余数
       byte[] buffer = new byte[available];
       int reading = input.read(buffer);
       request=request.append(new String(buffer,0,reading));  //byte数组相加
        readed+=reading;  //已读字符
   }
   }catch(IOException e){
     System.out.println("Read readLenData Error!");
   }
}

   private void  ReadHeader() //读取头部 并获得大小
   {
    byte[]  crlf   = new byte[1];
   int     crlfNum= 0;   //已经连接的回车换行数 crlfNum=4为头部结束
    try{
     while( input.read(crlf)!=-1 )   //读取头部
     {
      if(crlf[0]==crlf13 || crlf[0]==crlf10)
      {
         crlfNum++;
      }
      else
      {  crlfNum=0;  } //不是则清
      request=request.append(new String(crlf,0,1));  //byte数组相加
      if(crlfNum==4) break;
     }
   }catch(IOException e){
     System.out.println("Read Http Header Error!");
     return;
    }
  
    String tempStr=(new String(request)).toUpperCase();
   
    //这里我只处理了GET与POST方法
    String  strMethod  = tempStr.substring(0,4);
    if(strMethod.equals("GET ")) //前
    {  beGet=true;   
    }
    else if(strMethod.equals("POST"))
    {
     bePost=true;
     getContentlen_Chucked(tempStr);
    }
    else {
     System.out.println("不支持的HTTP包类型");
    
    } //其它的其它类型 暂不支持
  }

  private void getContentlen_Chucked(String tempStr)  //获得长度 CONTENT-LENGTH 或 是否为CHUNKED型
  {
   String ss1="CONTENT-LENGTH:";
   String ss2=new String("TRANSFER-ENCODING: CHUNKED");
  
   int clIndex   = tempStr.indexOf(ss1);
    int chuckIndex = tempStr.indexOf(ss2);  //为CHUNKED型
    byte requst[]= tempStr.getBytes();
    if(clIndex!=-1)
    { //从clIndex+1起至\r\n
       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;
       }
      
       CONTENT_LENGTH=Integer.parseInt(sb.toString());  //正式的HTML文件的大小
       //System.out.println("CONTENT_LENGTH==  "+CONTENT_LENGTH);
   }
   if(chuckIndex!=-1) beChucked=true;
  }
  
  private int  getChuckSize() //Chuck大小
   {
    byte[]  crlf   = new byte[1];
    StringBuffer  sb1   = new StringBuffer();

    int     crlfNum= 0;   //已经连接的回车换行数 crlfNum=4为头部结束
   
    try{
     while(input.read(crlf)!=-1)   //读取头部
     {
      if(crlf[0]==crlf13 || crlf[0]==crlf10)
      {  crlfNum++; }
      else
      {  crlfNum=0;  } //不是则清
      sb1.append((char)crlf[0]);
      request=request.append(new String(crlf,0,1));  //byte数组相加
      if(crlfNum==2) break;
     }
   }catch(IOException e){
     System.out.println("Read Http Package Error!");
     return 0;
    }
  
   return Integer.parseInt((sb1.toString()).trim(),16); //16进控制
}
  //通过此来进行过滤,是否为发至目标服务器的HTTP包
  private String parseUri(String requestString) {
    int index1, index2;
    index1 = requestString.indexOf(' ');
    if (index1 != -1) {
      index2 = requestString.indexOf(' ', index1 + 1);
      if (index2 > index1)
        return requestString.substring(index1 + 1, index2);
    }
    return null;
  }

  public String getData() {
    return request.toString();
  }
}

使用此类:
SocketRequest request = new SocketRequest(socket); //socket为ServerSocket.accept()返回的Socket实例
request.ReadData();  //读取数据
request.getData();
为什么我要用这么大的力量去读取呢,尤其是在因为Socket连接在发送数据时,由于网络的原因经常会发生延迟现象,可能在服务器端开始接收数据时可能只有部分数据可以从InputStream中获得,在一些地方处理不当时,可能只能获得不完整的数据或是错误的数据。
从InputStream读取字节时有多种办法:
常用int read()与int read(byte[] b)。在用read(byte[])时,程序员经常会犯错误,因为在网络环境中,读取的数据量不一定等于参数的大小。

希望我的这篇文章能给你带来一些帮助。
分享到:
评论
2 楼 fishermen 2008-07-29  
http本来不存在所谓长连接,这仅仅是一种特殊情况下的需求,然后有一些模拟解决技术。
理论上讲,一般有两种解决方案,你已经说了,其中后面那种可以参考开源的comet:pushlet。
在实际使用中,其实还有一些其他的折中方案,如:使用HttpClient作为请求端,然后使用对象池缓存一些请求对象,需要的时候从对象池取;或者直接缓存httpConnection,使用head请求,貌似在一些广告系统中常用。
使用常连接,压力主要在server端,要注意监控和均衡负载。
1 楼 jonson 2008-07-14  
兄台,我想问一句:http协议中 浏览器发送一个 http请求,与服务器建立链接,服务器返回请求结果,服务器结束链接。        这个过程就是普通的请求过程。    

但是如何建立一个所谓的 长链接呢   。现在所谓的commet应用原理如何呢。

如果是用ajax来实现长链接有两种方式:一种是 当ajax处于状态 3时(数据预备状态时)进行传送数据。因为没有转变为 状态4(完成)那么链接将在一段时间内不被释放。  或者使用 轮询发送请求的方式,即当一次请求结束了后,马上发起一次新的请求。   
观察此种种,长链接是模拟出来的一个链接概念。 只要持续的请求数据,是否就可以称之谓长链接

还有一种就是实现一种特殊的服务器,当每次请求结果返回后不结束本次链接。 我想这也是长链接的一种形式吧


看到兄台写的关于HTTP的文章很不错。所以想和你讨论下。

相关推荐

    SNS单模无芯光纤仿真与传感器结构特性分析——基于Rsoft beamprop模块

    内容概要:本文主要探讨了SNS单模无芯光纤的仿真分析及其在通信和传感领域的应用潜力。首先介绍了模间干涉仿真的重要性,利用Rsoft beamprop模块模拟不同模式光在光纤中的传播情况,进而分析光纤的传输性能和模式特性。接着讨论了光纤传输特性的仿真,包括损耗、色散和模式耦合等参数的评估。随后,文章分析了光纤的结构特性,如折射率分布、包层和纤芯直径对性能的影响,并探讨了镀膜技术对光纤性能的提升作用。最后,进行了变形仿真分析,研究外部因素导致的光纤变形对其性能的影响。通过这些分析,为优化光纤设计提供了理论依据。 适合人群:从事光纤通信、光学工程及相关领域的研究人员和技术人员。 使用场景及目标:适用于需要深入了解SNS单模无芯光纤特性和优化设计的研究项目,旨在提高光纤性能并拓展其应用场景。 其他说明:本文不仅提供了详细的仿真方法和技术细节,还对未来的发展方向进行了展望,强调了SNS单模无芯光纤在未来通信和传感领域的重要地位。

    发那科USM通讯程序socket-rece

    发那科USM通讯程序socket-set

    嵌入式八股文面试题库资料知识宝典-WIFI.zip

    嵌入式八股文面试题库资料知识宝典-WIFI.zip

    JS+HTML源码与image

    源码与image

    物流行业车辆路径优化:基于遗传算法和其他优化算法的MATLAB实现及应用

    内容概要:本文详细探讨了物流行业中路径规划与车辆路径优化(VRP)的问题,特别是针对冷链物流、带时间窗的车辆路径优化(VRPTW)、考虑充电桩的车辆路径优化(EVRP)以及多配送中心情况下的路径优化。文中不仅介绍了遗传算法、蚁群算法、粒子群算法等多种优化算法的理论背景,还提供了完整的MATLAB代码及注释,帮助读者理解这些算法的具体实现。此外,文章还讨论了如何通过MATLAB处理大量数据和复杂计算,以得出最优的路径方案。 适合人群:从事物流行业的研究人员和技术人员,尤其是对路径优化感兴趣的开发者和工程师。 使用场景及目标:适用于需要优化车辆路径的企业和个人,旨在提高配送效率、降低成本、确保按时交付货物。通过学习本文提供的算法和代码,读者可以在实际工作中应用这些优化方法,提升物流系统的性能。 其他说明:为了更好地理解和应用这些算法,建议读者参考相关文献和教程进行深入学习。同时,实际应用中还需根据具体情况进行参数调整和优化。

    嵌入式八股文面试题库资料知识宝典-C and C++ normal interview_8.doc.zip

    嵌入式八股文面试题库资料知识宝典-C and C++ normal interview_8.doc.zip

    基于灰狼优化算法的城市路径规划Matlab实现——解决TSP问题

    内容概要:本文介绍了基于灰狼优化算法(GWO)的城市路径规划优化问题(TSP),并通过Matlab实现了该算法。文章详细解释了GWO算法的工作原理,包括寻找猎物、围捕猎物和攻击猎物三个阶段,并提供了具体的代码示例。通过不断迭代优化路径,最终得到最优的城市路径规划方案。与传统TSP求解方法相比,GWO算法具有更好的全局搜索能力和较快的收敛速度,适用于复杂的城市环境。尽管如此,算法在面对大量城市节点时仍面临运算时间和参数设置的挑战。 适合人群:对路径规划、优化算法感兴趣的科研人员、学生以及从事交通规划的专业人士。 使用场景及目标:①研究和开发高效的路径规划算法;②优化城市交通系统,提升出行效率;③探索人工智能在交通领域的应用。 其他说明:文中提到的代码可以作为学习和研究的基础,但实际应用中需要根据具体情况调整算法参数和优化策略。

    嵌入式八股文面试题库资料知识宝典-Intel3.zip

    嵌入式八股文面试题库资料知识宝典-Intel3.zip

    嵌入式八股文面试题库资料知识宝典-2019京东C++.zip

    嵌入式八股文面试题库资料知识宝典-2019京东C++.zip

    嵌入式八股文面试题库资料知识宝典-北京光桥科技有限公司面试题.zip

    嵌入式八股文面试题库资料知识宝典-北京光桥科技有限公司面试题.zip

    物理学领域十字形声子晶体的能带与传输特性研究及应用

    内容概要:本文详细探讨了十字形声子晶体的能带结构和传输特性。首先介绍了声子晶体作为新型周期性结构在物理学和工程学中的重要地位,特别是十字形声子晶体的独特结构特点。接着从散射体的形状、大小、排列周期等方面分析了其对能带结构的影响,并通过理论计算和仿真获得了能带图。随后讨论了十字形声子晶体的传输特性,即它对声波的调控能力,包括传播速度、模式和能量分布的变化。最后通过大量实验和仿真验证了理论分析的正确性,并得出结论指出散射体的材料、形状和排列方式对其性能有重大影响。 适合人群:从事物理学、材料科学、声学等相关领域的研究人员和技术人员。 使用场景及目标:适用于希望深入了解声子晶体尤其是十字形声子晶体能带与传输特性的科研工作者,旨在为相关领域的创新和发展提供理论支持和技术指导。 其他说明:文中还对未来的研究方向进行了展望,强调了声子晶体在未来多个领域的潜在应用价值。

    嵌入式系统开发_USB主机控制器_Arduino兼容开源硬件_基于Mega32U4和MAX3421E芯片的USB设备扩展开发板_支持多种USB外设接入与控制的通用型嵌入式开发平台_.zip

    嵌入式系统开发_USB主机控制器_Arduino兼容开源硬件_基于Mega32U4和MAX3421E芯片的USB设备扩展开发板_支持多种USB外设接入与控制的通用型嵌入式开发平台_

    e2b8a-main.zip

    e2b8a-main.zip

    少儿编程scratch项目源代码文件案例素材-火柴人跑酷(2).zip

    少儿编程scratch项目源代码文件案例素材-火柴人跑酷(2).zip

    【HarmonyOS分布式技术】远程启动子系统详解:跨设备无缝启动与智能协同的应用场景及未来展望

    内容概要:本文详细介绍了HarmonyOS分布式远程启动子系统,该系统作为HarmonyOS的重要组成部分,旨在打破设备间的界限,实现跨设备无缝启动、智能设备选择和数据同步与连续性等功能。通过分布式软总线和分布式数据管理技术,它能够快速、稳定地实现设备间的通信和数据同步,为用户提供便捷的操作体验。文章还探讨了该系统在智能家居、智能办公和教育等领域的应用场景,展示了其在提升效率和用户体验方面的巨大潜力。最后,文章展望了该系统的未来发展,强调其在技术优化和应用场景拓展上的无限可能性。 适合人群:对HarmonyOS及其分布式技术感兴趣的用户、开发者和行业从业者。 使用场景及目标:①理解HarmonyOS分布式远程启动子系统的工作原理和技术细节;②探索该系统在智能家居、智能办公和教育等领域的具体应用场景;③了解该系统为开发者提供的开发优势和实践要点。 其他说明:本文不仅介绍了HarmonyOS分布式远程启动子系统的核心技术和应用场景,还展望了其未来的发展方向。通过阅读本文,用户可以全面了解该系统如何通过技术创新提升设备间的协同能力和用户体验,为智能生活带来新的变革。

    嵌入式八股文面试题库资料知识宝典-C and C++ normal interview_1.zip

    嵌入式八股文面试题库资料知识宝典-C and C++ normal interview_1.zip

    少儿编程scratch项目源代码文件案例素材-激光反弹.zip

    少儿编程scratch项目源代码文件案例素材-激光反弹.zip

    COMSOL相控阵检测技术在有机玻璃斜楔中检测工件内部缺陷的应用研究

    内容概要:本文详细介绍了COMSOL相控阵检测技术在有机玻璃斜楔上放置16阵元进行工件内部缺陷检测的方法。首先阐述了相控阵检测技术的基本原理,特别是通过控制各阵元的激发时间和相位来实现声波的聚焦和扫描。接着,重点解析了横孔缺陷的反射接收波,解释了波的折射现象及其背后的物理原因。最后,通过实例展示了COMSOL模拟声波传播过程的成功应用,验证了该技术的有效性和准确性。 适合人群:从事固体力学、无损检测领域的研究人员和技术人员,尤其是对相控阵检测技术和COMSOL仿真感兴趣的读者。 使用场景及目标:适用于需要精确检测工件内部缺陷的研究和工业应用场景,旨在提高检测精度和效率,确保产品质量和安全。 其他说明:文中提到的声速匹配现象有助于理解波在不同介质间的传播特性,这对优化检测参数设置有重要意义。

    少儿编程scratch项目源代码文件案例素材-极速奔跑者.zip

    少儿编程scratch项目源代码文件案例素材-极速奔跑者.zip

    嵌入式八股文面试题库资料知识宝典-微软_interview.zip

    嵌入式八股文面试题库资料知识宝典-微软_interview.zip

Global site tag (gtag.js) - Google Analytics