想要学习Java的Socket通信,首先要学习Java的IO和NIO基础,这方面可以阅读《Java NIO 系列教程》。

下面展示自己代码熟悉Java的NIO编程的笔记。

1、缓冲区(Buffer)

/*
 * 一、缓冲区(Buffer):在Java 中负责数据的存取。缓冲区就是数组。用于存储不同数据类型的数据
 *       
 *       根据数据类型不同(boolean除外),提供了相应类型的缓冲区
 *         ByteBuffer 那么实际上最常用的,因为网络传输的字节就是Byte
 *       CharBuffer
 *         ShortBuffer
 *         IntBuffer
 *         LongBuffer
 *       FloatBuffer
 *       DoubleBuffer
 *     
 *    上述缓冲区的管理方式几乎一致,通过allocate()获取缓冲区
 *    
 *  二、缓冲区存储数据的两个核心方法
 *      put()  : 存入数据到缓冲区中
 *      get()  : 获取缓冲区中的数据
 *      
 *  三、缓冲区中的四个核心属性
 *      capacity : 容量,表示缓冲区中最大存储数据的容量
 *      limit    : 界限,标识缓冲区中可以操作数据的大小(limit后面的数据不能进行读写的)
 *      position : 位置,标识缓冲区中正在操作数据的位置
 *      
 *      mark     : 标记,标识记录当前position的位置,可以通过reset()恢复到mark的位置
 *  
 *      0 <= mark <= position <= limit <= capacity
 *  四、直接缓冲区和非直接缓冲区
 *      非直接缓冲区: 通过allocate()方法分配缓冲区,将缓冲区建立在JVM的内存中。
 *      直接缓冲区 :  通过allockateDirect()方法分配直接缓存区,将缓冲区建立在物理内存中。可以提高效率。
 *                  如果有数据需要一直在内存空间中重复用,并且数据量不大的情况下,就用allockateDirect()这种方法。
 *                  毕竟内存空间是有限的。
 */

相关代码:

 package com.demo.test;

 import java.nio.ByteBuffer;

 public class TestBuffer {

     public void test3(){
// 分配直接缓冲区
ByteBuffer buf = ByteBuffer.allocate(1024);
System.out.println(buf.isDirect());// 判断这个缓冲区是直接还是非直接的。
} public void test2(){
String str = "abcde"; // 1、分配一个指定大小的缓冲区
ByteBuffer buf = ByteBuffer.allocate(1024); // 2、利用put()存入数据到缓冲区中
System.out.println("往buf插入所有字符串的bytes是:"+str.getBytes());
buf.put(str.getBytes()); buf.flip();// 将插入模式转为查询模式,就是查询position位置会回到0 System.out.println("创建和缓冲区等长度的字节数组,用来从缓冲区取出字节并存储以待读取操作");
byte[] dst = new byte[buf.limit()];
System.out.println("从缓冲区中去除0开始的2位字节数据放进数组dst中");
buf.get(dst, 0, 2);
System.out.println("打印dst字节数组"+new String(dst, 0, 2));
System.out.println("现在缓冲区的操作起始位置:"+buf.position()); // mark(); 标记
System.out.println("标记");
buf.mark();
buf.get(dst, 2, 2);// 取出从2开始的2位字节数据放进
System.out.println("打印dst字节数组"+new String(dst, 2, 2));
System.out.println("现在缓冲区的操作起始位置:"+buf.position()); // reset(); 回复到mark的位置
buf.reset();
System.out.println("reset重置之后,缓冲区的操作起始位置回到:"+buf.position()); // 判断缓冲区中是否还有剩余的数据
if(buf.hasRemaining()){
// 获取缓冲区还可以操作的数量
System.out.println("缓冲区还有可以操作的数量"+buf.remaining());
}
} public void test(){
String str = "abcde"; // 1、分配一个指定大小的缓冲区
ByteBuffer buf = ByteBuffer.allocate(1024);
System.out.println("--调用allocate之后--");
System.out.println(buf.capacity());
System.out.println(buf.limit());
System.out.println("position:"+buf.position()); // 2、利用put()存入数据到缓冲区中
System.out.println("字符串的bytes是:"+str.getBytes());
buf.put(str.getBytes()); System.out.println("--调用put之后--");
System.out.println(buf.capacity());
System.out.println(buf.limit());
System.out.println("position:"+buf.position()); // 3、前面是存入数据的模式,存入5个字节之后,position的位置从原本0现在到5了
buf.flip();
// 现在要将存储数据模式改成读取模式,position的位置会变回为0 System.out.println("--调用flip切换模式之后--");
System.out.println(buf.capacity());
System.out.println(buf.limit());
System.out.println("position:"+buf.position()); // 4、利用get()读取缓冲区中的数据
byte[] dst = new byte[buf.limit()];// 创建和缓冲区一样大的字节数据
buf.get(dst);
System.out.println(new String(dst,0,dst.length)); System.out.println("--调用get()切换模式之后--");
System.out.println(buf.capacity());
System.out.println(buf.limit());
System.out.println("position:"+buf.position());// 打印输出的结果会发现position变回5了 // 5、可重复读取
buf.rewind(); System.out.println("--调用rewind()切换模式之后--");
System.out.println(buf.capacity());
System.out.println(buf.limit());
System.out.println("position:"+buf.position()); // 6、清空缓冲区。但是缓冲区中的数据依然存在,但是出于“被遗忘”状态
buf.clear(); System.out.println("--调用clear()切换模式之后--");
System.out.println(buf.capacity());
System.out.println(buf.limit());
System.out.println("position:"+buf.position()); // 看看缓冲区有没有数据
System.out.println((char)(buf.get()+1)); }
}

