Java IO 与 NIO 服务器&客户端通信小栗子
本篇包含了入门小栗子以及一些问题的思考
BIO
package com.demo.bio; import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner; /**
* 问题:开启多个客户端,只有服务端发送足够条数的消息,客户端才会收到
*/
public class Server { public static void main(String[] args) throws Exception {
new Server().startServer();
} public void startServer() throws IOException {
ServerSocket serverSocket = new ServerSocket(9999); while (true){
Socket client = serverSocket.accept();
System.err.println("Client:" + client.getInetAddress().getHostAddress());
OutputStream out = client.getOutputStream();
PrintWriter writer = new PrintWriter(new OutputStreamWriter(out, "UTF-8"), true);
writer.println("Hello!We are already connected!say 'bye' to close"); new Thread(new SocketReadThread(client)).start();
new Thread(new SocketWriteThread(client)).start();
} }
} /**
* 读线程
*/
class SocketReadThread implements Runnable{ private Socket socket; public SocketReadThread(Socket socket) {
this.socket = socket;
} @Override
public void run() {
try {
InputStream in = socket.getInputStream();
Scanner scanner = new Scanner(in, "UTF-8");
boolean bye = false;
while (!bye && scanner.hasNextLine()){
String line = scanner.nextLine();
System.out.println("Client Msg[" + socket + "]:" + line);
if(line.trim().equals("bye")){
bye = true;
}
}
in.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
} }
} /**
* 写线程
*/
class SocketWriteThread implements Runnable{ private Socket socket; public SocketWriteThread(Socket socket) {
this.socket = socket;
} @Override
public void run() {
try {
OutputStream out = socket.getOutputStream();
PrintWriter writer = new PrintWriter(new OutputStreamWriter(out, "UTF-8"), true);
Scanner scanIn = new Scanner(System.in);
while (true){
String line = scanIn.nextLine();
writer.println(line);
if (socket.isClosed()){
break;
}
}
} catch (IOException e) {
e.printStackTrace();
} }
}
package com.demo.bio; import java.io.*;
import java.net.Socket;
import java.util.Scanner; /**
* 客户端
*/
public class Client { public static void main(String[] args) throws Exception {
Socket socket = new Socket("127.0.0.1", 9999);
OutputStream out = socket.getOutputStream();
PrintWriter writer = new PrintWriter(new OutputStreamWriter(out, "UTF-8"), true); new Thread(new SocketReceiveThread(socket)).start();
Scanner scanIn = new Scanner(System.in);
while (!socket.isClosed()){
String line = scanIn.nextLine();
writer.println(line);
if(line.trim().equals("bye")){
socket.close();
}
}
} }
class SocketReceiveThread implements Runnable{ private Socket socket; public SocketReceiveThread(Socket socket) {
this.socket = socket;
} @Override
public void run() {
try {
InputStream in = socket.getInputStream();
Scanner scanner = new Scanner(in, "UTF-8");
boolean bye = false;
while (!bye && scanner.hasNextLine()){
String line = scanner.nextLine();
System.out.println("Server Msg:" + line);
if(line.trim().equals("bye")){
bye = true;
}
}
scanner.close();
} catch (IOException e) {
e.printStackTrace();
} }
}
BIO没什么难的,同步阻塞。上面实现的主要就是服务器和客户端你一句我一句,巴拉巴拉巴拉
NIO
我要实现一个客户端服务器通信的例子,我的第一个版本
package com.demo.nio; import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set; /**
* 问题:启动服务器,没有启动客户端的时候,阻塞在selector.select();直到有客户端连接才会向下走。
* 启动客户端:获取到客户端的消息,并读取显示;然后写一条数据给客户端;然后进入了写操作模块,等待写入,阻塞。
* 这个时候,客户端已经经过了读取操作,并且没有读到数据,也进入了写操作模块,等待写入,阻塞。这就解释了为什么客户端收不到服务器的第一条消息。
* 客户端写入:客户端输入数据,发送给服务器,离开写操作模块,进入下一轮循环,然后进入读操作模块,读取到服务器的第一条消息并显示。
* 服务器接收:此时服务器并没有收到客户端的消息,因为此时还在写操作模块阻塞,所以想要读取到数据,就要向客户端发送数据,以离开写操作模块,进入下一轮循环。
* 这就解释了:为什么要先写入才能读取的数据。
*/
public class Server { private boolean isFirst = true;
private ServerSocketChannel ssc = null;
private Selector selector = null; public Server(int port) throws IOException {
ssc = ServerSocketChannel.open();
selector = Selector.open();
InetSocketAddress inetAddress = new InetSocketAddress(InetAddress.getLocalHost(), port); ssc.socket().bind(inetAddress);
ssc.configureBlocking(false);
ssc.register(selector, SelectionKey.OP_ACCEPT);
listener(selector);
} private void listener(Selector selector) throws IOException{
while(true){
System.out.println("等待客户端连接...");
selector.select();
System.out.println("捕获客户端连接...");
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectedKeys.iterator(); while (iterator.hasNext()) {
SelectionKey key = iterator.next();
//连接事件
if(key.isAcceptable()){
ServerSocketChannel channel = (ServerSocketChannel) key.channel();
channel.accept().configureBlocking(false).register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
//System.out.println(channel.toString() + "-已连接");
}
//读数据
if(key.isReadable()){
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer bf = ByteBuffer.allocate(1024);
channel.read(bf);
System.out.println("来自客户端数据:" + new String(bf.array()));
// 只有第一次通信返回消息
if(isFirst){
isFirst = false;
ByteBuffer bst = ByteBuffer.wrap("Hi!".getBytes());
channel.write(bst);
}
}
//写数据
if(key.isWritable()){
Scanner scanner = new Scanner(System.in);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String msg = sdf.format(new Date()) + "\t" + scanner.nextLine();
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer bst = ByteBuffer.wrap(msg.getBytes());
channel.write(bst);
// key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);// 取消写就绪,否则会一直触发写就绪
}
iterator.remove(); } }
} public static void main(String[] args) {
try {
Server server = new Server(9999);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} }
package com.demo.nio; import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set; public class Client { private SocketChannel sc = null;
private Selector selector = null; public Client(int port) throws IOException {
sc = SocketChannel.open();
selector = Selector.open();
sc.connect(new InetSocketAddress(InetAddress.getLocalHost(), port));
sc.configureBlocking(false);
sc.register(selector, SelectionKey.OP_CONNECT | SelectionKey.OP_READ | SelectionKey.OP_WRITE);
ByteBuffer bf = ByteBuffer.wrap("Hello".getBytes());
sc.write(bf); listener(selector);
} private void listener(Selector selector) throws IOException{
while(true){
selector.select();
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectedKeys.iterator(); while(iterator.hasNext()){
SelectionKey key = iterator.next();
if(key.isReadable()){
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer dst = ByteBuffer.allocate(1024);
channel.read(dst);
System.out.println("来自服务器:" + new String(dst.array()));
}
if(key.isWritable()){
Scanner scanner = new Scanner(System.in);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String msg = sdf.format(new Date()) + "\t" + scanner.nextLine();
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer bst = ByteBuffer.wrap(msg.getBytes());
channel.write(bst);
}
iterator.remove();
} }
} public static void main(String[] args) {
try {
Client client = new Client(9999);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} }
上面例子的问题在注释里已经详细描述了,不信可以运行一下,下面是修正版,把写操作放在一个独立的线程里
package com.demo.nio; import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set; /**
* 修正版
*/
public class ServerRevision { private boolean isFirst = true;
private ServerSocketChannel ssc = null;
private Selector selector = null; public ServerRevision(int port) throws IOException {
ssc = ServerSocketChannel.open();
selector = Selector.open();
InetSocketAddress inetAddress = new InetSocketAddress(InetAddress.getLocalHost(), port); ssc.socket().bind(inetAddress);
ssc.configureBlocking(false);
ssc.register(selector, SelectionKey.OP_ACCEPT);
listener(selector);
} private void listener(Selector selector) throws IOException{
while(true){
System.out.println("等待客户端连接...");
selector.select();
System.out.println("捕获客户端连接...");
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectedKeys.iterator(); while (iterator.hasNext()) {
SelectionKey key = iterator.next();
//连接事件
if(key.isAcceptable()){
ServerSocketChannel channel = (ServerSocketChannel) key.channel();
channel.accept().configureBlocking(false).register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
//System.out.println(channel.toString() + "-已连接");
}
//读数据
if(key.isReadable()){
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer bf = ByteBuffer.allocate(1024);
channel.read(bf);
System.out.println("来自客户端数据:" + new String(bf.array()));
// 只有第一次通信返回消息
if(isFirst){
isFirst = false;
ByteBuffer bst = ByteBuffer.wrap("Hi!".getBytes());
channel.write(bst);
}
}
//写数据
if(key.isWritable()){
System.out.println("[服务器]写就绪...");
new Thread(new DealWrite(key)).start();
key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);// 取消写就绪,否则会一直触发写就绪
}
iterator.remove(); } }
} public static void main(String[] args) {
try {
ServerRevision server = new ServerRevision(9999);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} } class DealWrite implements Runnable{ private SelectionKey key; public DealWrite(SelectionKey key) {
this.key = key;
} @Override
public void run() {
while (true){
Scanner scanner = new Scanner(System.in);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String msg = sdf.format(new Date()) + "\t" + scanner.nextLine();
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer bst = ByteBuffer.wrap(msg.getBytes());
try {
channel.write(bst);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
package com.demo.nio; import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set; /**
* 修正版
*/
public class ClientRevision { private SocketChannel sc = null;
private Selector selector = null; public ClientRevision(int port) throws IOException {
sc = SocketChannel.open();
selector = Selector.open();
sc.connect(new InetSocketAddress(InetAddress.getLocalHost(), port));
sc.configureBlocking(false);
sc.register(selector, SelectionKey.OP_CONNECT | SelectionKey.OP_READ | SelectionKey.OP_WRITE);
ByteBuffer bf = ByteBuffer.wrap("Hello".getBytes());
sc.write(bf); listener(selector);
} private void listener(Selector selector) throws IOException{
while(true){
selector.select();
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectedKeys.iterator(); while(iterator.hasNext()){
SelectionKey key = iterator.next();
if(key.isReadable()){
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer dst = ByteBuffer.allocate(1024);
channel.read(dst);
System.out.println("来自服务器:" + new String(dst.array()));
}
if(key.isWritable()){
System.out.println("[客户端]写就绪...");
new Thread(new DealWrite(key)).start();
key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);// 取消写就绪,否则会一直触发写就绪
}
iterator.remove();
} }
} public static void main(String[] args) {
try {
ClientRevision client = new ClientRevision(9999);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} }
目前只是测试了服务器-客户端一对一的通信,不知道一个服务器对多个客户端会出什么bug
NIO稍微有些复杂吧,不过核心的就三个Selector、Channel、Buffer,NIO是同步非阻塞的。
Java IO 与 NIO 服务器&客户端通信小栗子的更多相关文章
- 如何解读 Java IO、NIO 中的同步阻塞与同步非阻塞?
原文链接:如何解读 Java IO.NIO 中的同步阻塞与同步非阻塞? 一.前言 最近刚读完一本书:<Netty.Zookeeper.Redis 并发实战>,个人觉得 Netty 部分是写 ...
- JAVA基础知识之网络编程——-TCP/IP协议,socket通信,服务器客户端通信demo
OSI模型分层 OSI模型是指国际标准化组织(ISO)提出的开放系统互连参考模型(Open System Interconnection Reference Model,OSI/RM),它将网络分为七 ...
- Java IO 和 NIO
昨天面试问到了有关Java NIO的问题,没有答上来.于是,在网上看到了一篇很有用的系列文章讲Java IO的,浅显易懂.后面的备注里有该系列文章的链接.内容不算很长,需要两个小时肯定看完了,将该系列 ...
- java IO和NIO 的区别
Java NIO和IO的主要区别 下表总结了Java NIO和IO之间的主要差别. IO NIO 面向流 面向缓冲 阻塞IO 非 ...
- java的服务端与客户端通信(2)
一.Socket连接与HTTP连接 1.1Socket套接字 套接字(socket)是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元.它是网络通信过程中端点的抽象表示,包含进行网络通信 ...
- java的服务端与客户端通信(1)
一.理解socket 1.1什么是socket? socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄.应用程序通常通过"套接字"向网络 ...
- 漫谈Java IO之 NIO那些事儿
前面一篇中已经介绍了基本IO的使用以及最简单的阻塞服务器的例子,本篇就来介绍下NIO的相关内容,前面的分享可以参考目录: 网络IO的基本知识与概念 普通IO以及BIO服务器 NIO的使用与服务器Hel ...
- Java IO、NIO、AIO知识总结
本文主要讲述下自己对IO的理解,对IO的用法和细则可能没有顾虑到. 本文的理解基于以下几篇文章,他们对各自部分都讲的很细,对我理解IO提供了很大帮助. https://www.cnblogs.com/ ...
- 关于Java IO与NIO知识都在这里
由于内容比较多,我下面放的一部分是我更新在我的微信公众号上的链接,微信排版比较好看,更加利于阅读.每一篇文章下面我都把文章的主要内容给列出来了,便于大家学习与回顾. Java面试通关手册(Java学习 ...
随机推荐
- Nginx 代理到Jetty 页面跳转端口改变问题
Nginx安装 Windows下部署Nginx只需下载安装包,解压启动服务器即可.下载官网:http://nginx.org/en/download.html 操作Nginx首先进入安装文件夹: 查看 ...
- JS对象创建模式
JS的对象创建模式 1.Object构造函数模式 var person = new Object(); person.name = 'name'; person.age = 43; console.l ...
- Django 基础篇(二)视图与模板
视图 在django中,视图对WEB请求进行回应 视图接收reqeust对象作为第一个参数,包含了请求的信息 视图就是一个Python函数,被定义在views.py中 #coding:utf- fro ...
- 【JZOJ6230】【20190625】梦批糼
题目 一个$n\times m \times l $的立方体,有一些位置有障碍 一次操作会随机选择一个立方体,共有\(w\)次操作 询问所有操作都不选到障碍点,被选到至少一次的点的期望 $n ,m,l ...
- JS中判断空对象
js 判断空对象 首先要区分一个概念,空对象和空引用: 空对象:{}是指不含任何属性的对象,当然对象属性包括字面值和函数. 空引用:obj=null 是指变量值指向null变量,当然在js默认不赋值的 ...
- Luogu P2447 [SDOI2010]外星千足虫 高斯消元
链接 给出的条件是异或类型的方程,可以直接用bitset优化高斯消元. 至于求K,在高斯消元时记录用到的最大的方程的编号即可. 代码: // luogu-judger-enable-o2 #inclu ...
- 菜鸟网络Java面试-社招-一面(2019/11)
个人情况 2017年毕业,普通本科,计算机科学与技术专业,毕业后在一个二三线小城市从事Java开发,2年Java开发经验.做过分布式开发,没有高并发的处理经验,平时做To G的项目居多.写下面经是希望 ...
- Spring中静态方法中使用@Resource注解的变量
开发中,有些时候可能会工具类的静态方法,而这个静态方法中又使用到了@Resource注解后的变量.如果要直接使用 Utils.staticMethod(),项目会报异常:如果不直接使用,还要先 new ...
- 这个中秋,我用 Java 画了一个月饼!
栈长代表微信公众号 "Java技术栈" 祝所有粉丝中秋佳节快乐! 为了用一种特殊的方式表达我的心意,去年中秋节,我写了这篇文章: 为了写中秋这篇文章,我学了 20 种编程语言! 没 ...
- BERT模型
BERT模型是什么 BERT的全称是Bidirectional Encoder Representation from Transformers,即双向Transformer的Encoder,因为de ...