DNS就是一个域名系统,它本质上就是一个分布式数据库
节点流
字节流是8位的,字符流是16位的。
CharArrayReader:从一个char数组中读取流;
StringReader:从一个字符串中读取流。
Writer也是同理。
这些流是运行在基本流之上的
InputStreamReader / OutputStreamWriter 是将字节流转换成字符流,然后再用于其他操作。
比如说BufferedReader,他是运行在FileReader之上的
FileWriter writer = new FileWriter(file);
BufferedWriter bufferedWriter = new BufferedWriter(writer);
InputStreamReader,就是将字节流转换成字符流,然后通过他的子类FileReader读取进来。
网络编程的基本模型是Client/Server模型,也就是两个进程之间进行相互通信,其中服务端提供位置信息(绑定的IP地址和监听端口),客户端通过连接操作向服务端监听的地址发起连接请求,通过三次握手建立连接,如果连接建立成功,双方就可以通过网络套接字(Socket)进行通信。
在基于传统同步阻塞模型开发中,ServerSocket负责绑定IP地址,启动监听端口,Socket负责发起连接操作,连接成功之后,双方通过输入和输出流进行同步阻塞式通信。
网络编程模型有:同步、异步、阻塞、非阻塞
同步阻塞:客户端向服务端发送消息,客户端再等待服务端的回应,服务端也正在处理客户端发来的信息,处理完毕后回应客户端;
同步非阻塞:客户端向服务端发送消息,客户端不需要等待服务端的回应,而服务端正在处理客户端发来的信息,处理完毕后回应客户端;
异步阻塞:客户端向服务端发送消息,客户端再等待服务端的回应,服务端不一定正在处理客户端发来的信息,而是处理手头上的信息;
异步非阻塞:客户端向服务端发送消息,客户端不需要等待服务端的回应,服务端不一定正在处理客户端发来的信息,而是处理手头上的信息;
为了避免多线程的浪费,节省内存空间资源,java提供了线程池,来实现线程的复用。
通过实现ExecutorService接口,来对线程池进行管理。当有一个任务希望使用一个已经被创建好的线程去执行,那么可以把这个任务交给ExecutorService,如果ExecutorService中有空闲下的线程,则会调用该线程去执行,执行结果是一个Future对象,我们可以调用Future的isDone()方法查询线程是否执行完毕,调用get()方法获取线程结果。
Socket是网络通信的端点,也是一种数据源。
当一个主机与其他主机进行交互时,或者进程间的交互,就通过Socket绑定本地的 IP 和 port,然后作为本地的网络通信的端点,进程间通过这个Socket网络通信端点来互相传输数据(IO流)。
服务端
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
?
public class Server {
public static void main(String[] args) {
//定义本机端口号
final int DEFAULT_PORT = 8888;
ServerSocket serverSocket = null;
try {
//绑定监听端口
serverSocket = new ServerSocket(DEFAULT_PORT);
System.out.println("启动服务器,监听端口" + DEFAULT_PORT);
?
while(true){
//serverSocket.accept()返回的是Socet类型
//建立本机通信端点,等待客户端连接,否则一直卡在这里
Socket socket =serverSocket.accept();
System.out.println("客户端【" + socket.getPort() + "】已连接");
//BufferedReader 是建立在流之上的
//InputStreamReader 也是建立流之上的
//socket.getInputStream()接收客户端发来的流数据
BufferedReader reader = new BufferedReader(
new InputStreamReader(socket.getInputStream())
);
//向客户端发送消息
BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream())
);
//读取客户端发送的消息
String msg = reader.readLine();
if(msg != null){
System.out.println("客户端【" + socket.getPort() + "】:" + msg);
?
//回复客户端发送的消息
writer.write("服务器:" + msg + "\n");
//刷新,确保Buffered缓冲区的内容都输出来
writer.flush();
}
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if(serverSocket != null){
try {
serverSocket.close();
System.out.println("服务端关闭连接");
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
客户端
import java.io.*;
import java.net.Socket;
?
public class Client {
public static void main(String[] args) {
//客户端ip地址
final String DEFAULT_SERVER_HOST = "127.0.0.1";
final int DEFAULT_SERVER_PORT = 8888;
Socket socket = null;
BufferedWriter writer = null;
try {
//创建本机通信端点
socket = new Socket(DEFAULT_SERVER_HOST,DEFAULT_SERVER_PORT);
?
?
//读取用户输入信息
BufferedReader consoleReader =
new BufferedReader(new InputStreamReader(System.in));
//获取用户输入的信息
String input = consoleReader.readLine();
?
?
//向服务端发送信息
writer = new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream())
);
//发送
writer.write(input + "\n");
writer.flush();
?
?
//接收服务端的信息
BufferedReader reader = new BufferedReader(
new InputStreamReader(socket.getInputStream())
);
//读取
String msg = reader.readLine();
System.out.println(msg);
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
//再关闭Buffered流的时候会自动调用flush()
writer.close();
System.out.println("客户端关闭连接");
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
在以上的基础上,添加一些功能:
1、客户端可以重复发送信息;
2、客户端要求关闭连接时,客户端和服务端都关闭连接。
服务端
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
?
public class Server {
public static void main(String[] args) {
final String exit = "exit";
//定义本机端口号
final int DEFAULT_PORT = 8888;
ServerSocket serverSocket = null;
try {
//绑定监听端口
serverSocket = new ServerSocket(DEFAULT_PORT);
System.out.println("启动服务器,监听端口" + DEFAULT_PORT);
?
//serverSocket.accept()返回的是Socet类型
//建立本机通信端点,等待客户端连接,否则一直卡在这里
Socket socket = serverSocket.accept();
System.out.println("客户端【" + socket.getPort() + "】已连接");
//BufferedReader 是建立在流之上的
//InputStreamReader 也是建立流之上的
//socket.getInputStream()接收客户端发来的流数据
BufferedReader reader = new BufferedReader(
new InputStreamReader(socket.getInputStream())
);
//向客户端发送消息
BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream())
);
String msg = null;
while ((msg = reader.readLine())!=null) {
System.out.println("客户端【" + socket.getPort() + "】:" + msg);
//回复客户端发送的消息
writer.write("服务器:" + msg + "\n");
//刷新,确保Buffered缓冲区的内容都输出来
writer.flush();
//查看客户端是否退出
if(exit.equals(msg)){
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if(serverSocket != null){
try {
serverSocket.close();
System.out.println("服务端关闭连接");
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
?
客户端
import java.io.*;
import java.net.Socket;
?
public class Client {
public static void main(String[] args) {
//定义客户端退出
final String exit = "exit";
//客户端ip地址
final String DEFAULT_SERVER_HOST = "127.0.0.1";
final int DEFAULT_SERVER_PORT = 8888;
Socket socket = null;
BufferedWriter writer = null;
try {
//创建本机通信端点
socket = new Socket(DEFAULT_SERVER_HOST,DEFAULT_SERVER_PORT);
?
//读取用户输入信息
BufferedReader consoleReader =
new BufferedReader(new InputStreamReader(System.in));
//向服务端发送信息
writer = new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream())
);
//接收服务端的信息
BufferedReader reader = new BufferedReader(
new InputStreamReader(socket.getInputStream())
);
System.out.println("已连接【" + socket.getPort() + "】");
while(true) {
//获取用户输入的信息
String input = consoleReader.readLine();
?
//发送
writer.write(input + "\n");
writer.flush();
?
//读取
String msg = reader.readLine();
System.out.println(msg);
//用户是否退出
if(exit.equals(input)){
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
//再关闭Buffered流的时候会自动调用flush()
writer.close();
System.out.println("客户端关闭连接");
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
?
BIO通信模型图
BIO的服务端通信模型:采用BIO通信模型的服务端,通常由一个独立的Acceptor线程负责监听客户端的连接,它接收到客户端连接请求之后为每个客户端创建一个新的线程进行链路处理,处理完成之后,通过输出流返回应答给客户端,线程销毁。这就是典型的一请求一应答通信模型。
1、ServerSocket.accept():服务端等待客户端连接后才能接着往下运行。
2、InputStream.read(),OutputStream.write():当用户输入时,才会运行,否则一直等待用户输入。
3、无法在同一个线程里处理多个Stream I/O。
1、使用Channel代替Stream,不再使用Stream;
2、使用Selecyor监控多条Channel;
3、可以在一个线程里处理多个Channel I/O。
Channel与Buffer
Channel通道可以进行读写操作,其实是通过Buffer来完成的。向Channel写数据时候,是要先把数据写入到Buffer中,从Channel中读数据时候,Channel是先从Buffer里面读出数据再返回出来。所以我们对Channel的操作都离不开Buffer。
向Buffer写入数据
创建一个Buffer时候就要给他设定一个内存大小。
capacity指针表示整个缓存区的最大容量;
position指针表示目前所操作数据的位置,也就是我写入的时候,是把数据放到position所指向的地方;
limit指针表示切换模式后数据的状态;
写数据操作已经完毕后,要在Buffer中读取数据,此时不能直接读,首先要调用flip()方法,把Buffer的写模式切换成读模式。
写数据模式,数据会被写入到position指向的位置,最多只能写到capacity指针指向的位置。
调用filp()之前的写模式
切换为读模式后,position指针指向写入的第一个数据位置,limit指针指向写入的最后一个数据位置。读的时候,最多只能读到limit指向的位置。这样就可以保证我们读取的,就是从头到写入最后一个数据的位置。
调用filp()之后切换为读模式
读模式切换写模式有两种情况:
1、读完数据,可以调用clear()切换回写模式,此时position指针会指向头,limit指针指到末尾,但是Buffer中的数据不会被清除。调用clear()只是移动了指针。
调用了clear()切换回写模式
2、当第一次读数据时,没把数据读完就要切换为写模式,此时可以使用compact()切换为写模式。
使用compact()切换为写模式时,会把第一次读时候没读完的数据放在前面,然后position指针指向这些数据的后面,在进行写操作,下次读时,就会先把未读的数据先读出来,再接着往下读。
文件拷贝案例
package io;
?
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
?
public class Nio {
public static void main(String[] args) {
File file1 = new File("D:\\java视频\\笔记\\网络编程\\q.txt");
File file2 = new File("D:\\java视频\\笔记\\网络编程\\w.txt");
new Nio(file1,file2);
}
public Nio(File source,File target){
FileChannel fin = null;
FileChannel fout = null;
?
try {
fin = new FileInputStream(source).getChannel();
fout = new FileOutputStream(target).getChannel();
//设置容量1024字节缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
//Channel的读操作就是将数据写入到Buffer中
while((fin.read(buffer))!=-1){
//把写模式切换成读模式
buffer.flip();
//buffer.hasRemaining()判断是否还有可读的数据,使用这种方法更加可靠
while(buffer.hasRemaining()) {
fout.write(buffer);
}
//切换为写模式
buffer.clear();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Channel之间可以互相读写数据
FileChannel:与文件进行数据传输通道;
ServerSocketChannel和SocketChannel:在网络编程中进行数据传输。
原文:https://www.cnblogs.com/nicechen/p/15167899.html