OSI网络七层模型

为了使不同的计算机厂家的计算机可以相互通信,以便在更大的范围内建立计算机网络,有必要建立一个国际范围的网络体系结构标准。国际标准化组织ISO推荐了一个网络系统结构----七层参考模型。



每层的主要功能:

TCP/UDP协议

TCP协议(传输控制层协议),是Internet一个重要的传输层协议,TCP提供面向连接、可靠、有序,字节流传输服务,在使用TCP之前,需要建立TCP连接。

TCP消息头



1:16位源端口号和16位目的端口号,告诉我们,报文来自什么地方(源端口号),要传向什么地方(目的端口号),TCP通信时,客户端的端口号一般是自动生成的,服务端的端口号可以自己指定。

2:标志位:

URG:表示紧急指针是否有效

ACK:确认号是否有效

PSH:接收方接收到数据后,应该立即转到应用层

RST:重新连接标志

SYN:建立连接标志

FIN:关闭连接标志

TCP三次握手、四次挥手

一:三次握手



三次握手,就是说发起的人,跟另一个人说你能听到我说话吗,另一个说我能听到,你可以听到我说的吗,发起的人说我也可以听到,那么接下来就可以聊天了。

下面看下三次握手的过程:

1:第一次握手,客户端将SYN标志位设置为1,并产生一个随机的值Seq=x,然后把syn包发送给服务端,客户端的状态变为SYN_SENT,等待服务端去确认。

2:第二次握手,服务端收到客户端的建立连接请求后,要确认连接,即ack=x+1,然后也有一个syn=1,并且也随机产生一个值Seq=y,然后发送给客户端确认连接请求,最后服务端的状态变为SYN_RCVD。

3:第三次握手,客户端收到服务端的确认请求后,确认ack=x+1,如果正确,就给服务端发送ack=y+1,seq=z,服务端收到后再次检查,正确后就可以建立连接成功,客户端和服务端都进入ESTABLISHED状态。

为什么是三次握手而不是两次或者四次?

看过别的文章都应该清楚,为何要是三次握手:

如果是两次,客户端发送了一个连接请求,假如在传输的过程中,因为网络的原因,迟迟未发送给服务端,这时,客户端又发送了一个连接请求,然后服务端回应了这个请求,连接建立成功,然后过一段时间,这个迟迟未到的请求又恢复了,然后发送给服务端,这时又会建立请求,这样会消耗资源。

为何不是四次呢,三次就可以建立连接通信了,所以不需要第四次了。

二:四次挥手



四次挥手:

1:第一次挥手,客户端主动发送关闭连接请求,客户端发送FIN=1,seq=u给服务端,告诉服务端我没有数据要发送给你了,如果服务端还有数据发送,那么我客户端可以接收,这时客户端状态会变为FIN_WAIT_1。

2:第二次挥手,服务端收到客户端关闭连接请求后,会给客户端发送一个确认ack=1,告诉客户端,你的请求我收到了,但是呢,我还没有准备好关闭呢,可能还有数据没有传输完成,然后客户端进入FIN_WAIT_2状态。

3:第三次挥手,服务端确认数据已经传输完成了,可以进入准备关闭状态了,就给客户端发送一个FIN=1,这时服务端进入LAST_ACK状态。

4:客户端收到服务端的请求后,会再发一个确认请求给服务端,然后客户端进入TIME_WAIT状态,服务端收到后,就可以确认关闭连接了,客户端会等待2MSL,证明服务端已经关闭了,然后客户端也可以关闭了,最后就完成了四次挥手。

四次挥手主要是为了保证数据传输的完整性。

UDP协议

用户数据报协议UDP是传输层的协议,提供无连接、不可靠、数据报尽力传输服务。

TCP协议/UDP协议区别

TCP UDP
面向连接 无连接
可靠 不可靠
资源占用多 资源占用少

TCP协议的请求包相对于UDP协议的请求包,占用的字节要大,所以资源占用多一些;而快和慢主要是比较两者,请求包大传输就会相对慢一些。

HTTP协议

HTTP协议是应用层协议

HTTP协议请求头

HTTP协议响应头

HTTP状态码

socket编程

socket中文翻译叫做套接字。

