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。注意不能调⽤InputStream().clo()。这样会导致socket被关闭。
当然如果不需要继续在socket上进⾏读操作,也可以直接关闭socket。但是这个⽅法不能⽤于通信双⽅需要多次交互的情况。
周末早安1 os.write("nder say hello socket".getBytes());
2 os.flush();
读书板报3 client.shutdownOutput(); //调⽤shutdown 通知对端请求完毕
这个解决⽅案缺点⾮常明显,socket任意⼀端都依赖于对⽅调⽤shutdownOutput()来完成read返回 -1,如果任意⼀⽅没有执⾏shutdown函数那么就会出现问题。所以⼀般我们都会在socket请求时设置连接的超时时间 socket.tSoTimeout(5000);以防⽌长时间没有响应造成系统瘫痪。李滋
1 while (true) {
2 rver = rverSocket.accept();
3 System.out.println("rver socket is start……");
4 rver.tSoTimeout(5000);母爱素材
5 .....
6 }
参考⽂章: