Java网络编程(二) Java I/O分析(jdk1.4之前)

http://zhaohuiopensource.iteye.com/blog/1498517

根据传输数据的方式这里把I/O流分为: 

1.基于磁盘操作的I/O接口:File 
2.基于网络操作的I/O接口:socket 

下面看一个典型的socket连接实例 
服务器端:
 

public class ThreadSocketServer {  
    /** 服务器端口 **/  
    public static final int PORT = 8189;  
  
    public static void main(String[] args) {  
  
        int i = 1;  
        try {  
            ServerSocket s = new ServerSocket(PORT);  
            System.out.println("Listening on port: " + PORT);  
            while (true) {  
                Socket incoming = s.accept();  
                System.out.println("Spawning" + i);  
                Runnable r = new ThreadedEchoHandler(incoming);  
                Thread t = new Thread(r);  
                t.start();  
                i++;  
            }  
        } catch (IOException e) {  
            e.printStackTrace();  
        }  
    }  
  
}  
  
class ThreadedEchoHandler implements Runnable {  
  
    private Socket incoming;  
  
    public ThreadedEchoHandler(Socket i) {  
        incoming = i;  
    }  
  
    @Override  
    public void run() {  
  
        try {  
            try {  
                OutputStream outStream = incoming.getOutputStream();  
                PrintWriter out = new PrintWriter(outStream, true);  
                out.println("Hello!");  
  
            } finally {  
                incoming.close();  
            }  
        } catch (IOException e) {  
            e.printStackTrace();  
        }  
    }  
}


客户端: 
public class SocketClient {  
    /** 客户端尝试连接的次数 **/  
    public static final int CONNECT_UNM = 5000;  
    /** 服务器端IP **/  
    public static final String SERVER_IP = "127.0.0.1";  
  
    public static void main(String[] args) {  
  
        for (int i = 0; i < CONNECT_UNM; i++) {  
            final int temp=i+1;  
            new Thread(new Runnable() {  
                  
                @Override  
                public void run() {  
                    Socket s = null;  
                    try {  
                        s = new Socket(SERVER_IP, ThreadSocketServer.PORT);  
                        System.out.println("第" + temp + "尝试连接--分配的本地端口:"  
                                + s.getLocalPort());  
                        InputStream inStream = s.getInputStream();  
                        Scanner in = new Scanner(inStream);  
                        System.out.println(in.nextLine());  
                    } catch (Exception e) {  
                        e.printStackTrace();  
                    } finally {  
                        try {  
                            s.close();  
                        } catch (IOException e) {  
                        }  
                    }  
                }  
            }).start();  
              
            try {  
                Thread.sleep(1);  
            } catch (InterruptedException e) {  
                e.printStackTrace();  
            }  
              
        }  
  
    }  
}  


从上面的例子中我们了解了以下几个关键点: 
1. 连接->传输数据->关闭连接  是一个短连接的实例 
2. 在客户端我们指定了一个连接数5000,当我们在本地的xp下进行测试时,当连接达到3970左右时,异常java.net.BindException: Address already in use: connect 
原因:在Java网络编程(一)中我们了解到:动态端口的分配范围为 1024-5000 ,也就是说默认情况下,客户端最多可以同时发起3977 个Socket 连接。有人会问我们不是在每次连接的时候都关闭了连接,应该会释放掉可用的端口啊? 
备注:关闭TCP连接 
我们要知道当我们关闭socket的时候我们要知道TCP连接并不是马上就能关闭。TCP协议有一个优雅的关闭(graceful close)机制,以保证应用程序在关闭连接时不必担心正在传输的数据会丢失。 
TCP还是使用了包括Time-Wait状态在内的多种机制对其进行防范。Time-Wait状态用于保证每个TCP连接都在一段平静时间内结束,这期间不会有数据发送。平静时间的长度应该等于分组报文在网络上存留的最长时间的两倍。因此,当一个连接完全结束(即套接字数据结构离开Time-Wait状态并被删除),并为同样一对地址上的新连接清理道路后,就不会再有旧实例发送的消息还存留在网络中。实际上,平静时间的长度要依赖于具体实现,因为没有机制能真正限制分组报文在网络上能够延迟的时间。通常使用的时间范围是4分钟减到30秒,或更短。 
Time-Wait状态最重要的作用是,只要底层套接字数据结构还存在,就不允许在相同的本地端口上关联其他套接字。尤其是试图使用该端口创建新的Socket实例时,将抛出IOException异常。 

所以在最短的30s内我们已经执完了我们的循环,最后导致客户端的动态端口不可用。 

3. 阻塞与非阻塞 
阻塞与非阻塞主要是从 CPU 的消耗上来说的,阻塞就是 CPU 停下来等待一个慢的操作完成 CPU 才接着完成其它的事。非阻塞就是在这个慢的操作在执行时 CPU 去干其它别的事,等这个慢的操作完成时,CPU 再接着完成后续的操作。虽然表面上看非阻塞的方式可以明显的提高 CPU 的利用率,但是也带了另外一种后果就是系统的线程切换增加。增加的 CPU 使用时间能不能补偿系统的切换成本需要好好评估。 

      我们知道在jdk1.4之前只有阻塞I/O,如果我们所有客户端的连接都放在一个线程中,这样的话,很容易造成一个用户出现问题(I/O阻塞)其他用户受到影响。所有一种常用的方式就是:在服务器端为每个到来的客户端都开通了一个线程,这样的话出现阻塞时只是一个线程阻塞而不会影响其它线程工作,还有为了减少系统线程的开销,采用线程池的办法来减少线程创建和回收的成本。但是这样的话也存在问题:系统的线程切换增加。 

针对以上的问题在jdk1.4以后提供了一个新的概念:nio,有效解决了多线程服务器存在的线程开销问题……………..待续
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值