Socket是Internet中应用最为广泛的网络应用编程接口,实现了与三种底层协议的交互:

1:数据包类型的Socket,是面向UDP的

2:流式Socket,是面向TCP的

3:原始的Socket,面向IP、ICMP等

Socket调用的过程:



Socket API中定义了很多函数:

listen()、accept()只用于服务端

connect()只用于客户端

还有其他一些函数:socket()、bind()、close()等

BIO

BIO服务端

public static void main(String[] args) throws IOException {
//创建一个ServerSocket,并指定一个端口号8098
ServerSocket serverSocket = new ServerSocket(8998);
System.out.println("服务器启动成功");
//判断ServerSocket是不是已经关闭了
while (!serverSocket.isClosed()) {
//获取客户端的连接
//accept会阻塞,直到有新的连接过来
Socket socket = serverSocket.accept();
System.out.println("有客户端连接:" + socket.toString());
//读取客户端发来的数据
InputStream inputStream = socket.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "utf-8"));
String readMsg;
while ((readMsg = reader.readLine()) != null) {
//读取到数据,就退出循环
if (readMsg.length() > 0) {
break;
}
}
System.out.println("有数据发过来了,来自:" + socket.toString());
System.out.println("收到客户端的数据:" + readMsg);
}
//关闭ServerSocket
serverSocket.close();
}

BIO客户端

public static void main(String[] args) throws IOException {
//创建一个Socket,连接服务端
Socket socket = new Socket("localhost", 8998);
//写数据
OutputStream outputStream = socket.getOutputStream();
Scanner scanner = new Scanner(System.in);
System.out.println("请输入:");
String writeMsg = scanner.nextLine();
outputStream.write(writeMsg.getBytes("utf-8"));
//关闭连接
scanner.close();
socket.close();
}

启动服务端和客户端,进行数据传输:

服务端输出结果:



客户端输入:



可以看到,到启动好服务端后,再去启动客户端,服务端会立马打印有新连接过来,当在客户端输入一些数据时,服务端就会接收到数据然后打印;服务端的端口号一般是自己指定,客户端的端口号一般是随机生成的。

如果启动两个客户端会发生什么呢?



可以看到,启动两个客户端后,服务端只打印了一个客户端连接进来了,是因为服务端读取数据时会阻塞,再次开启一个客户端过来,就不会再打印新的连接了,只有当第一个客户端数据传输到服务端后,另一个客户端才会连接进来:



上图可以看到,第一个客户端数据传输到服务端后,另一个客户端连接就立马打印了,怎么解决这种问题呢?

因为在服务端只有一个main方法,也就是只有一个单线程,那么换成多线程时,就可以解决这种阻塞问题,看下面改造后的代码:

    //创建一个线程池