2、通道(Channel)

由java.nio.channels包定义的。Channel表示IO源与目标打开的连接。Channel类似于传统的“流”。只不过Channel本身不能直接访问数据,Channel只能与Buffer进行交互。

先完成文件的复制:

 /*
* 一、通道(Channel):用于源节点与目标节点的连接。在Java NIO中负责缓冲区中数据的传输。
* Channel本身不存储数据,因此需要配合缓冲区进行传输
*
* 二、通道的主要实现类
* java.nio.channels.Channel接口
* |--FileChannel
* |--SocketChannel
* |--ServerSocketChannel
* |--DatagramChannel
*
* 三、获取通道
* 1、Java针对支持通道的类提供了getChannel()方法
* 本地:
* FileInputStream/FileOutputStream
* RandomAccessFile随机存储文件流
* 网络:
* Socket
* ServerSocket
* DatagramSocket
*
* 2、在JDK1.7中的NIO.2针对各个通道提供了一个静态的方法
* 3、在JDK1.7中的NIO.2的Files工具类的newByteChannel()
*
* 四、通道之间的数据传输
* transferFrom()
* transferTo()
*
* 五、分散(Scatter)和聚集(Gather)
* 分散读取(Scattering Reads):将通道中的数据分散到多个缓冲区中
* 聚集写入(Gathering Writes):将多个缓冲区中的数据聚集到通道中
*/
public class TestChannel {
// 1、利用通道完成文件的复制(非直接缓冲区)
public void test(){
long start = System.currentTimeMillis(); FileInputStream fis = null;
FileOutputStream fos = null;
// 获取通道
FileChannel inChannel = null;
FileChannel outChannel = null;
try {
fis = new FileInputStream("1.png");
fos = new FileOutputStream("2.png");
inChannel = fis.getChannel();
outChannel = fos.getChannel();
// 分批额指定大小的缓冲区
ByteBuffer buf = ByteBuffer.allocate(1024); // 将通道中的数据存入缓冲区中
while(inChannel.read(buf)!=-1){
buf.flip();//切换读取数据的模式
// 将缓冲区中的数据写入通道中
outChannel.write(buf);
buf.clear();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if(outChannel != null){
try {
outChannel.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(inChannel != null){
try {
inChannel.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(fos != null){
try {
fos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(fis != null){
try {
fis.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
long end = System.currentTimeMillis();
System.out.println("1、耗费的时间:"+(end - start)); } // 2、利用通道完成文件的复制(直接缓冲区,内存映射文件的方式)
public void test2(){ long start = System.currentTimeMillis(); FileChannel inChannel = null;
// CREATE_NEW 存在就报错
FileChannel outChannel = null;
try {
inChannel = FileChannel.open(Paths.get("1.png"), StandardOpenOption.READ);
outChannel = FileChannel.open(Paths.get("3.png"), StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE); // 物理内存映射文件
MappedByteBuffer inMapByteBuffer = inChannel.map(MapMode.READ_ONLY,0,inChannel.size());
MappedByteBuffer outMapByteBuffer = outChannel.map(MapMode.READ_WRITE,0,inChannel.size()); // 直接对缓冲区进行数据的读写操作
byte[] dst = new byte[inMapByteBuffer.limit()];
inMapByteBuffer.get(dst);
outMapByteBuffer.put(dst);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally{
if(inChannel!=null){
try {
inChannel.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(outChannel!=null){
try {
outChannel.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} long end = System.currentTimeMillis();
System.out.println("2、耗费的时间:"+(end - start)); } // 通道之间的数据传输
public void test3(){
FileChannel inChannel = null;
// CREATE_NEW 存在就报错
FileChannel outChannel = null; try {
inChannel = FileChannel.open(Paths.get("1.png"), StandardOpenOption.READ);
outChannel = FileChannel.open(Paths.get("4.png"), StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE); // inChannel.transferTo(0, inChannel.size(), outChannel);
outChannel.transferFrom(inChannel, 0, inChannel.size());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally{
if(inChannel!=null){
try {
inChannel.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(outChannel!=null){
try {
outChannel.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} } // 分散(Scatter)和聚集(Gather)
public void test4(){
// 文件路径: webapp/index.html
try {
RandomAccessFile raFile = new RandomAccessFile("webapp/index.html", "rw");
FileChannel fileChannel = raFile.getChannel(); ByteBuffer tBuffer = ByteBuffer.allocate(24);
ByteBuffer tBuffer2 = ByteBuffer.allocate(80); // 分散读取到两个字节缓冲区数组中
ByteBuffer[] bBuffers = {tBuffer,tBuffer2};
fileChannel.read(bBuffers);
// 打印之前需要修改一下缓冲区的写入模式为读取模式
for (ByteBuffer byteBuffer : bBuffers) {
byteBuffer.flip();
}
System.out.println(new String(bBuffers[0].array(),0,bBuffers[0].limit()));
System.out.println("===========");
System.out.println(new String(bBuffers[1].array(),0,bBuffers[1].limit())); // 聚集写入到通道中
RandomAccessFile randomAccFile2 = new RandomAccessFile("test.html", "rw");
FileChannel fileChannel2 = randomAccFile2.getChannel(); fileChannel2.write(bBuffers); } catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

>>1、字节缓冲区要么是直接的,要么是非直接的。如果为直接字节缓冲区,则Java虚拟机会尽最大努力直接在 此缓冲区上执行本机 I/O 操作。也就是说,在每次调用基础操作系统的一个本机 I/O 操作之前(或之后), 虚拟机都会尽量避免将缓冲区的内容复制到中间缓冲区中(或从中间缓冲区中复制内容)。

>>2、直接字节缓冲区可以通过调用此类的allocateDirect()工厂方法来创建。此方法返回的缓冲区进行分配和取消 分配所需成本通常高于非直接缓冲区。直接缓冲区的内容可以驻留在常规的垃圾回收堆之外,因此,它们对 应用程序的内存需求量造成的影响可能并不明显。所以,建议将直接缓冲区主要分配给那些易受基础系统的 本机 I/O 操作影响的大型、持久的缓冲区。一般情况下,最好仅在直接缓冲区能在程序性能方面带来明显好 处时分配它们。

>>3、直接字节缓冲区还可以通过FileChannel的map()方法将文件区域直接映射到内存中来创建。该方法返回 MappedByteBuffer 。Java 平台的实现有助于通过 JNI 从本机代码创建直接字节缓冲区。如果以上这些缓冲区 中的某个缓冲区实例指的是不可访问的内存区域,则试图访问该区域不会更改该缓冲区的内容,并且将会在 访问期间或稍后的某个时间导致抛出不确定的异常。

>>4、字节缓冲区是直接缓冲区还是非直接缓冲区可通过调用其isDirect()方法来确定。提供此方法是为了能够在 性能关键型代码中执行显式缓冲区管理。

3、字符集(Charset)

  
    编码:字符串 --> 字节数组 将字符序列转为字节序列的过程叫编码
    解码:字节数组 --> 字符串 将字节序列转为字符序列的过程叫解码

代码:

 // 编码解码
public void test6(){
// 获取字符集合中的GBK字符集
Charset cs1 = Charset.forName("GBK"); // 获取编码器
CharsetEncoder ce = cs1.newEncoder();
// 获取解码器
CharsetDecoder cd = cs1.newDecoder(); // 创建字符串缓冲区
CharBuffer cBuf = CharBuffer.allocate(1024);
cBuf.put("何杨很帅!");
cBuf.flip(); try {
// 开始编码
ByteBuffer bBuf = ce.encode(cBuf);
System.out.println("用GBK编码之后:");
for (int i = 0; i < bBuf.limit(); i++) {
System.out.println(bBuf.get());
} // 然后开始解码回去
bBuf.flip();
CharBuffer cBuf2 = cd.decode(bBuf);
System.out.println("用GBK解码之后:");
System.out.println(cBuf2.toString()); System.out.println("------------"); System.out.println("获取UTF-8字符集");
Charset cs2 = Charset.forName("UTF-8");
bBuf.flip();
CharBuffer cBuf3 = cs2.decode(bBuf);// 也可以直接用字符集进行解码 System.out.println("用字符集进行解码之后:"+cBuf3.toString()); } catch (CharacterCodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} }

4、使用NIO完成网络通信

 /*
* 一、使用NIO完成网络通信的三个核心:
* 1、通道(Channel):负责连接
* java.nio.channels.Channel 接口:
* |--SelectableChannel
* |--ServerSocketChannel
* |--DatagramChannel
*
* |--Pipe.SinkChannel
* |--Pipe.SourceChannel
*
* 2、缓冲区(Buffer):负责数据的存取
*
* 3、选择器(Selector):是SelectableChannel的多路复合器。
* 用于监控SelectableChannel的IO状况
*/

4-1、下面先实现阻塞式通道

 public class TestBlockingNIO {
// 客户端
@Test
public void client(){
try {
System.out.println("客户端开始创建。");
// 1、获取Socket通道
SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898));
// 2、分配指定大小的缓冲区
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
// 3、读取本地文件,并发到服务端
// 先需要文件通道获取本地文件
FileChannel fileChannel = FileChannel.open(Paths.get("2.png"), StandardOpenOption.READ);
System.out.println("开始发送图片数据");
while (fileChannel.read(byteBuffer) != -1) {
byteBuffer.flip();
socketChannel.write(byteBuffer);
byteBuffer.clear();
System.out.println("发送一次");
} System.out.println("--开始停止输出--");
// 4、客户端发送完数据之后要停止通道的输出
socketChannel.shutdownOutput();
// --发送完了图片数据之后才会进行下面一步,其实这里也是单线程阻塞的。--
System.out.println("--发送完了图片数据之后才会进行下面一步,其实这里也是单线程阻塞的。--"); // 5、接收服务端的反馈
int len = 0;
while((len = socketChannel.read(byteBuffer))!=-1){
byteBuffer.flip();
System.out.println(new String(byteBuffer.array(), 0, len));
byteBuffer.clear(); }
socketChannel.shutdownInput();// 停止客户端的接收输入 // 6、关闭通道
fileChannel.close();
socketChannel.close(); } catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} // 服务端
@Test
public void server(){
try {
System.out.println("服务器开始启动。。。");
// 1、获取服务端SocketChannel
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
// 2、绑定连接端口号
serverSocketChannel.bind(new InetSocketAddress(9898));
// 3、获取客户端连接的通道
SocketChannel sChannel = serverSocketChannel.accept(); // 4、创建一个指定大小的缓冲区
ByteBuffer byteBuffer = ByteBuffer.allocate(1024); // 5、接收的数据通道也要文件通道 StandardOpenOption.CREATE表示有文件就覆盖,没文件就创建
FileChannel fileChannel = FileChannel.open(Paths.get("4.png"), StandardOpenOption.WRITE,StandardOpenOption.CREATE); System.out.println("开始接收图片数据");
while (sChannel.read(byteBuffer)!=-1) {
byteBuffer.flip();
fileChannel.write(byteBuffer);
byteBuffer.clear();
System.out.println("接收一次");
} System.out.println("接收完毕客户端发来的数据,开始反馈数据给客户端"); // 6、发送反馈给客户端
byteBuffer.put("服务器接收数据成功".getBytes());
byteBuffer.flip();
sChannel.write(byteBuffer); // 7、关闭通道
fileChannel.close();
sChannel.close();
serverSocketChannel.close(); } catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

4-2、然后是实现非阻塞通道

 package com.demo.test;

 import java.io.IOException;
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.util.Iterator;
import java.util.Scanner; import org.junit.Test; public class TestNonBlockingNIO { @Test
public void startClient(){
SocketChannel sChannel = null;
// 获取通道
try { System.out.println("启动客户端"); sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1",9898)); // 切换非阻塞模式
sChannel.configureBlocking(false); ByteBuffer buf = ByteBuffer.allocate(1024); System.out.println("开始通过输入框输入给服务器端发送数据");
Scanner scan = new Scanner(System.in);
while (scan.hasNext()) {
String str = scan.next();
buf.put(str.getBytes());
buf.flip();
sChannel.write(buf);
buf.clear();
} scan.close(); } catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (sChannel != null) {
try {
sChannel.close();
System.out.println("关闭客户端Socket通道");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
} // 启动非阻塞的NIO的Socket服务器程序
@Test
public void startServer(){
ServerSocketChannel serverSocketChannel = null;
try {
System.out.println("启动服务器Socket。"); // --ServerSocketChannel--
// 直接用 服务器Socket通道类 获取 服务器Socket通道
serverSocketChannel = ServerSocketChannel.open();
// 设置为非阻塞模式
serverSocketChannel.configureBlocking(false);
// 用 服务器Socket通道 来绑定端口号,端口是需要new的对象
serverSocketChannel.bind(new InetSocketAddress(9898)); // --Selector--
// 获取选择器,选择器也是通过类似类方法的open方法创建
Selector sel = Selector.open(); // --注册--
// 服务器Socket通道注册选择器,指定的接收事件
serverSocketChannel.register(sel, SelectionKey.OP_ACCEPT); // --开始处理接收到的--
// 轮询式的获取选择器上已经准备就绪的事件
while (sel.select() > 0) {
// 使用迭代器获取所有的 "已经接收准备就绪的" 选择键
Iterator<SelectionKey> it = sel.selectedKeys().iterator(); while (it.hasNext()) {
SelectionKey selKey = it.next();
// 如果是接收就绪
if (selKey.isAcceptable()) {
System.out.println("接收就绪,接收客户端的Socket通道。");
// 通过服务器Socket通道获取客户端的连接的Socket通道
SocketChannel socketChannel = serverSocketChannel.accept();
// 设置这个客户端发过来的通道为非阻塞模式
socketChannel.configureBlocking(false);
// 再将这个客户端发过来的通道注册选择器,因为已经准备好了,那就注册设置为读就绪状态
socketChannel.register(sel, SelectionKey.OP_READ); // 如果是读就绪状态
}else if (selKey.isReadable()) {
// System.out.println("读就绪,开始读取客户端的Socket通道里的数据。");
// 通过选择器获取这个通道
SocketChannel socketChannel = (SocketChannel) selKey.channel(); // 读取数据
// 创建一个ByteBuffer
ByteBuffer byteBuffer = ByteBuffer.allocate(1024); // 从通道中读取数据到字节缓冲
int len;
while ((len = socketChannel.read(byteBuffer))>0) {
byteBuffer.flip();
System.out.println("开始读取的数据是:");
System.out.println(new String(byteBuffer.array(),0,len));
byteBuffer.clear();
} }
// 记得用完it之后要取消掉
it.remove();
}
} } catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
try {
if (serverSocketChannel != null) {
serverSocketChannel.close();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} }

Java之NIO的更多相关文章

  1. JAVA bio nio aio

    [转自]http://qindongliang.iteye.com/blog/2018539 在高性能的IO体系设计中,有几个名词概念常常会使我们感到迷惑不解.具体如下: 序号 问题 1 什么是同步? ...

  2. java的nio之:java的nio系列教程之buffer的概念

    一:java的nio的buffer==>Java NIO中的Buffer用于和NIO通道Channel进行交互.==>数据是从通道channel读入缓冲区buffer,从缓冲区buffer ...

  3. java的nio之:java的nio系列教程之channel的概念

    一:java的nio的channel Java NIO的通道类似流,但又有些不同: ==>既可以从通道中读取数据,又可以写数据到通道.但流的读写通常是单向的. ==>通道可以异步地读写. ...

  4. java的nio之:java的nio系列教程之概述

    一:java的nio的核心组件?Java NIO 由以下几个核心部分组成: ==>Channels ==>Buffers ==>Selectors 虽然Java NIO 中除此之外还 ...

  5. java之NIO编程

    所谓行文如编程,随笔好比java文件,文章好比类,参考文献是import,那么目录就是方法定义. 本篇文章处在分析thrift的nonblocking server之前,因为后者要依赖该篇文章的知识. ...

  6. 输入和输出--java的NIO

    Java的NIO 实际开发中NIO使用到的并不多,我并不是说NIO使用情景不多,是说我自己接触的并不是很多,前面我在博客园和CSDN上转载了2篇别人写的文章,这里来大致总结下Java的NIO,大概了解 ...

  7. JAVA 探究NIO

    事情的开始 1.4版本开始,java提供了另一套IO系统,称为NIO,(New I/O的意思),NIO支持面向缓冲区的.基于通道的IO操作. 1.7版本的时候,java对NIO系统进行了极大的扩展,增 ...

  8. 理解Java的NIO

    同步与阻塞 同步和异步是针对应用程序和内核的交互而言的. 同步:执行一个操作之后,进程触发IO操作并等待(阻塞)或者轮询的去查看IO的操作(非阻塞)是否完成,等待结果,然后才继续执行后续的操作. 异步 ...

  9. Java通过NIO实现快速文件拷贝的代码

    将内容过程重要的内容片段做个记录,下面的内容段是关于Java通过NIO实现快速文件拷贝的内容. public static void fileCopy( File in, File out ) thr ...

  10. 一个小时就能理解Java的NIO必须掌握这三大要素!

    同步与阻塞 同步和异步是针对应用程序和内核的交互而言的. 同步:执行一个操作之后,进程触发IO操作并等待(阻塞)或者轮询的去查看IO的操作(非阻塞)是否完成,等待结果,然后才继续执行后续的操作. 异步 ...

随机推荐

  1. Sumdiv POJ - 1845 (逆元/分治)

    Consider two natural numbers A and B. Let S be the sum of all natural divisors of A^B. Determine S m ...

  2. a标签下划线

    页面中有一处box中的a标签都被加上了下划线,查找元素却没有找到css中的underline. 原因是 <a>标签默认是有下划线的,而一般看到的<a>标签链接中的下划线都被覆盖 ...

  3. Socket/ServerSocket 选项

    在网络编程中,Socket/ServerSocket有一些选项用来自定义一些行为,现在分享一下.     Socket选项 1.TCP_NODELAY 在Socket发送数据时,默认情况下,数据会先进 ...

  4. MongoDB+php7搭建

    0x00前言: 今天一位非计算机专业的朋友问我怎么打开.bson文件,我第一反应.bson文件是什么,网上查了下是mongodb的传输文件.也就是类似于mysql的.sql文件一样 之前看过mongo ...

  5. Left join on where 区别

    on 后面 直接加条件的话,不会对左边的表产生影响,on条件是在左关联时候的条件,不管如何都会返回左边表中的记录 where 加条件 才会对左边的表 生效.where条件是关联查询之后的条件

  6. 解决ant Design dva ajax跨越请求 (status=0)

    今天实现了antd作为前端展现,python flask作为后端的数据填充,完全两个独立的服务:过程中遇到ajax跨越请求问题,导致status一直等于0,原来是这么写的: xmlhttp.open( ...

  7. 使用Nginx部署静态网站

    这篇文章将介绍如何利用Nginx部署静态网站. 之前写过2篇有关Nginx的文章,一篇是<利用nginx,腾讯云免费证书制作https>,另外一篇是<linux安装nginx> ...

  8. C# SQLiteHelper

    using System; using System.Data; using System.Data.Common; using System.Data.SQLite; using System.IO ...

  9. react-router那些事儿

    切换路由时,控制台警告 Can Only update a mounted or mounting component.this usually means you called setState() ...

  10. 手机端input获取焦点弹出键盘时挡住input解决方案

    问题重现 原始页面:页面中有header.main.footer三部分,其中 header 和 footer 通过 position: fixed; 定位在浏览器顶部和底部. 其中,footer 中有 ...