从Socket入门到BIO,PIO,NIO,multiplexing,AIO(未完待续)
Socket入门
最简单的Server端读取Client端内容的demo
public class Server {
public static void main(String [] args) throws Exception{
ServerSocket ss = new ServerSocket(9000);
Socket s = ss.accept();
InputStream input = s.getInputStream();
byte[] bytes = new byte[1024];
int len = input.read(bytes);
System.out.println(new String(bytes,0,len));
}
}
打开浏览器,输入localhost:9000

可以看到控制台输出了如下内容(HTTP请求头)

或者命令行输入:telnet localhost 9000 , 然后按下ctrl+] 然后再输入: send 发送的内容 , 然后就可以再IDEA控制台看到send的内容了.
但是现在手头是Mac系统,不知道为啥send不好使....windows下测过,肯定好使.
最简单的Server端写入到Client端内容的demo
public class Server2 {
public static void main(String[] args) throws Exception {
ServerSocket ssocket = new ServerSocket(9000);
Socket socket = ssocket.accept();
OutputStream os = socket.getOutputStream();
os.write("http/1.0 200 OK\nContent-Type:text/html;charset:GBK\n\nhello".getBytes());
os.flush();
os.close();
}
}
可以在浏览器输入localhost:9000来请求内容 , http响应头的那部分会被浏览器解析掉.所以只输出一段hello

或者可以使用telnet localhost 9000来访问, 得到的结果就是那段写入的内容

最简单的Client端写入到Server端的Demo
public class Client {
public static void main(String[] args) throws Exception{
Socket socket = new Socket("localhost",9000);
OutputStream output = socket.getOutputStream();
output.write("你好".getBytes());
output.close();
socket.close();
}
}
注:需要先运行上面的Server类, 然后再运行这个Client. 然后再点击Server的控制台标签, 就会发现Server类的控制台输已经出了"你好"字样.

最简单的用Client端来模拟浏览器http请求Demo
用socket作为Client来模拟浏览器访问网站, 来获取网站的html内容.以www.sohu.com 为例...为什么选sohu呢? 你试试百度,会报302....
public class Client2 {
public static void main(String[] args) throws Exception {
Socket socket = new Socket("www.sohu.com", 80);
InputStream input = socket.getInputStream();
OutputStream output = socket.getOutputStream();
StringBuilder str = new StringBuilder();
//http协议中请求行,必须,不然不会被识别为HTTP
str.append("GET / HTTP/1.1\r\n");
//http协议中的请求头
str.append("Host: www.sohu.com\r\n");
str.append("Connection: Keep-Alive\r\n");
// 用于模拟浏览器的user-agent
str.append("user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36\r\n");
//这里一定要一个回车换行,表示消息头完,不然服务器会一直等待,认为客户端没发送完
str.append("\r\n");
byte[] bytes = new byte[1024];
output.write(str.toString().getBytes());
while (true) {
int len = input.read(bytes);
if (len > 0) {
String result = new String(bytes, 0, len);
System.out.println(result);
} else {
break;
}
}
}
}
运行后, 前面是http响应头, 后面是html代码

