zoukankan      html  css  js  c++  java
  • socket通信模型、socket中的accept()阻塞与read()阻塞

    Socket整体流程

        Socket编程主要涉及到客户端和服务端两个方面,首先是在服务器端创建一个服务器套接字(ServerSocket),并把它附加到一个端口上,服务器从这个端口监听连接。端口号的范围是0到65536,但是0到1024是为特权服务保留的端口号,我们可以选择任意一个当前没有被其他进程使用的端口。

      客户端请求与服务器进行连接的时候,根据服务器的域名或者IP地址,加上端口号,打开一个套接字。当服务器接受连接后,服务器和客户端之间的通信就像输入输出流一样进行操作。

      如果一个程序创建了一个socket,并让其监听80端口,其实是向TCP/IP协议栈声明了其对80端口的占有。以后,所有目标是80端口的TCP数据包都会转发给该程序(这里的程序,因为使用的是Socket编程接口,所以首先由Socket层来处理)。所谓accept函数,其实抽象的是TCP的连接建立过程。  accept函数返回的新socket其实指代的是本次创建的连接,而一个连接是包括两部分信息的,一个是源IP和源端口,另一个是宿IP和宿端口。所以,accept可以产生多个不同的socket,而这些socket里包含的宿IP和宿端口是不变的,变化的只是源IP和源端口。这样的话,这些socket宿端口就可以都是80,而Socket层还是能根据源/宿对来准确地分辨出IP包和socket的归属关系,从而完成对TCP/IP协议的操作封装。TCP/IP只是一个协议栈,就像操作系统的运行机制一样,必须要具体实现,同时还要提供对外的操作接口。就像操作系统会提供标准的编程接口,比如  Win32编程接口一样,TCP/IP也必须对外提供编程接口,这就是Socket编程接口。

    客户端上的使用

      getInputStream()方法可以得到一个输入流,客户端的Socket对象上的getInputStream方法得到输入流其实就是从服务器端发回的数据。
      getOutputStream()方法得到的是一个输出流,客户端的Socket对象上的getOutputStream方法得到的输出流其实就是发送给服务器端的数据。

    服务器端上的使用

      getInputStream()方法得到的是一个输入流,服务端的Socket对象上的getInputStream方法得到的输入流其实就是从客户端发送给服务器端的数据流。

      getOutputStream()方法得到的是一个输出流,服务端的Socket对象上的getOutputStream方法得到的输出流其实就是发送给客户端的数据。

    ServerSocket类的accept()阻塞

    ServerSocket的accept()方法是侦听并接受到此套接字的连接,就是一直等待连接,此方法在连接传入之前一直阻塞(即后面的代码不会往下执行)。直到接受到有socket的连接,然后创建并返回新的Socket对象。

    read()阻塞

    从socket上读取对端发过来的数据一般有两种方法: 

    1)按照字节流读取

    2)按照字符流读取

    这段代码执行以后会发现read()方法发生了阻塞,经过查找资料发现:

    read() 是一个阻塞函数,如果客户端没有声明断开outputStream那么它就会认为客户端仍旧可能发送数据,所以就会一直阻塞而不是返回-1,所以System.out.println("服务器");这行代码在连接断开之前就一直不会执行,因为在while ((len = is.read(buf)) != -1) 这里阻塞了。

    像read()这种阻塞读取函数还有BufferedReader类种的 readLine()、DataInputStream种的readUTF()等。


    这个特性使得编程非常方便也很高效。 
    但是这样也有一个问题,就是如何让程序从这两个方法的阻塞调用中返回。

     总结一下,有这么几个方法:

     1、发送完后调用Socket的shutdownOutput()方法关闭输出流,这样对端的输入流上的read操作就会返回-1。  注意不能调用socket.getInputStream().close()。这样会导致socket被关闭。 

           当然如果不需要继续在socket上进行读操作,也可以直接关闭socket。  但是这个方法不能用于通信双方需要多次交互的情况。

    1 os.write("sender say hello socket".getBytes());
    2 os.flush();
    3 client.shutdownOutput();  //调用shutdown 通知对端请求完毕

           这个解决方案缺点非常明显,socket任意一端都依赖于对方调用shutdownOutput()来完成read返回 -1,如果任意一方没有执行shutdown函数那么就会出现问题。所以一般我们都会在socket请求时设置连接的超时时间 socket.setSoTimeout(5000);以防止长时间没有响应造成系统瘫痪。

    1 while (true) {
    2      server = serverSocket.accept();
    3      System.out.println("server socket is start……");
    4      server.setSoTimeout(5000);
    5      .....
    6  }

    参考文章:

    https://blog.csdn.net/anthony_ju/article/details/82192135

    https://www.cnblogs.com/swordfall/p/10781281.html

    https://www.cnblogs.com/gaoqiri/p/10055610.html

    https://blog.csdn.net/yanchuang1/article/details/48049259

  • 相关阅读:
    2、基础知识点回顾
    jQuery事件二
    71、auth模块、bbs项目关系表
    PYthon-4.26作业
    PYthon-线程
    PYthon-4.23作业
    PYthon-4.15作业
    PYthon-4.9作业
    PYthon-4.7作业
    PYthon-3.31作业
  • 原文地址:https://www.cnblogs.com/FengZeng666/p/12488827.html
Copyright © 2011-2022 走看看