《林老师带你学编程》知识星球是由多个工作10年以上的一线大厂开发人员联合创建,希望通过我们的分享,帮助大家少走弯路,可以在技术的领域不断突破和发展。
🔥 具体的加入方式:
- 直接访问链接:https://t.zsxq.com/14F2uGap7
- 微信扫码下图:
曾经使用 Netty 写过一个 RPC 框架,对 Netty 有一点点研究。这篇回答可以带你入门 Netty,即使你没有网络编程基础应该也能看懂。
老套路,学习某一门技术或者框架的时候,第一步当然是要了解下面这几样东西。
- 是什么?
- 有哪些特点?
- 有哪些应用场景?
- 有哪些成功使用的案例?
- …..
为了让你更好地了解 Netty 以及它诞生的原因,先从传统的网络编程说起吧!
还是要从 BIO 说起
传统的阻塞式通信流程
早期的 Java 网络相关的 API(java.net
包) 使用 Socket(套接字)进行网络通信,不过只支持阻塞函数使用。
要通过互联网进行通信,至少需要一对套接字:
- 运行于服务器端的 Server Socket。
- 运行于客户机端的 Client Socket
Socket 网络通信过程如下图所示:
Socket 网络通信过程简单来说分为下面 4 步:
- 建立服务端并且监听客户端请求
- 客户端请求,服务端和客户端建立连接
- 两端之间可以传递数据
- 关闭资源
对应到服务端和客户端的话,是下面这样的。
服务器端:
- 创建
ServerSocket
对象并且绑定地址(ip)和端口号(port):server.bind(new InetSocketAddress(host, port))
- 通过
accept()
方法监听客户端请求 - 连接建立后,通过输入流读取客户端发送的请求信息
- 通过输出流向客户端发送响应信息
- 关闭相关资源
客户端:
- 创建
Socket
对象并且连接指定的服务器的地址(ip)和端口号(port):socket.connect(inetSocketAddress)
- 连接建立后,通过输出流向服务器端发送请求信息
- 通过输入流获取服务器响应的信息
- 关闭相关资源
一个简单的 demo
为了便于理解,我写了一个简单的代码帮助各位小伙伴理解。
服务端:
public class HelloServer {
private static final Logger logger = LoggerFactory.getLogger(HelloServer.class);
public void start(int port) {
//1.创建 ServerSocket 对象并且绑定一个端口
try (ServerSocket server = new ServerSocket(port);) {
Socket socket;
//2.通过 accept()方法监听客户端请求, 这个方法会一直阻塞到有一个连接建立
while ((socket = server.accept()) != null) {
logger.info("client connected");
try (ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream())) {
//3.通过输入流读取客户端发送的请求信息
Message message = (Message) objectInputStream.readObject();
logger.info("server receive message:" + message.getContent());
message.setContent("new content");
//4.通过输出流向客户端发送响应信息
objectOutputStream.writeObject(message);
objectOutputStream.flush();
} catch (IOException | ClassNotFoundException e) {
logger.error("occur exception:", e);
}
}
} catch (IOException e) {
logger.error("occur IOException:", e);
}
}
public static void main(String[] args) {
HelloServer helloServer = new HelloServer();
helloServer.start(6666);
}
}
ServerSocket
的 accept()
方法是阻塞方法,也就是说 ServerSocket
在调用 accept()
等待客户端的连接请求时会阻塞,直到收到客户端发送的连接请求才会继续往下执行代码,因此我们需要要为每个 Socket 连接开启一个线程(可以通过线程池来做)。
上述服务端的代码只是为了演示,并没有考虑多个客户端连接并发的情况。
客户端:
/**
* @author shuang.kou
* @createTime 2020年05月11日 16:56:00
*/
public class HelloClient {
private static final Logger logger = LoggerFactory.getLogger(HelloClient.class);
public Object send(Message message, String host, int port) {
//1. 创建Socket对象并且指定服务器的地址和端口号
try (Socket socket = new Socket(host, port)) {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
//2.通过输出流向服务器端发送请求信息
objectOutputStream.writeObject(message);
//3.通过输入流获取服务器响应的信息
ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
return objectInputStream.readObject();
} catch (IOException | ClassNotFoundException e) {
logger.error("occur exception:", e);
}
return null;
}
public static void main(String[] args) {
HelloClient helloClient = new HelloClient();
helloClient.send(new Message("content from client"), "127.0.0.1", 6666);
System.out.println("client receive message:" + message.getContent());
}
}
发送的消息实体类:
/**
* @author shuang.kou
* @createTime 2020年05月11日 17:02:00
*/
@Data
@AllArgsConstructor
public class Message implements Serializable {
private String content;
}
首先运行服务端,然后再运行客户端,控制台输出如下: