Java,InputStream,Socket阻塞.(关于HTTP请求的IO问题自我总结)

简介: Java,InputStream,Socket阻塞.(关于HTTP请求的IO问题自我总结)

前言:


由于项目的需求,需要实现以下流程:


1. Client发送HTTP请求到Server.


2. Server接收HTTP请求并显示出请求的内容(包含请求的头及Content的内容)


服务端实现:


Server部分代码如下:


import java.net.Socket;
import java.net.ServerSocket;
import java.net.InetAddress;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
/**
 *  
 * @author Anson
 */
public class Server{
    private String ServerIP;
    private int ServerPort;
    private boolean shutdown = false;
    /**
       * Init Server
       */
    public void init(){
        ServerIP = "192.168.0.1";
        ServerPort = 8082;
    }
    /**
     *  
     */
    public void await(){
        ServerSocket serverSocket = null;
        try {
            serverSocket =  new ServerSocket(ServerPort, 1, InetAddress.getByName(ServerIP));
        }catch (IOException e) {
            e.printStackTrace();
            System.exit(1);
        }
        // Loop waiting for a request
        while(!shutdown){
            Socket          socket  = null;
            InputStream     input   = null;
            OutputStream    output  = null;
            try {
                socket  = serverSocket.accept();
                input   = socket.getInputStream();
                output  = socket.getOutputStream();
                this.parse(input);
                 // Close the socket
                socket.close();
            }catch (Exception e) {
                e.printStackTrace();
                continue;
            }
        }
    }
    public void parse(InputStream input) {
        // Read a set of characters from the socket
        StringBuffer request = new StringBuffer(2048);
        int i;
        byte[] buffer = new byte[2048];
        try {
            i = input.read(buffer);
        }catch (IOException e) {
            e.printStackTrace();
            i = -1;
        }
        for (int j=0; j<i; j++) {
            request.append((char) buffer[j]);
        }
        System.out.println(request.toString());
    }
}

再编写StartServer.java


....
public class StartServer{
    public static void main(String[] args){
        Server server = new Server();
        server.init();
        server.await();
        .....
    }
}

跟着,客户端的代码做法有很多,


可以用最简单的Socket,


也可以用HttpClient,


或其它客户端如:OC


具体做法,网上有很多,在此不细述.


我用的是OC,


当然,可能有些做法,并不会出现这样的问题,


毕竟,我,并非一个高手.


我只描述我遇到的状况,


成功实现的可以忽略.


问题描述:


当客户端发送的数据量小于一定长度(如2048byte)的时候,


Server运行正常,即可以正常打印出请求的内容.


如:GET index HTTP/1.1


   ........


请求的头加上一些Content.


但当数据量大于2048的时候,奇怪的问题出现了.


有时会发现数据读不全.


例如,在请求的头下面,Content的内容是一串XML的String:


<?xml version="1.0" encoding="UTF-8" ?><label1>label1></label1>......


当请求的头加上XML的String的总长度超过2048,那么,可能出现在情况就是超过的部分丢失.


解决办法:


可能已经有人发觉,在上面的Server里面的parse(InputStream input)这里的处理有问题.


因为很大程度上这个2048在StringBuffer的定义时就出现了.


所以,第一个失败的尝试便是修改2048.


第一次变成10*2048


跟着是100*2048....


最后是1000*2048....


但结果可以发现:数据量的限制并不会随着这个数值的成倍增长而以相同倍数增长.有时可能是为了增加一倍,


而这个数值必需增加20倍.....


最后发现这个办法不可能,


于是,改变了读取的办法.


用了其它的如StreamBufferReader还有很多其它的来尝试读取,


结果却令人失望,


甚至于这时候出现了阻塞.


即,在读取的过停中,卡住了,


直到等到客户端的请求超时,才跳出来.


所以这些方法也失败了.


具体的情况也记不大清楚了.


之后的另一个方法:


String request = "";
           //如果不先读一次,那么下面的input.available()有可能是0值.
           char firstbyte = (char) input.read();
            request += firstbyte;
            int ava = 0;
            while ((ava = input.available()) > 0) {
                try {
                    // 在此睡眠0.1秒,很重要
                    Thread.sleep(100);
                } catch (Exception t) {
                    t.printStackTrace();
                }
                byte[] bufferedbyte = new byte[ava];
                input.read(bufferedbyte, 0, ava);
                request += new String(bufferedbyte, "UTF-8");
            }


这是一个成功解决的方法,


之所以要睡眠0.1秒,等待高手帮忙解答,


这也许跟网络有关系,


当然,数据量越大,睡眠的时应该有小小的加长.(缓冲时间).


虽然以上的做法成功实现了,但对于服务器来说,效率是个问题,所以只能寻找更优的办法.



第三次更改:



int readInt = 0;
      int availableCount = 0;
      char requestHead = (char)(input.read());
      request += requestHead;