public static ExecutorService executorService = Executors.newCachedThreadPool(); public static void main(String[] args) throws IOException {
//创建一个ServerSocket,并指定一个端口号8098
ServerSocket serverSocket = new ServerSocket(8998);
System.out.println("服务器启动成功");
//判断ServerSocket是不是已经关闭了
while (!serverSocket.isClosed()) {
//获取客户端的连接
//accept会阻塞,直到有新的连接过来
Socket socket = serverSocket.accept();
System.out.println("有客户端连接:" + socket.toString());
//执行后,会立马返回,不会阻塞
executorService.execute(new Runnable() {
@Override
public void run() {
try {
//读取客户端发来的数据
InputStream inputStream = socket.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "utf-8"));
String readMsg;
while ((readMsg = reader.readLine()) != null) {
//读取到数据,就退出循环
if (readMsg.length() > 0) {
break;
}
}
System.out.println("有数据发过来了,来自:" + socket.toString());
System.out.println("收到客户端的数据:" + readMsg);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
//关闭ServerSocket
serverSocket.close();
}

启动两个客户端后,服务端打印结果:



可以看到,最后就不会再阻塞了,这种利用多个线程解决阻塞问题的方式,存在很大的问题,当有成千上万个客户端需要连接时,那开启的线程数就相当多了,对于CPU来说,会有很大的消耗,而且CPU在线程之间切换,也会耗时。

阻塞IO:资源不可用的时候,IO请求会阻塞,直到有数据或者超时才会退出阻塞。

非阻塞IO:资源不可用时,IO请求会离开返回,返回数据标识不可用。

还有同步IO和异步IO,主要区别是阻塞IO是获取资源的时候发生,同步IO是在处理资源的时候发生。

客户端可以发送数据给服务端,那么服务端也可以发送数据给客户端,改造上面的服务端和客户端代码:

改造后的服务端:

try {
//读取客户端发来的数据
InputStream inputStream = socket.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "utf-8"));
String readMsg;
while ((readMsg = reader.readLine()) != null) {
//读取到数据,就退出循环
if (readMsg.length() > 0) {
break;
}
}
System.out.println("有数据发过来了,来自:" + socket.toString());
System.out.println("收到客户端的数据:" + readMsg); //发送数据给客户端
OutputStream outputStream = socket.getOutputStream();
//加\r\n是因为读取数据的方法是readLine
outputStream.write("hello world\r\n".getBytes());
outputStream.flush();
} catch (Exception e) {
e.printStackTrace();
}

改造后的客户端:

public static void main(String[] args) throws IOException {
//创建一个Socket,连接服务端
Socket socket = new Socket("localhost", 8998);
//写数据
OutputStream outputStream = socket.getOutputStream();
Scanner scanner = new Scanner(System.in);
System.out.println("请输入:");
String writeMsg = scanner.nextLine() + "\r\n";
outputStream.write(writeMsg.getBytes("utf-8"));
//关闭连接
scanner.close(); //获取服务端发送过来的数据
System.out.println("服务端响应的数据:");
InputStream inputStream = socket.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "utf-8"));
String readMsg;
while ((readMsg = reader.readLine()) != null) {
//读取到数据,就退出循环
if (readMsg.length() > 0) {
break;
}
}
System.out.println("获取到服务端发送的数据:" + readMsg);
socket.close();
}

客户端打印结果:



在使用tomcat时,启动后,输入tomcat启动后的地址可以在浏览器中访问到页面,那么下面再次修改服务端代码,使得可以在浏览器访问,主要是需要遵守浏览器中使用的HTTP协议,即响应头部信息需要加上:

 //发送数据给客户端
OutputStream outputStream = socket.getOutputStream();
outputStream.write("HTTP/1.1 200 OK\r\n".getBytes());
outputStream.write("Content-Length: 11\r\n\r\n".getBytes());
outputStream.write("hello world".getBytes());
outputStream.flush();

使用浏览器访问:

NIO

NIO始于java1.4,提供了非阻塞的API,NIO中有三个核心组件:Buffer缓冲区、Channel通道、Selector选择器。

Buffer缓冲区

Buffer缓冲区本质上是一个可以写入数据的内存块,类似数组,然后还可以读取数据,它提供了一些方法,可以更加方便的操作,使用Buffer写入和读取数据需要下面四个步骤:

1:将数据写入Buffer缓冲区

2:调用buffer.flip(),转换为读取模式

3:从Buffer缓冲区读取数据

4:调用buffer.clear()或者buffer.compact()转换为写模式

Buffer的工作原理:

capacity容量:作为一个内存块,它肯定有大小,也就是容量

position位置:也就是数据的索引值,写入模式时指写数据的位置,读取模式时指读数据的位置

limit限制:写入模式时,限制等于buffer的容量,读取模式时指写入的数据量



ByteBuffer:

ByteBuffer为性能关键性的代码提供了两种实现:直接内存和堆内存。



可以看到,从堆内存进行读写,多了一次拷贝,下面看下ByteBuffer的用法:

