创建阻塞的 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 网络编程 —— 实现非阻塞式的客户端的更多相关文章

  1. Java网络编程 -- NIO非阻塞网络编程

    从Java1.4开始,为了替代Java IO和网络相关的API,提高程序的运行速度,Java提供了新的IO操作非阻塞的API即Java NIO.NIO中有三大核心组件:Buffer(缓冲区),Chan ...

  2. 网络编程之非阻塞connect编写

    一.connect非阻塞编写 TCP连接的建立涉及到一个三次握手的过程,且socket中connect函数需要一直等到客户接收到对于自己的SYN的ACK为止才返回, 这意味着每 个connect函数总 ...

  3. Java网络编程(TCP协议-服务端和客户端交互)

    客户端: package WebProgramingDemo; import java.io.IOException; import java.io.InputStream; import java. ...

  4. Java基础——NIO(二)非阻塞式网络通信与NIO2新增类库

    一.NIO非阻塞式网络通信 1.阻塞与非阻塞的概念  传统的 IO 流都是阻塞式的.也就是说,当一个线程调用 read() 或 write() 时,该线程被阻塞,直到有一些数据被读取或写入,该线程在 ...

  5. Java网络编程和NIO详解5:Java 非阻塞 IO 和异步 IO

    Java网络编程和NIO详解5:Java 非阻塞 IO 和异步 IO Java 非阻塞 IO 和异步 IO 转自https://www.javadoop.com/post/nio-and-aio 本系 ...

  6. Java网络编程 -- BIO 阻塞式网络编程

    阻塞IO的含义 阻塞(blocking)IO :阻塞是指结果返回之前,线程会被挂起,函数只有在得到结果之后(或超时)才会返回 非阻塞(non-blocking)IO :非阻塞和阻塞的概念相对应,指在不 ...

  7. Java IO(3)非阻塞式输入输出(NIO)

    在上篇<Java IO(2)阻塞式输入输出(BIO)>的末尾谈到了什么是阻塞式输入输出,通过Socket编程对其有了大致了解.现在再重新回顾梳理一下,对于只有一个“客户端”和一个“服务器端 ...

  8. Socket-IO 系列(三)基于 NIO 的同步非阻塞式编程

    Socket-IO 系列(三)基于 NIO 的同步非阻塞式编程 缓冲区(Buffer) 用于存储数据 通道(Channel) 用于传输数据 多路复用器(Selector) 用于轮询 Channel 状 ...

  9. NIO非阻塞式编程

    /** * NIO非阻塞式编程<p> * 服务端和客户端各自维护一个管理通道的对象,我们称之为selector,该对象能检测一个或多个通道 (channel) 上的事件. * 我们以服务端 ...

  10. Java基础知识强化之多线程笔记07:同步、异步、阻塞式、非阻塞式 的联系与区别

    1. 同步: 所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回.但是一旦调用返回,就必须先得到返回值了. 换句话话说,调用者主动等待这个"调用"的结果. 对于 ...

随机推荐

  1. 使用Github Copilot生成单元测试并执行

    上一篇文章我们介绍了 使用Github Copilot完成代码编写 本文我们继续使用Github Copilot在已有代码的基础上生成代码测试并执行. 一.先说一下代码的背景 需要上需要提供一个度量衡 ...

  2. PZthon

    一道新式题目,python.exe的分析 这个时候没有思路不要紧,直接wp 先用特别的软件执行.exe程序,就是对.exe进行反编译 然后在反编译脚本的目录写就有了一个打包文件夹 在里面找到文件的.p ...

  3. 混合应用与Hybrid App开发上架流程透析

    Hybrid App(混合 App)已经成为大家接触最为广泛的 App 形式,不管是我们用到的微信.支付宝还是淘宝.京东等大大小小的应用都非常热衷于Hybrid App 带来的研发效率提升和灵活性. ...

  4. 使用js写一个音乐音谱图

    我们经常看到在听乐音的时候,会有音谱图随着音乐的节奏不断变化给人视觉上的享受,那么我们通过js来实现以下这个效果,下面是简单的效果图 首先我们需要有一个绘制音频的函数 function draw() ...

  5. H.264中的帧

    导言 高级视频编码 (AVC) 也称为 H.264,是使用最广泛的视频压缩标准.它与所有主要的流式传输协议和容器格式兼容. 当我们使用播放器播放一个视频时,通常会经过:解协议,解封装,音视频解码,音视 ...

  6. python01-基础概念与环境搭建

    学习目标 了解硬件 & 操作系统 & 软件(应用系统)之间的关系. 了解常见的操作系统都有哪些. 了解编译器和解释器的区别和作用. 了解编程语言进行分类 了解Python解释器的种类 ...

  7. Python 中的单下划线和双下划线

    哈喽大家好,我是咸鱼 当我们在学习 Python 的时候,可能会经常遇到单下划线 _ 和双下划线 __ 这两种命名方式 单下划线 _ 和双下划线 __ 不仅仅是只是一种简单的命名习惯,它们在 Pyth ...

  8. 【Javaweb】Servlet三|实现Servlet程序的几种方法及Servlet继承体系说明

    GET和POST请求的分发处理 代码如下 注意页面要和index位置一样否则浏览器找不到 注意大小写 HttpServletRequest httpServletRequest = (HttpServ ...

  9. VLOOKUP函数10种经典用法

    VLOOKUP函数是Excel中非常常用的函数之一,可以用于在一个区域或表格中查找某个值,并返回该值所在行的另一个指定列中的数值.以下是VLOOKUP函数的10种经典用法: 基本的VLOOKUP用法: ...

  10. DevOps|研发提效-敏捷开发之每日站立会

    对于研发效能团队建设和组织,本文不再赘述,可以参考之前的文章,已经讲得很透彻了.本文重点讲我们日常是怎么开站立会,怎么让团队跑起来,高效能产出的.每日站立会,15分钟到30分钟,看似非常短的一个会,但 ...