//取得请求的类型是PUT或GET
      if(requestHead == 'P') {//主要是PUT的方法里面会带有XML.
        while((readInt=input.read()) != -1) {
        request += (char)readInt;
        if(request.indexOf("</endXML>") != -1){
          break;
        }
        }
        System.out.println("this is put\n" +request);
      } else if(requestHead=='G') {//GET的方法内容比较少,用以下的方法可以正常实现
        while((availableCount=input.available()) > 0){
        byte[] requestbuffer = new byte[availableCount];
        input.read(requestbuffer);
        request += new String(requestbuffer, "UTF-8");
        }


*代码并不完整.


这种做法,我在读取XML的String里加上了一个结束的判断即对"</endXML>"的判断(当然,这是在知道Content内容的基础上这种做法才可行).


虽然暂时解决了问题,但仍然不能完美解决存在的问题.


第四次更改:


这也是最后一次更改,办法是:


在PUT的请求里面,先取得一个Content-Length:的请求头的值length.


这里的大小就是Content的长度.


在这个基础上,


先判断是否开始读取Content:


if(requestString.indexOf("\r\n\r\n") != -1)
.....



之后再循环读取:


for(int i=0; i < length; i++){
        requestString += (char)input.read();
    }


当读完length后,跳出循环,


并跳出读取input的读取.


.......


问题在此告一段落.



若有哪位朋友懂得其中原理,恳请告知,


知其然而不知其所以然,心里不免有个结.


结言:


以上做法,仅供参考.


若有错误,请不啬指出.



欢迎转载


转载进请注明转自:http://ansonlai.iteye.com


相关文章
|
25天前
|
Java
Java aop 如何获取请求头里的token
【8月更文挑战第13天】Java aop 如何获取请求头里的token
57 0
|
15天前
|
数据采集 人工智能 监控
【Azure 应用程序见解】Application Insights Java Agent 3.1.0的使用实验,通过修改单个URL的采样率来减少请求及依赖项的数据采集
【Azure 应用程序见解】Application Insights Java Agent 3.1.0的使用实验,通过修改单个URL的采样率来减少请求及依赖项的数据采集
|
2月前
|
数据采集 Web App开发 Java
Java爬虫安全策略:防止TikTok音频抓取过程中的请求被拦截
Java爬虫安全策略:防止TikTok音频抓取过程中的请求被拦截
|
2月前
|
存储 安全 Java
Java面试题:假设你正在开发一个Java后端服务,该服务需要处理高并发的用户请求,并且对内存使用效率有严格的要求,在多线程环境下,如何确保共享资源的线程安全?
Java面试题:假设你正在开发一个Java后端服务,该服务需要处理高并发的用户请求,并且对内存使用效率有严格的要求,在多线程环境下,如何确保共享资源的线程安全?
48 0
|
2月前
|
并行计算 安全 算法
Java面试题:Java内存管理与多线程并发处理,设计一个Java应用,该应用需要处理大量并发用户请求,同时要求对内存使用进行优化,如何通过垃圾回收机制优化内存使用?
Java面试题:Java内存管理与多线程并发处理,设计一个Java应用,该应用需要处理大量并发用户请求,同时要求对内存使用进行优化,如何通过垃圾回收机制优化内存使用?
29 0
|
18天前
|
存储 Java
【IO面试题 四】、介绍一下Java的序列化与反序列化
Java的序列化与反序列化允许对象通过实现Serializable接口转换成字节序列并存储或传输,之后可以通过ObjectInputStream和ObjectOutputStream的方法将这些字节序列恢复成对象。
|
2月前
|
Java 大数据
解析Java中的NIO与传统IO的区别与应用
解析Java中的NIO与传统IO的区别与应用
|
17天前
|
Java 数据处理
Java IO 接口(Input)究竟隐藏着怎样的神秘用法?快来一探究竟,解锁高效编程新境界!
【8月更文挑战第22天】Java的输入输出(IO)操作至关重要,它支持从多种来源读取数据,如文件、网络等。常用输入流包括`FileInputStream`,适用于按字节读取文件;结合`BufferedInputStream`可提升读取效率。此外,通过`Socket`和相关输入流,还能实现网络数据读取。合理选用这些流能有效支持程序的数据处理需求。
22 2
|
18天前
|
XML 存储 JSON
【IO面试题 六】、 除了Java自带的序列化之外,你还了解哪些序列化工具?
除了Java自带的序列化,常见的序列化工具还包括JSON(如jackson、gson、fastjson)、Protobuf、Thrift和Avro,各具特点,适用于不同的应用场景和性能需求。
|
18天前
|
缓存 Java
【IO面试题 一】、介绍一下Java中的IO流
Java中的IO流是对数据输入输出操作的抽象,分为输入流和输出流,字节流和字符流,节点流和处理流,提供了多种类支持不同数据源和操作,如文件流、数组流、管道流、字符串流、缓冲流、转换流、对象流、打印流、推回输入流和数据流等。
【IO面试题 一】、介绍一下Java中的IO流
下一篇
DDNS