public static void main(String[] args) {
//分配10容量
ByteBuffer byteBuffer = ByteBuffer.allocate(10);
//直接内存的用法
//ByteBuffer byteBuffer1 = ByteBuffer.allocateDirect(10);
System.out.println("capacity容量:" + byteBuffer.capacity()
+ ",position位置:" + byteBuffer.position() + ",limit限制:" + byteBuffer.limit());
//写入数据
byteBuffer.put("1".getBytes());
byteBuffer.put("2".getBytes());
byteBuffer.put("3".getBytes());
//再看一下数据
System.out.println("capacity容量:" + byteBuffer.capacity()
+ ",position位置:" + byteBuffer.position() + ",limit限制:" + byteBuffer.limit()); //读取数据
System.out.println("--------------开始读取数据---------------");
//转换为读模式
byteBuffer.flip();
byte byte1 = byteBuffer.get();
System.out.println(byte1);
byte byte2 = byteBuffer.get();
System.out.println(byte2);
System.out.println("读取两个数据之后,capacity容量:" + byteBuffer.capacity()
+ ",position位置:" + byteBuffer.position() + ",limit限制:" + byteBuffer.limit()); //写入数据
System.out.println("--------------再次写入数据---------------");
byteBuffer.compact();
byteBuffer.put("4".getBytes());
byteBuffer.put("5".getBytes());
System.out.println("capacity容量:" + byteBuffer.capacity()
+ ",position位置:" + byteBuffer.position() + ",limit限制:" + byteBuffer.limit());
}

输出结果:



ByteBuffer和BIO读取和写入数据的区别:

BIO读取和写入:



BIO读取和写入是单向的,通过OutputStream写和InputStream读来实现。

ByteBuffer读取和写入:



ByteBuffer的读取和写入是双向的,通过Channel通道实现。

Channel通道

Channel通道是在一个通道内进行读写,是双向的,可以非阻塞的进行读取和写入,通道始终读取和写入buffer中,也就是读取的时候,先从buffer中读取出来,然后通过通道去传输,写入的时候也是先写入buffer,然后再写入通道中去传输。

Channel和BIO的实现有什么好处和区别呢?看下面图示:

BIO:



BIO中,客户端和服务端通信,都需要建立一个线程,线程多了,消耗CPU。

Channel实现:



NIO中是使用Channel和Selector实现。

Selector选择器

Selector选择器,可以检查一个或者多个通道,并且可以确定哪些通道已经准备好读取和写入了,可以实现单个线程管理多个通道,继而管理多个网络连接。

一个线程使用Selector可以监听多个Channel,有四个事件:

1:connect(对应常量值:OP_CONNECT)

2:accept(对应常量值:OP_ACCEPT)

3:read(对应常量值:OP_READ)

4:write(对应常量值:OP_WRITE)

Selector选择器可以监听哪些通道已经注册了事件,通过判断不同的事件,进行处理。



Selector的实现原理:



select会监听Channel通道,当Channel中有数据可以读取或者写入时,就会把Channel的引用拷贝一份出来,放到selectKeys集合中,selectKeys中都是监听到的可以进行读取或者写入的通道,那么下面通过遍历selectKeys,然后根据Selector提供的ket.isConnectable()、ket.isAcceptable()、ket.isReadable()、ket.isWritable()判断是哪个事件,然后再进行事件处理。

下面通过具体的实例实现上面所说的内容,以便更容易理解。

NIO实例

服务端代码:

public static void main(String[] args) throws IOException {
//创建一个ServerSocketChannel
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//默认是阻塞的,需要手动设置为非阻塞模式
serverSocketChannel.configureBlocking(false);
//指定一个服务端的端口号
serverSocketChannel.socket().bind(new InetSocketAddress(8977));
System.out.println("服务端启动成功");
while (true) {
//serverSocketChannel.accept()
//如果通道是非阻塞模式,那么没有挂起的连接,这个方法会立即返回,必须要检查是否为空
SocketChannel socketChannel = serverSocketChannel.accept();
if (socketChannel != null) {
//设置为非阻塞模式
socketChannel.configureBlocking(false);
//socketChannel.getRemoteAddress获取客户端连接信息
System.out.println("收到一个新的连接:" + socketChannel.getRemoteAddress()); try {
//读取数据
//创建一个ByteBuffer,并指定容量大小为1024
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
String readMsg;
//read会阻塞
while (socketChannel.isOpen() && socketChannel.read(byteBuffer) != -1) {
//手动判断有没有读取结束
if (byteBuffer.position() > 0) {
break;
}
}
//没有数据就继续执行下面的代码
if(byteBuffer.position() == 0) {
continue;
}
//转换为读取模式
byteBuffer.flip();
byte[] bytes = new byte[byteBuffer.limit()];
byteBuffer.get(bytes);
System.out.println("收到新的数据:" + new String(bytes));
System.out.println("收到新的数据,来自:" + socketChannel.getRemoteAddress());
} catch (Exception e) {
e.printStackTrace();
}
} }
}