改进Server类, 循环读取
前面提到的"最简单的Server端读取Client端内容的demo"这段代码, 也就是Server类. 其实不管客户端发来多少内容, 都只能读取1024字节以内的数据. 因为代码就是这么写的....
下面进行改进, 让他循环读取, 直到读完为止.
public class Server3 {
public static void main(String[] args) throws Exception {
ServerSocket ss = new ServerSocket(9000);
Socket s = ss.accept();
InputStream input = s.getInputStream();
byte[] bytes = new byte[1024];
while (true) {
int len = input.read(bytes);
// 如果读到了内容,说明得输出啊
if (len > 0) {
System.out.println(new String(bytes, 0, len));
}
// 如果没读取到内容,或者没读满bytes数组 说明读完了, 不用再读下一次了, 该退出循环了.
if (len < bytes.length) {
break;
}
}
}
}
可以配合着用前文中的Client类来进行测试.先跑Server来监听端口, 再运行Client发送请求. 去看Server类的控制台是否输出相应的内容.
将服务器改为循环运行
之前是Server响应一个客户端就终止了,这回用while(true)来让Server不停地进行服务.
public class Server4 {
public static void main(String[] args) throws Exception {
ServerSocket ss = new ServerSocket(9000);
while (true) {
Socket s = ss.accept();
InputStream input = s.getInputStream();
byte[] bytes = new byte[1024];
while (true) {
int len = input.read(bytes);
// 如果读到了内容,说明得输出啊
if (len > 0) {
System.out.println(new String(bytes, 0, len));
}
// 如果没读取到内容,或者没读满bytes数组 说明读完了, 不用再读下一次了, 该退出循环了.
if (len < bytes.length) {
break;
}
}
}
}
}
可以配合着用前文中的Client类来进行测试.先跑Server来监听端口, 再运行Client发送请求. 去看Server类的控制台是否输出相应的内容.
Server运行一次就行, 一直在提供服务. 而Client这回可以运行多次了, Client运行几次, Server的控制台下就会收到几个'你好'.
BIO
同步阻塞IO, 每个线程都处理着一个客户端.客户端与线程数是1:1
public class Server5 {
public static void main(String[] args) throws Exception {
ServerSocket ss = new ServerSocket(9000);
System.out.println("服务端启动");
// 循环着监听
while (true) {
Socket s = ss.accept();
System.out.println("接收到客户端");
// 一旦接收到客户端,就开一个线程
new Thread(() -> {
//为了让代码简短,try多包一些代码...
try {
InputStream input = s.getInputStream();
OutputStream output = s.getOutputStream();
byte[] bytes = new byte[1024];
while (true) {
int len = input.read(bytes);
// 如果读到了内容,说明得输出啊
if (len > 0) {
System.out.println(new String(bytes, 0, len));
}
// 如果没读取到内容,或者没读满bytes数组 说明读完了, 不用再读下一次了, 该退出循环了.
if (len < bytes.length) {
break;
}
}
output.write("http/1.1 200 OK\nContent-Type:text/html;charset:GBK\n\nhello".getBytes());
output.flush();
input.close();
output.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
//...其实close应该出现在这里...
System.out.println("断开连接");
}
}).start();
}
}
}
可以在浏览器上用两个标签栏来访问localhost:9000来进行测试.
PIO
伪异步IO,为了避免Server5无限制地开一个新线程,使用线程池来统一管理线程.
public class Server6 {
public static void main(String[] args) throws Exception {
ServerSocket ss = new ServerSocket(9000);
System.out.println("服务端启动");
ExecutorService pool = Executors.newFixedThreadPool(40);
// 循环着监听
while (true) {
Socket s = ss.accept();
System.out.println("接收到客户端");
// 一旦接收到客户端,就放入线程池
pool.submit(() -> {
//为了让代码简短,try多包一些代码...
try {
InputStream input = s.getInputStream();
OutputStream output = s.getOutputStream();
byte[] bytes = new byte[1024];
while (true) {
int len = input.read(bytes);
// 如果读到了内容,说明得输出啊
if (len > 0) {
System.out.println(new String(bytes, 0, len));
}
// 如果没读取到内容,或者没读满bytes数组 说明读完了, 不用再读下一次了, 该退出循环了.
if (len < bytes.length) {
break;
}
}
output.write("http/1.1 200 OK\nContent-Type:text/html;charset:GBK\n\nhello".getBytes());
output.flush();
input.close();
output.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
//...其实close应该出现在这里...
System.out.println("断开连接");
}
});
}
}
}
NIO
首先推荐一本书,讲的详细:

