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. 同步: 所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回.但是一旦调用返回,就必须先得到返回值了. 换句话话说,调用者主动等待这个"调用"的结果. 对于 ...
随机推荐
- jpa用findAll((Specification<GoodsSpu>) (root, criteriaQuery, criteriaBuilder) -> {})排序
//需要用到的包import org.springframework.data.domain.Page;import org.springframework.data.domain.PageReque ...
- 基础练习:FJ的字符串
问题描述 FJ在沙盘上写了这样一些字符串: A1 = "A" A2 = "ABA" A3 = "ABACABA" A4 = "AB ...
- 【scipy 基础】--稀疏矩阵
稀疏矩阵是一种特殊的矩阵,其非零元素数目远远少于零元素数目,并且非零元素分布没有规律.这种矩阵在实际应用中经常出现,例如在物理学.图形学和网络通信等领域. 稀疏矩阵其实也可以和一般的矩阵一样处理,之所 ...
- H.264 和 H.265对比
前言 H.264标准正式发布于2003年3月,距今已经20多年了,但它仍然是当下最流行的视频编解码标准. H.265正式发布于2013年4月.虽然H.265标准是围绕着H.264进行制定的,也保留了原 ...
- Modbus 转 PROFIBUS DP 应用场景 PM-160
1)在网关PROFIBUS DP侧是一个PROFIBUSDP从站,在Modbus串口侧有Modbus主站.Modbus从站.通用模式可选:接口有RS232RS485.RS422三种可选. 2)通信方式 ...
- Linux MIPI 调试中常见的问题
一.概述 做嵌入式工作的小伙伴知道,有时候程序编写没有调试过程中费时,之间笔记里有 MIPI 摄像头驱动开发的过程,有需要的小伙伴可以参考:Linux RN6752 驱动编写. 而我也是第一次琢磨 M ...
- Hdu4742 (CDQ分治)
题意:给出n个三维点对(x,y,z),可随意排列,求三维非严格最长上升子序列长度和最长上升子序列数量. 输入格式:第一行为一整数T表示用例组数,每组用例第一行为一整数n表示点数,之后n行每行三个整数x ...
- Java+Selenium爬取高德POI边界坐标
一.写在前面 关于爬取高德兴趣点边界坐标网上有几篇文章介绍实现方式,总的来说就是通过https://www.amap.com/detail/get/detail传入POI的ID值获取数据,BUT,如果 ...
- [ABC261A] Intersection
Problem Statement We have a number line. Takahashi painted some parts of this line, as follows: Firs ...
- Ubuntu 22.04 LTS 安装lnmp
Ubuntu 22.04 LTS 安装最新稳定版本nginx.mysql5.7和php7.2 全部apt-get安装,就是快,迅速.前提是需要在有网络环境的情况下哈!! 操作系统版本:Ubuntu 2 ...