客户端代码:

 public static void main(String[] args) throws IOException {
//创建一个SocketChannel
SocketChannel socketChannel = SocketChannel.open();
//设置为非阻塞模式
socketChannel.configureBlocking(false);
//连接服务端
socketChannel.connect(new InetSocketAddress("localhost", 8977));
//当客户端没有连接上就等待
while (!socketChannel.finishConnect()) {
Thread.yield();
}
//写入数据
Scanner scanner = new Scanner(System.in);
System.out.println("请输入:");
String writeMsg = scanner.nextLine();
//ByteBuffer方法的用法请自行百度
ByteBuffer byteBuffer = ByteBuffer.wrap(writeMsg.getBytes());
while (byteBuffer.hasRemaining()) {
socketChannel.write(byteBuffer);
}
//关闭
scanner.close();
socketChannel.close();
}

服务端输出结果:



同样的,服务端也可以发送数据给客户端,修改上述服务端和客户端代码代码。

服务端加入下面的代码:

//写入数据
String writeMsg = "HTTP/1.1 200 OK\r\n" +
"Content-Length: 11\r\n\r\n" +
"hello world";
//为啥没调用转换写入模式,这里新建了一个ByteBuffer
ByteBuffer writeBuffer = ByteBuffer.wrap(writeMsg.getBytes());
while (writeBuffer.hasRemaining()) {
socketChannel.write(writeBuffer);
}

客户端加入下面的代码:

//获取服务端数据
System.out.println("收到服务端响应:");
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
//read会阻塞
while (socketChannel.isOpen() && socketChannel.read(readBuffer) != -1) {
//手动判断有没有读取结束
if (readBuffer.position() > 0) {
break;
}
}
//转换为读取模式
readBuffer.flip();
byte[] bytes = new byte[readBuffer.limit()];
readBuffer.get(bytes);
System.out.println("收到服务端数据:" + new String(bytes));

客户端打印结果:



改造上面的服务端和客户端代码,使用Selector:

使用Selector的好处,不用遍历所有的通道,直接遍历有事件触发的通道,节省时间和资源的消耗。

服务端修改之后:

