Java 网络编程 —— 实现非阻塞式的客户端
创建阻塞的 EchoClient
客户程序一般不需要同时建立与服务器的多个连接,因此用一个线程,按照阻塞模式运行就能满足需求
public class EchoClient {
private SocketChannel socketChannel = null;
public EchoClient() throws IOException {
socketChannel = SocketChannel.open();
InetAddress ia = InetAddress,getLocalHost();
InetSocketAddress isa = new InetSocketAddress(ia,8000);
socketChannel.connect(isa); //连接服务器
}
public static void main(String args[])throws IOException {
new EchoClient().talk();
}
private PrintWriter getWriter(Socket socket) throws IOException {
OutputStream socketOut = socket.getOutputStream();
return new PrintWriter(socketOut,true);
}
private BufferedReader getReader(Socket socket) throws IOException {
InputStream socketIn = socket.getInputStream();
return new BufferedReader(new InputStreamReader(socketIn));
}
public void talk() throws IOException {
try {
BufferedReader br = getReader(socketChannel.socket());
PrintWriter pw = getWriter(socketChannel.socket());
BufferedReader localReader = new BufferedReader(new InputStreamReader(System.in));
String msq = null;
while((msg = localReader.readLine()) != null) {
pw.println(msg);
System.out.println(br.readLine());
if(msq.equals("bye")) {
break;
}
}
} catch(IOException e) {
e.printStackTrace();
} finally {
try {
socketChannel.close();
} catch(IOException e) {
e.printStackTrace();
}
}
}
}
创建非阻塞的 EchoClient
对于客户与服务器之间的通信,按照它们收发数据的协调程度来区分,可分为同步通信和异步通信
同步通信指甲方向乙方发送了一批数据后,必须等接收到了乙方的响应数据后,再发送下一批数据。同步通信要求一个 IO 操作完成之后,才能完成下一个 IO 操作,用阻塞模式更容易实现
异步通信指发送数据和接收数据的操作互不干扰,各自独立进行。异步通信允许发送数据和接收数据的操作各自独立进行,用非阻塞模式更容易实现
值得注意的是,通信的两端并不要求都采用同样的通信方式,当一方采用同步通信时,另一方可以采用异步通信
public class EchoClient {
private SocketChannel socketChannel = null;
private ByteBuffer sendBuffer = ByteBuffer.allocate(1024);
private ByteBuffer receiveBuffer = ByteBuffer.allocate(1024);
private Charset charset = Charset.forName("GBK");
private Selector selector;
public EchoClient() throws IOException {
socketChannel = SocketChannel.open();
InetAddress ia = InetAddress.getLocalHost();
InetSocketAddress isa = new InetSocketAddress(ia, 8000);
socketChannel.connect(isa); //采用阻塞模式连接服务器
socketChannel.configureBlocking(false); //设置为非阻塞模式
selector = Selector.open();
}
public static void main(String args[]) throws IOException {
final EchoClient client = new EchoClient();
Thread receiver=new Thread() {
public void run() {
client.receiveFromUser(); //接收用户向控制台输入的数据
}
};
receiver.start();
client.talk();
}
/** 接收用户从控制台输入的数据,放到sendBuffer中 */
public void receiveFromUser() {
try {
BufferedReader localReader = new BufferedReader(new InputStreamReader(System.in));
String msg = null;
while((msg = localReader.readLine()) != null) {
synchronized(sendBuffer) {
sendBuffer.put(encode(msg + "\r\n"));
}
if (msg.equals("bye")) {
break;
}
}
} catch(IOException e) {
e.printStackTrace();
}
}
//接收和发送数据
public void talk() throws IOException {
socketChannel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
while (selector.select() > 0 ) {
Set readyKeys = selector.selectedKeys();
Iterator it = readyKeys.iterator();
while (it.hasNext()) {
SelectionKey key = null;
try {
key = (SelectionKey) it.next();
it.remove();
if (key.isReadable()) {
receive(key);
}
if (key.isWritable()) {
send(key);
}
} catch(IOException e) {
e.printStackTrace();
try {
if(key != null) {
key.cancel();
key.channel().close() ;
}
} catch(Exception ex) {
e.printStackTrace();
}
}
}
}
}
public void send(SelectionKey key) throws IOException {
//发送sendBuffer的数据
SocketChannel socketChannel = (SocketChannel)key.channel();
synchronized(sendBuffer) {
sendBuffer.flip(); //把极限设为位置,把位置设为0
socketChannel.write(sendBuffer); //发送数据
sendBuffer.compact(); //删除已经发送的数据
}
}
public void receive(SelectionKey key) throws IOException {
//接收EchoServer发送的数据,把它放到receiveBuffer
//如果receiveBuffer有一行数据,就打印这行数据,然后把它从receiveBuffer删除
SocketChannel socketChannel = (SocketChannel) key.channel();
socketChannel.read(receiveBuffer):
receiveBuffer.flip();
String receiveData = decode (receiveBuffer);
if(receiveData.indexOf("\n") == -1) return;
String outputData = receiveData.substring(0, receiveData.indexOf("\n") + 1):
System.out.print(outputData);
if(outputData.equals("echo:bye\r\n")) {
key.cancel():
socketChannel.close();
selector.close();
System.exit(0);
}
ByteBuffer temp = encode(outputData);
receiveBuffer.position(temp.limit());
receiveBuffer.compact(): //删除已经打印的数据
}
//解码
public String decode(ByteBuffer buffer) {
CharBuffer charBuffer= charset.decode(buffer);
return charBuffer.toString();
}
//编码
public ByteBuffer encode(String str) {
return charset.encode(str);
}
}
Java 网络编程 —— 实现非阻塞式的客户端的更多相关文章
- Java网络编程 -- NIO非阻塞网络编程
从Java1.4开始,为了替代Java IO和网络相关的API,提高程序的运行速度,Java提供了新的IO操作非阻塞的API即Java NIO.NIO中有三大核心组件:Buffer(缓冲区),Chan ...
- 网络编程之非阻塞connect编写
一.connect非阻塞编写 TCP连接的建立涉及到一个三次握手的过程,且socket中connect函数需要一直等到客户接收到对于自己的SYN的ACK为止才返回, 这意味着每 个connect函数总 ...
- Java网络编程(TCP协议-服务端和客户端交互)
客户端: package WebProgramingDemo; import java.io.IOException; import java.io.InputStream; import java. ...
- Java基础——NIO(二)非阻塞式网络通信与NIO2新增类库
一.NIO非阻塞式网络通信 1.阻塞与非阻塞的概念 传统的 IO 流都是阻塞式的.也就是说,当一个线程调用 read() 或 write() 时,该线程被阻塞,直到有一些数据被读取或写入,该线程在 ...
- Java网络编程和NIO详解5:Java 非阻塞 IO 和异步 IO
Java网络编程和NIO详解5:Java 非阻塞 IO 和异步 IO Java 非阻塞 IO 和异步 IO 转自https://www.javadoop.com/post/nio-and-aio 本系 ...
- Java网络编程 -- BIO 阻塞式网络编程
阻塞IO的含义 阻塞(blocking)IO :阻塞是指结果返回之前,线程会被挂起,函数只有在得到结果之后(或超时)才会返回 非阻塞(non-blocking)IO :非阻塞和阻塞的概念相对应,指在不 ...
- Java IO(3)非阻塞式输入输出(NIO)
在上篇<Java IO(2)阻塞式输入输出(BIO)>的末尾谈到了什么是阻塞式输入输出,通过Socket编程对其有了大致了解.现在再重新回顾梳理一下,对于只有一个“客户端”和一个“服务器端 ...
- Socket-IO 系列(三)基于 NIO 的同步非阻塞式编程
Socket-IO 系列(三)基于 NIO 的同步非阻塞式编程 缓冲区(Buffer) 用于存储数据 通道(Channel) 用于传输数据 多路复用器(Selector) 用于轮询 Channel 状 ...
- NIO非阻塞式编程
/** * NIO非阻塞式编程<p> * 服务端和客户端各自维护一个管理通道的对象,我们称之为selector,该对象能检测一个或多个通道 (channel) 上的事件. * 我们以服务端 ...
- Java基础知识强化之多线程笔记07:同步、异步、阻塞式、非阻塞式 的联系与区别
1. 同步: 所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回.但是一旦调用返回,就必须先得到返回值了. 换句话话说,调用者主动等待这个"调用"的结果. 对于 ...
随机推荐
- 使用Github Copilot生成单元测试并执行
上一篇文章我们介绍了 使用Github Copilot完成代码编写 本文我们继续使用Github Copilot在已有代码的基础上生成代码测试并执行. 一.先说一下代码的背景 需要上需要提供一个度量衡 ...
- PZthon
一道新式题目,python.exe的分析 这个时候没有思路不要紧,直接wp 先用特别的软件执行.exe程序,就是对.exe进行反编译 然后在反编译脚本的目录写就有了一个打包文件夹 在里面找到文件的.p ...
- 混合应用与Hybrid App开发上架流程透析
Hybrid App(混合 App)已经成为大家接触最为广泛的 App 形式,不管是我们用到的微信.支付宝还是淘宝.京东等大大小小的应用都非常热衷于Hybrid App 带来的研发效率提升和灵活性. ...
- 使用js写一个音乐音谱图
我们经常看到在听乐音的时候,会有音谱图随着音乐的节奏不断变化给人视觉上的享受,那么我们通过js来实现以下这个效果,下面是简单的效果图 首先我们需要有一个绘制音频的函数 function draw() ...
- H.264中的帧
导言 高级视频编码 (AVC) 也称为 H.264,是使用最广泛的视频压缩标准.它与所有主要的流式传输协议和容器格式兼容. 当我们使用播放器播放一个视频时,通常会经过:解协议,解封装,音视频解码,音视 ...
- python01-基础概念与环境搭建
学习目标 了解硬件 & 操作系统 & 软件(应用系统)之间的关系. 了解常见的操作系统都有哪些. 了解编译器和解释器的区别和作用. 了解编程语言进行分类 了解Python解释器的种类 ...
- Python 中的单下划线和双下划线
哈喽大家好,我是咸鱼 当我们在学习 Python 的时候,可能会经常遇到单下划线 _ 和双下划线 __ 这两种命名方式 单下划线 _ 和双下划线 __ 不仅仅是只是一种简单的命名习惯,它们在 Pyth ...
- 【Javaweb】Servlet三|实现Servlet程序的几种方法及Servlet继承体系说明
GET和POST请求的分发处理 代码如下 注意页面要和index位置一样否则浏览器找不到 注意大小写 HttpServletRequest httpServletRequest = (HttpServ ...
- VLOOKUP函数10种经典用法
VLOOKUP函数是Excel中非常常用的函数之一,可以用于在一个区域或表格中查找某个值,并返回该值所在行的另一个指定列中的数值.以下是VLOOKUP函数的10种经典用法: 基本的VLOOKUP用法: ...
- DevOps|研发提效-敏捷开发之每日站立会
对于研发效能团队建设和组织,本文不再赘述,可以参考之前的文章,已经讲得很透彻了.本文重点讲我们日常是怎么开站立会,怎么让团队跑起来,高效能产出的.每日站立会,15分钟到30分钟,看似非常短的一个会,但 ...