nio博客 http://ifeve.com/java-nio-all/ ,这个更适合快速入门,但原理最好还是看书..讲的很详细
所以相关原理就不在这里粘贴了....再整理也没人家讲的全面易懂,这本书还是非常推荐看的,一开始我就是在网上看各种博客(当时也知道这本书), 但直到耐下心看了这本书,才发现很多地方这里讲的非常详细, 这本书是基于api讲的, 以后需要分析看源码的话还是借助博客更好一些,网上有很多大神的源码分析
public class NIOServer {
private static int BUFFER_SIZE = 1024;
private static int PORT = 9000;
public static void main(String[] args) throws Exception {
Selector selector = Selector.open();
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.socket().bind(new InetSocketAddress(PORT));
serverSocketChannel.configureBlocking(false);
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
selector.select();
Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
while (iter.hasNext()) {
SelectionKey key = iter.next();
iter.remove();
if (key.isAcceptable()) {
SocketChannel socketChannle = serverSocketChannel.accept();
socketChannle.configureBlocking(false);
socketChannle.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(BUFFER_SIZE));
} else if (key.isReadable()) {
SocketChannel socketChannel = (SocketChannel) key.channel();
ByteBuffer buf = (ByteBuffer) key.attachment();
// 读浏览器发送的HTTP请求
long bytesRead = socketChannel.read(buf);
while (bytesRead > 0) {
buf.flip();
while (buf.hasRemaining()) {
System.out.print((char) buf.get());
}
System.out.println();
buf.clear();
bytesRead = socketChannel.read(buf);
}
//向浏览器返回HTTP请求
buf.put("http/1.1 200 OK\nContent-Type:text/html;charset:GBK\n\nhello".getBytes());
socketChannel = (SocketChannel) key.channel();
buf.flip();
while (buf.hasRemaining()) {
socketChannel.write(buf);
}
// 关闭
socketChannel.close();//这样浏览器才不继续拉取
key.cancel();
}
}
}
}
}
然后再浏览器中访问localhost:9000即可看到"hello"
或者用普通SocketClient来访问
或者用如下的NIOClient访问:
public class NIOClient {
public static void main(String[] args) throws Exception {
ByteBuffer buffer = ByteBuffer.allocate(1024);
SocketChannel socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress("localhost", 9000));
if (socketChannel.finishConnect()) {
//向服务器写入
for (int i = 0; i < 3; i++) {
String info = "hello:<" + i + ">";
buffer.clear();
buffer.put(info.getBytes());
buffer.flip();
while (buffer.hasRemaining()) {
socketChannel.write(buffer);
}
}
//从服务器读取
buffer.clear();
long bytesRead = socketChannel.read(buffer);
while (bytesRead > 0) {
buffer.flip();
while (buffer.hasRemaining()) {
System.out.print((char) buffer.get());
}
System.out.println();
buffer.clear();
bytesRead = socketChannel.read(buffer);
}
}
}
}
未完待续....
从Socket入门到BIO,PIO,NIO,multiplexing,AIO(未完待续)的更多相关文章
- JAVA SOCKET 通信总结 BIO、NIO、AIO ( NIO 2) 的区别和总结
1 同步 指的是用户进程触发IO操作并等待或者轮询的去查看IO操作是否就绪 自己上街买衣服,自己亲自干这件事,别的事干不了.2 异步 异步是指用户进程触发IO操作以后便开始做自己的事情,而当IO操作已 ...
- 从Socket入门到BIO,NIO,multiplexing,AIO
Socket入门 最简单的Server端读取Client端内容的demo public class Server { public static void main(String [] args) t ...
- BIO、NIO、AIO入门认识
同步.异步.阻塞.非阻塞概念理解. 同步: 比如在执行某个逻辑业务,在没有得到结果之前一直处于等待阻塞状态,得到结果后才继续执行 异步: 比如在执行某个逻辑业务,在没有得到结果可以去干其他的事情,等待 ...
- Java IO模型:BIO、NIO、AIO
Java IO模型:BIO.NIO.AIO 本来是打算直接学习网络框架Netty的,但是先补充了一下自己对Java 几种IO模型的学习和理解.分别是 BIO.NIO.AIO三种IO模型. IO模型的基 ...
- BIO与NIO、AIO的区别
IO的方式通常分为几种,同步阻塞的BIO.同步非阻塞的NIO.异步非阻塞的AIO. 一.BIO 在JDK1.4出来之前,我们建立网络连接的时候采用BIO模式,需要先在服务端启动一个Serve ...
- Java提高班(五)深入理解BIO、NIO、AIO
导读:本文你将获取到:同/异步 + 阻/非阻塞的性能区别:BIO.NIO.AIO 的区别:理解和实现 NIO 操作 Socket 时的多路复用:同时掌握 IO 最底层最核心的操作技巧. BIO.NIO ...
- [转帖] BIO与NIO、AIO的区别
培训里面讲的东西 自己查了下 啥意思,,, 转帖强化一下. http://blog.csdn.net/skiof007/article/details/52873421 IO的方式通常分为几种,同步 ...
- JDK中关于BIO,NIO,AIO,同步,异步介绍
在理解什么是BIO,NIO,AIO之前,我们首先需要了解什么是同步,异步,阻塞,非阻塞.假如我们现在要去银行取钱: 同步 : 自己亲自出马持银行卡到银行取钱(使用同步IO时,Java自己处理IO读写) ...
- BIO与NIO、AIO的区别(这个容易理解)
转自:http://blog.csdn.net/skiof007/article/details/52873421 BIO与NIO.AIO的区别(这个容易理解) IO的方式通常分为几种,同步阻塞的BI ...
随机推荐
- WebRtc编译好的vs2015源码
一直想看webrtc的源码,苦于FQ能力有限且整个编译过程耗时巨大,故求助于互联网.在互联网寻找许久编译好的Webrtc源码,好多版本下载下来总是报各种错误,很是失落. 皇天不负有心人,终于寻得一版可 ...
- LeetCode算法题-Construct String from Binary Tree(Java实现)
这是悦乐书的第273次更新,第288篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第141题(顺位题号是606).构造一个字符串,该字符串由二叉树中的括号和整数组成,并具 ...
- 黏包现象之TCP
老师的博客:http://www.cnblogs.com/Eva-J/articles/8244551.html#_label5 server #_*_coding:gbk*_ from socket ...
- 解决Error response from daemon: Get https://registry-1.docker.io/v2/library/hello-world/manifests/
https://blog.csdn.net/quanqxj/article/details/79479943
- vue 应用生产环境的 webpack 打包配置优化
转:https://blog.csdn.net/robin_star_/article/details/83856363 前言:很好的打包优化的帖子,还没来的急去实测验证 1. 去掉 console ...
- c++11の简单线程管理
1.简单的例子 #include "stdafx.h" #include <iostream> #include <thread> void functio ...
- 前端——JavaScript
何谓JavaScript?它与Java有什么关系? JavaScript与HTML.CSS组合使用应用于前端开发,JavaScript是一门独立的语言,浏览器内置了JS的解释器.它除了和Java名字长 ...
- 理解koa-router 路由一般使用
阅读目录 一:理解koa-router一般的路由 二:理解koa-router命名路由 三:理解koa-router多个中间件使用 四:理解koa-router嵌套路由 五:分割路由文件 回到顶部 一 ...
- js修改父子json格式成树状结构格式
js修改父子json成树状结构 var json = [ { "id" : "01", "pId":"" } , { & ...
- C# 远程获取图片二进制
直接上代码, 紧做记录. public byte[] GetByteByImgUrl() { System.Net.WebRequest webreq = System.Net.WebRequest. ...