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,NIO,multiplexing,AIO的更多相关文章

  1. Java BIO NIO 与 AIO

    回顾 上一章我们介绍了操作系统层面的 IO 模型. 阻塞 IO 模型. 非阻塞 IO 模型. IO 复用模型. 信号驱动 IO 模型(用的不多,知道个概念就行). 异步 IO 模型. 并且介绍了 IO ...

  2. 从Socket入门到BIO,PIO,NIO,multiplexing,AIO(未完待续)

    Socket入门 最简单的Server端读取Client端内容的demo public class Server { public static void main(String [] args) t ...

  3. BIO,NIO与AIO的区别

    Java NIO : 同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理.Java AIO(NIO.2 ...

  4. Java的BIO,NIO和AIO的区别于演进

    作者:公众号:我是攻城师 前言 Java里面的IO模型种类较多,主要包括BIO,NIO和AIO,每个IO模型都有不一样的地方,那么这些IO模型是如何演变呢,底层的原理又是怎样的呢? 本文我们就来聊聊. ...

  5. Socket网络通信——IO、NIO、AIO介绍以及区别

    一 基本概念 Socket又称"套接字",应用程序通常通过"套接字"向网路发出请求或者应答网络请求. Socket和ServerSocket类位于java.ne ...

  6. 关于BIO NIO和AIO的理解

    转载自 :http://blog.csdn.net/anxpp/article/details/51512200 1.BIO编程 1.1.传统的BIO编程 网络编程的基本模型是C/S模型,即两个进程间 ...

  7. BIO、NIO、AIO入门认识

    同步.异步.阻塞.非阻塞概念理解. 同步: 比如在执行某个逻辑业务,在没有得到结果之前一直处于等待阻塞状态,得到结果后才继续执行 异步: 比如在执行某个逻辑业务,在没有得到结果可以去干其他的事情,等待 ...

  8. JAVA SOCKET 通信总结 BIO、NIO、AIO ( NIO 2) 的区别和总结

    1 同步 指的是用户进程触发IO操作并等待或者轮询的去查看IO操作是否就绪 自己上街买衣服,自己亲自干这件事,别的事干不了.2 异步 异步是指用户进程触发IO操作以后便开始做自己的事情,而当IO操作已 ...

  9. 也谈BIO | NIO | AIO (Java版--转)

    关于BIO | NIO | AIO的讨论一直存在,有时候也很容易让人混淆,就我的理解,给出一个解释: BIO | NIO | AIO,本身的描述都是在Java语言的基础上的.而描述IO,我们需要从两个 ...

随机推荐

  1. AndroidStudio中导入module(简单版)

    1.把要导入成Mudle的项目修改成符合Library的格式 修改该项目中bulid.gradle文件中第一行代码 把 apply plugin: 'com.android.application' ...

  2. 看eShopOnContainers学一个EventBus

    最近在看微软eShopOnContainers 项目,看到EventBus觉得不错,和大家分享一下 看完此文你将获得什么? eShop中是如何设计事件总线的 实现一个InMemory事件总线eShop ...

  3. Python开发入门14天集训营-第一章

    python第一章 python变量 变量的作用 存数据 被程序调用和操作 标记数据 声明变量 name = "Ydh" 变量名 = 变量值 变量定义规范: 变量名只能是 字母.数 ...

  4. S3 Browser 配置指南

    S3 Browser 相对于s3cmd是一个很方便的操作S3的图形化界面工具. 以下是配置步骤: 下载网址:http://s3browser.com/ keygen破解版: http://appdol ...

  5. 修改android 开机画面

    对于使用安卓手机的人来说,能够自由定制手机的各种界面是每个用户之所以喜欢安卓系统的最根本的缘由,比如手机的开机界面中的bootanimation.zip文件.本文就如何修改开机界面,做一个简单的流程介 ...

  6. List转换成JSON对象报错(三)

    List转换成JSON对象 1.具体错误如下 Exception in thread "main" java.lang.NoClassDefFoundError: net/sf/e ...

  7. VxWorks6.6 pcPentium BSP 使用说明(三):设备驱动

      本文主要介绍了pcPentium BSP中包含的驱动程序.包含了官方提供的所有驱动程序,除了aic7888Lib--现在已用得很少的一个AIC-7888 SCSI控制器的驱动介绍.建议重点阅读at ...

  8. ORA-00904:标识符无效

    1.错误描述 ORA-00904:"TTT"."RN":标识符无效 00904 . 00000 - "%s:invalid identifier&qu ...

  9. 芝麻HTTP:Python爬虫入门之Urllib库的基本使用

    1.分分钟扒一个网页下来 怎样扒网页呢?其实就是根据URL来获取它的网页信息,虽然我们在浏览器中看到的是一幅幅优美的画面,但是其实是由浏览器解释才呈现出来的,实质它是一段HTML代码,加 JS.CSS ...

  10. 关于html5 data-*自定义属性相关注意点和踩过的坑

    在HTML5中添加了data-*的方式来自定义属性,所谓data-*实际上上就是data-前缀加上自定义的属性名,命名可以用驼峰命名方式,但取值是必需全部使用小写(后面会说),使用这样的结构可以进行数 ...