创建阻塞的 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. Kubernetes: kube-apiserver 之认证

    kubernetes:kube-apiserver 系列文章: Kubernetes:kube-apiserver 之 scheme(一) Kubernetes:kube-apiserver 之 sc ...

  2. python3使用pymsql操作mysql数据库

    操作系统 :Windows 10_x64 python版本 :3.9.2 pymysql版本: 1.0.2 MySQL版本: 5.7.38   之前写过一篇关于python操作mysql数据库的文章: ...

  3. Linux TTY/PTS

    转载:https://segmentfault.com/a/1190000009082089 当我们在键盘上敲下一个字母的时候,到底是怎么发送到相应的进程的呢?我们通过ps.who等命令看到的类似tt ...

  4. Echarts 饼图,legend样式美化

    最后样式图: 实现代码: var myChart = echarts.init(document.getElementById('container')); let option = { /*{b}: ...

  5. 在.net中使用AutoMapper进行对象映射,对象相互转,简单方便

    AutoMapper是一种对象映射工具,它可以帮助我们将不同类型的数据对象之间进行相互转换.在.NET中,我们可以使用AutoMapper库来简化数据对象之间的映射操作,从而提高代码的可读性和可维护性 ...

  6. 使用Slurm集群进行分布式图计算:对Github网络影响力的系统分析

    本文分享自华为云社区<基于Slurm集群的分布式图计算应用实践:Github协作网络影响力分析>,作者:yd_263841138 . 1. 引言 Slurm(Simple Linux Ut ...

  7. Stream API学习笔记

    Java8 中Stream API介绍   Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象.这种风格将要处理的元素集合看作一种流, ...

  8. Kernel Memory 入门系列:文档预处理

    Kernel Memory 入门系列:文档预处理 Embedding为我们提供了问题理解和文档检索的方法,但是面对大量的文档,如果在用于提问的时候再进行文档的Embedding的话,那这个过程是非常耗 ...

  9. 2021AI量化投资训练营重磅升级,自带编程的优势显而易见

    量化交易规模突破万亿大关 国内量化交易规模快速发展,今年量化基金已突破万亿大关,并且量化私募的整体业绩十分亮眼,过去5年一线量化私募的超额收益基本在20%~30%,量化交易的占比已达到20%-30%( ...

  10. 一文掌握 Kubernetes 证书

    如果你正在自己的环境中运行 Kubernetes,那么了解证书的工作原理以及如何管理它们以确保集群的安全性和完整性至关重要.在本文中,我们将解释什么是 Kubernetes 证书.其重要性,以及如何检 ...