public static void main(String[] args) throws IOException {
//创建一个ServerSocketChannel
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//默认是阻塞的,需要手动设置为非阻塞模式
serverSocketChannel.configureBlocking(false);
//创建一个Selector
Selector selector = Selector.open();
//注册一个感兴趣的事件
//服务端永远是被动的,所以这里只能注册OP_ACCEPT事件
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
//指定一个服务端的端口号
serverSocketChannel.socket().bind(new InetSocketAddress(8977));
System.out.println("服务端启动成功");
while (true) {
//启动selector,会阻塞,直到有事件为止,可以监听事件的触发
selector.select();
//获取到触发的事件集合
Set<SelectionKey> selectionKeys = selector.selectedKeys();
//遍历事件
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
//获取已经准备就绪的事件
if (key.isAcceptable()) {
//获取到客户端的Channel
SocketChannel socketChannel = ((ServerSocketChannel)key.channel()).accept();
//设置为非阻塞模式
socketChannel.configureBlocking(false);
//注册事件
socketChannel.register(selector, SelectionKey.OP_READ);
System.out.println("收到一个新的连接:" + socketChannel.getRemoteAddress());
} else if (key.isReadable()) {
try {
SocketChannel socketChannel = (SocketChannel)key.channel();
//读取数据
//创建一个ByteBuffer,并指定容量大小为1024
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
//read会阻塞
while (socketChannel.isOpen() && socketChannel.read(byteBuffer) != -1) {
//手动判断有没有读取结束
if (byteBuffer.position() > 0) {
break;
}
}
//没有数据就继续执行下面的代码
if(byteBuffer.position() == 0) {
continue;
}
//转换为读取模式
byteBuffer.flip();
byte[] bytes = new byte[byteBuffer.limit()];
byteBuffer.get(bytes);
System.out.println("收到新的数据:" + new String(bytes));
System.out.println("收到新的数据,来自:" + socketChannel.getRemoteAddress()); //写入数据
String writeMsg = "HTTP/1.1 200 OK\r\n" +
"Content-Length: 11\r\n\r\n" +
"hello world";
//为啥没调用转换写入模式,这里新建了一个ByteBuffer
ByteBuffer writeBuffer = ByteBuffer.wrap(writeMsg.getBytes());
while (writeBuffer.hasRemaining()) {
socketChannel.write(writeBuffer);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
//处理完之后要移除,不然下次还会再集合中
//需要自己去管理集合
iterator.remove();
}
}

客户端修改之后:

 public static void main(String[] args) throws IOException {
//创建一个SocketChannel
SocketChannel socketChannel = SocketChannel.open();
//设置为非阻塞模式
socketChannel.configureBlocking(false);
//创建一个Selector
Selector selector = Selector.open();
//注册一个感兴趣的事件
socketChannel.register(selector, SelectionKey.OP_CONNECT);
//连接服务端
socketChannel.connect(new InetSocketAddress("localhost", 8977));
while (true) {
//启动selector,会阻塞,直到有事件为止,可以监听事件的触发
selector.select();
//获取到触发的事件集合
Set<SelectionKey> selectionKeys = selector.selectedKeys();
//遍历事件
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
if (key.isConnectable()) {
if (socketChannel.finishConnect()) {
System.out.println("连接成功:" + socketChannel);
//切换事件为写入
key.interestOps(SelectionKey.OP_WRITE);
}
} else if(key.isWritable()) {
//写入数据
Scanner scanner = new Scanner(System.in);
System.out.println("请输入:");
String writeMsg = scanner.nextLine();
scanner.close();
//ByteBuffer方法的用法请自行百度
ByteBuffer byteBuffer = ByteBuffer.wrap(writeMsg.getBytes());
while (byteBuffer.hasRemaining()) {
socketChannel.write(byteBuffer);
}
key.interestOps(SelectionKey.OP_READ);
} else if (key.isReadable()) {
//获取服务端数据
System.out.println("收到服务端响应:");
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
//read会阻塞
while (socketChannel.isOpen() && socketChannel.read(readBuffer) != -1) {
//手动判断有没有读取结束
if (readBuffer.position() > 0) {
break;
}
}
//转换为读取模式
readBuffer.flip();
byte[] bytes = new byte[readBuffer.limit()];
readBuffer.get(bytes);
System.out.println("收到服务端数据:" + new String(bytes));
}
//处理完之后要移除,不然下次还会再集合中
iterator.remove();
}
}
}

服务端打印结果:



客户端打印结果:



启动多个客户端后,服务端打印结果:



注意:SocketChannel的write方法,可能在没有写入任何内容就返回了,所以需要在while中使用,read可能没有读取直接返回了null,所以需要根据返回的int值判断读取了多少内容。

注意:ServerSocketChannel的accept方法,在通道没有阻塞的情况下,如果没有挂起的连接,会立即返回null,所以必须要检查ServerSocketChannel的accept方法是否为空。

到此,NIO三个核心组件就说完了。

结束语

本文的内容,都在学习过程的记录,加上个人的一些理解,存在不足或者不够深入的地方,或者有些地方说的不够明白,还望谅解,多多指正。

网络编程之BIO和NIO的更多相关文章

  1. 【图灵学院15】极致优化-高性能网络编程之BIO与NIO区别

    一.Java IO概念 1.  一个http请求节点 数据传输 1)网络传输 TCP.UDP 2)通信模型 BIO.NIO.AIO 数据处理 3)应用协议 HTTP.RMI.WEBSERVICE.Re ...

  2. 【Java】网络编程之NIO

    简单记录 慕课网-解锁网络编程之NIO的前世今生 & 一站式学习Java网络编程 全面理解BIO/NIO/AIO 内容概览 文章目录 1.[了解] NIO网络编程模型 1.1.NIO简介 1. ...

  3. 网络编程之TCP编程

    网络编程之TCP编程 前面已经介绍过关于TCP协议的东西,这里不做赘述.Java对于基于TCP协议的网络通信提供了良好的封装,Java使用socket对象来代表两端的通信窗口,并通过Socket产生I ...

  4. 网络编程之socket

    网络编程之socket socket:在网络编程中的一个基本组件,也称套接字. 一个套接字就是socket模块中的socket类的一个实例. 套接字包括两个: 服务器套接字和客户机套接字 套接字的实例 ...

  5. [深入浅出WP8.1(Runtime)]网络编程之HttpClient类

    12.2 网络编程之HttpClient类 除了可以使用HttpWebRequest类来实现HTTP网络请求之外,我们还可以使用HttpClient类来实现.对于基本的请求操作,HttpClient类 ...

  6. java网络编程之TCP通讯

    java中的网络编程之TCP协议的详细介绍,以及如何使用,同时我在下面举2例说明如何搭配IO流进行操作, /* *TCP *建立连接,形成传输数据的通道: *在连接中进行大数据量传输: *通过三次握手 ...

  7. python3网络编程之socketserver

    本节主要是讲解python3网络编程之socketserver,在上一节中我们讲到了socket.由于socket无法支持多用户和多并发,于是就有了socket server. socket serv ...

  8. 网络编程之UDP编程

    网络编程之UDP编程 UDP协议是一种不可靠的网络协议,它在通信的2端各建立一个Socket,但是这个Socket之间并没有虚拟链路,这2个Socket只是发送和接受数据的对象,Java提供了Data ...

  9. 网络编程之socketserver

    网络编程之socketserver """ socketserver.py 中的5个基础类 +------------+ | BaseServer | +-------- ...

随机推荐

  1. Java中的CPU占用高和内存占用高的问题排查

    下面通过模拟实例分析排查Java应用程序CPU和内存占用过高的过程.如果是Java面试,这2个问题在面试过程中出现的概率很高,所以我打算在这里好好总结一下. 1.Java CPU过高的问题排查 举个例 ...

  2. Javascript中的事件对象和事件类型

    接上次看JS的事件冒泡和捕获,所以顺带就把事件相关的知识都看完好了 而且想到一个好的学习方法,第一天自己看,第二天把前一天学习的东西写下来,一方面可以当复习,一方面当重新整理并且分享 事件对象 事件处 ...

  3. Oracle数据库配置监听程序

    最近在学习Oracle数据库,从安装到配置监听程序基本靠百度... 不得不说百度真的很nice!!! 下面是我的Oracle服务端(PL/SQL Developer)出现的监听程序的问题及我解决的方法 ...

  4. 【ZeyFraのJavaEE开发小知识02】MybatisPlus&ElementUI

    1.关于如何获得Mybatis-Plus在插入对应为自增长主键但并未对该主键赋值的实体类之后其主键值 对应数据库中某张表并未设置主键值,但其主键为自增长类型的实体类,在使用Mybatis-Plus做i ...

  5. [Java Tutorial学习分享]接口与继承

    目录 接口 概述 Java 中的接口 使用接口作为API 定义一个接口 The Interface Body 实现接口 使用接口作为类型 进化的接口 默认方法 扩展包含默认方法的接口 静态方法 接口总 ...

  6. 剑指 Offer 10- II. 青蛙跳台阶问题

    剑指 Offer 10- II. 青蛙跳台阶问题 Offer 10- II 题目描述: 动态规划方程: 循环求余: 复杂度分析: package com.walegarrett.offer; impo ...

  7. C#无损压缩图片

    /// <summary> /// 根据指定尺寸得到按比例缩放的尺寸,返回true表示以更改尺寸 /// </summary> /// <param name=" ...

  8. vue打开新窗口并且实现传参,有图有真相

    我要实现的功能是打开一个新窗口用来展示新页面,而且需要传参数,并且参数不能显示在地址栏里面,而且当我刷新页面的时候,传过来的参数不能丢失,要一直存在,除非我手动关闭这个新窗口,即浏览器的标签页. 通过 ...

  9. [同步到 MaixPy3 文档] 使用 Python 编程入门开源硬件项目

    本文是给有一点 Python 基础但还想进一步深入的同学,有经验的开发者建议跳过. 前言 在写这篇案例系列的时候 junhuanchen 期望能够引导用户如何成为专业的开发者,不是只会调用代码就好,所 ...

  10. 【python+selenium的web自动化】- Selenium WebDriver原理及安装

    简单介绍 selenium ​ selenium是一个用于测试web网页的自动化测试工具,它直接运行在浏览器中,模拟用户的操作.