Socket 使用简介

作者: 理查德成 | 来源:发表于2019-06-30 17:28 被阅读6次

摘要

文章介绍如何通过JAVA Socket来建立长连接;包括使用blocking IO方式,None-blocking IO方式。

一、HTTP与socket的区别

HTTP:超文本传输协议,首先它是一个协议,并且是基于TCP/IP协议基础之上的应用层协议。TCP/IP协议是传输层协议,主要解决数据如何在网络中传输,HTTP是应用层协议,主要解决如何包装数据。HTTP协议详细规定了浏览器与服务器之间相互通信的规则,是万维网交换信息的基础。HTTP是基于请求-响应形式并且是短连接,并且是无状态的协议。针对其无状态特性,在实际应用中又需要有状态的形式,因此一般会通过session/cookie技术来解决此问题。

Socket:Socket不属于协议范畴,而是一个调用接口(API),Socket是对TCP/IP协议的封装,通过调用Socket,才能使用TCP/IP协议。Socket连接是长连接,理论上客户端和服务器端一旦建立连接将不会主动断开此连接。Socket连接属于请求-响应形式,服务端可主动将消息推送给客户端。

二、套接字Socket

套接字并没有语言的限制,并非JAVA独有,套接字(socket)是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元。它是网络通信过程中端点的抽象表示,包含进行网络通信必须的五种信息:连接使用的协议,本地主机的IP地址,本地进程的协议端口,远地主机的IP地址,远地进程的协议端口。

image.png

三、Blocking IO Socket

以下两个类为socket提供支持:

  1. ServerSocket:服务端套接字,能接收其他通信实体请求的套接字;
  2. Socket:客户端套接字,或者直接叫做套接字。套接字是网络中两台机器通讯的端点。

套接字通信之间本没有服务端和客户端之分,它们的地位是平等的;但是在建立连接时,必须有一个套接字要做出主动姿态,主动接收来自其他通信实体的连接请求(ServerSocket)。当两个套接字建立连接之后,就没有服务端和客户端之分了。

Server Socket

建立一个Server Socket的代码通常是这样的:

// 监听本机指定端口的连接请求
ServerSocket serverSocket = new ServerSocket(port);
//  accept方法会阻塞当前线程,直到连接建立;并且产生一个socket对象,用以和客户端通信
Socket clientSocket = serverSocket.accept();

InputStream is = clientSocket.getInputStream();
// read()类方法 也会阻塞当前线程,直到数据到达
is.read()

// 消息处理逻辑

// 下面向客户端发送消息
OutputStream os = clientSocket.getOutputStream();
os.write();

// 关闭流,socket
is.close();
os.close();
clientSocket.close();
serverSocket.close();

可用telnet 测试是否Server Socket是否建立成功。

Client Socket

建立一个客户端socket的代码通常如下:

Socket socket = new Socket("服务端socket host", "服务端socket监听的端口");

// 或者用以下方法建立与服务端socket的连接, 可指定连接超时时间
// Socket socket = new Socket();
// 设置连接服务超时
// socket.connect(new InetSocketAddress(host, port), timeout);

// 设置读socket超时,写操作不受限制
socket.setSoTimeout(timeout);

// 下面向客户socket发送信息 
OutputStream os = s.getOutputStream();
os.write()

// 下面读取客户socket发送过来的信息
InputStream is = s.getInputStream();
// read()类方法同样会阻塞当前线程,直到来自连接的数据抵达。
// 这个方法的阻塞时间受到以上 setSoTimeout() 超时时间的控制
is.read():

// 关闭流, socket
os.close()
is.close()
socket.close()

以上server socket, client socket都是基于阻塞式IO;对来自另一端主机的数据读取,都是采用阻塞式的read()方法。

Multi-thread Server Socket

用上面的方式建立的server socket,只能接受来自一个主机的连接;因为accept逻辑与socket的通信逻辑处于同一个线程,而socket的通讯逻辑由于采用了阻塞式IO,导致server socket不能接受来自另一个网络主机的连接。所以通常的方法是使用多线程来处理连接:

ServerSocket serverSocket = new ServerSocket(port)
while(true) {
    // 等待客户端接入
    Socket clientSocket = serverSocket.accept();
    // 每接入一个连接,都交给新线程去处理
    new Thread(()->{
        // 连接处理逻辑
    }).start();
}

还可以使用线程池的技术优化上面new Thread的部分。

半关闭

一种读取socket连接输入方式是:

InputStream is = socket.getInputStream();
int len;
while((len = is.read(bytes)) != -1) {
    // 处理数据
}

关于InputStream的read()方法返回值,jdk是这样描述的:

the total number of bytes read into the buffer, or -1 if there is no more data because the end of the stream has been reached.

达到了流的末尾,那么要怎样达到流的末尾呢,一种方法是客户端socket使用close方法关闭其输出流:

OutputStream os = socket.getOutputStream();
os.write("msg".getBytes())
os.close();

但是关闭输出流也会导致套接字的关闭,如果后面的客户端有读取来自服务端socket的需求,那么就无法读取来自服务端的响应了。

使用socket的半关闭方法就可以解决以上问题:

OutputStream os = socket.getOutputStream();
os.write("msg".getBytes())
os.flush();
socket.shutdownOutput();
// 现在socket是半关闭状态了(关闭了输出流),但是还可以通过输入流读取来自服务端的数据
InputStream is = socket.getInputStream();
// 读取数据逻辑

类似的也有关闭输入流的半关闭方法: shutdownInput

// 待续NIO

相关文章

网友评论

    本文标题:Socket 使用简介

    本文链接:https://www.haomeiwen.com/subject/krvbcctx.html