通过JAVA 代码来看TCP的3次握手和4次挥手

2019-08-29 16:37   4552 浏览

本文链接:https://blog.csdn.net/u014022405/article/details/82082186

之前一直没弄明白我使用JAVA API进行socket编程的时候,到底调用哪个API的时候,TCP底层进行了3次握手,调用哪个API的时候,TCP底层进行了4次握手。网上查阅一番资料后没找到想要的,于是自己利用周末时间搞搞明白,记录一下,下次好查阅!


阅读提前

1.TCP3次握手和4次挥手理解  传送门:TCP3次握手连接协议和4次握手断开连接协议   TCP三次握手连接及四次挥手断开过程   理解TCP三次握手/四次断开的必要性


2.NIO(IO)相关知识、socket相关知识  


此次使用NIO 做例子(原阻塞方式同理)


先上代码

服务端代码

package com.mtl.day20180825;

 

import java.io.IOException;

import java.net.InetSocketAddress;

import java.net.ServerSocket;

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.Set;

 

/**

 * @author motianlong

 * @date 2018年8月25日 下午9:21:10

 * @jdk 1.8

 */

public class NIOSocketService {

public static void main(String[] args) throws IOException {

ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

//绑定端口

serverSocketChannel.bind(new InetSocketAddress(10086));

//设置serverSocketChannel为非阻塞模式

serverSocketChannel.configureBlocking(false);

Selector selector = Selector.open();

//将serverSocketChannel注册到selector,并设置对连接事件感兴趣

serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

while (true) {

//非阻塞式查询selector是否有准备好的读、写、连接事件

int select = selector.selectNow();

//如果返回大于0,则有准备好的读、写、连接事件,然后处理

if (select>0) {

Set<SelectionKey> selectedKeys = selector.selectedKeys();

Iterator<SelectionKey> iterator = selectedKeys.iterator();

while (iterator.hasNext()) {

SelectionKey selectionKey = (SelectionKey) iterator.next();

//如果事件为准备好可读

if (selectionKey.isReadable()) {

System.out.println("readable");

//处理读事件的handler

new ReadHandler().readHandler(selectionKey);

}

//如果事件为准备好连接

else if (selectionKey.isAcceptable()) {

ServerSocketChannel ssc=(ServerSocketChannel) selectionKey.channel();

//同意连接,返回一个SocketChannel

SocketChannel accept = ssc.accept();

System.out.println("有连接进来!"+accept.getRemoteAddress());

//将SocketChannel也设置为非阻塞模式

accept.configureBlocking(false);

//注册到selector,并设置对读感兴趣

accept.register(selector, SelectionKey.OP_READ);

}

iterator.remove(); 

}

}

}

}

}

服务端读数据handler类

package com.mtl.day20180825;

 

import java.nio.ByteBuffer;

import java.nio.channels.SelectionKey;

import java.nio.channels.SocketChannel;

 

/**

 * @author motianlong

 * @date 2018年8月25日 下午10:20:50

 * @jdk 1.8

 */

public class ReadHandler {

public void readHandler(SelectionKey selectionKey) {

SocketChannel channel = (SocketChannel) selectionKey.channel();

ByteBuffer buffer=ByteBuffer.allocate(64);

StringBuilder stringBuilder=new StringBuilder();

try {

//循环读取客户端传过来的数据

while(channel.read(buffer)!=-1) {

buffer.flip();

stringBuilder.append(new String(buffer.array(), 0, buffer.limit(), "UTF-8"));

buffer.clear();

}

System.out.println(stringBuilder.toString());

//组织服务发送给客户端的数据

String re=new String("我是服务器,我已经收到你的消息了!");

buffer.put(re.getBytes("UTF-8"));

//将buffer设置为写模式

buffer.flip();

//写入SocketChannel通道

while(buffer.hasRemaining()) {

channel.write(buffer);

}

//关闭服务端的写出通道,此时服务端会发送FIN到客户端,客户端收到后,会返回ACK确认字符

channel.socket().shutdownOutput();

} catch (Exception e) {

e.printStackTrace();

}finally {

try {

channel.close();

channel.socket().close();

} catch (Exception e2) {

e2.printStackTrace();

}

}

}

}

客户端代码:

package com.mtl.day20180825;

/**

 * @author motianlong

 * @date 2018年8月26日 下午4:14:35

 * @jdk 1.8

 */

 

import java.io.ByteArrayOutputStream;

import java.io.IOException;

import java.net.InetSocketAddress;

import java.nio.ByteBuffer;

import java.nio.channels.SocketChannel;

 

public class NIOSocketClient {

public static void main(String[] args) throws IOException {

SocketChannel socketChannel=SocketChannel.open();

socketChannel.configureBlocking(false);

//连接到服务端,此时客服端会发送SYN到服务端,服务端收到SYN后,会把SYN和ACK一并发送给客户端,客户端收到后,返回ACK给服务端,然后完成3次握手。

//(如果SocketChannel为阻塞模式,执行完这一句代码,客户端和服务的3次握手已经完成!)

socketChannel.connect(new InetSocketAddress("127.0.0.1", 10086));

//因为SocketChannel设置为非阻塞模式,所有执行下面语句的时候可能还没有连接成功,所以循环检查是否已经连接成功!

while(!socketChannel.finishConnect()) {

System.out.println("等待连接完成。。。");

}

//代码执行到这里,证明客服端和服务端已连接成功!完成3次握手!

ByteBuffer buffer=ByteBuffer.allocate(16);

buffer.put("A".getBytes("UTF-8"));

buffer.flip();

//向服务器发送1个字节的数据

while(buffer.hasRemaining()) {

socketChannel.write(buffer);

}

//关闭客服端的写出通道,此时执行完这句客户端会向发送FIN到服务端,并且服务端会返回ACK确认字符

socketChannel.socket().shutdownOutput();

buffer.clear();

ByteArrayOutputStream outputStream=new ByteArrayOutputStream();

//循环读取服务端发送回来的数据

while(socketChannel.read(buffer)!=-1) {

buffer.flip();

outputStream.write(buffer.array(), 0, buffer.limit());

buffer.clear();

}

System.out.println(new String(outputStream.toByteArray(), 0, outputStream.size(), "UTF-8"));

socketChannel.shutdownInput();

socketChannel.close();

socketChannel.socket().close();

}

}

以下为抓包工具数据(图中结论由代码中打断点执行,总结得出)

服务器端端口为10086     客户端端口为:2649



抓包工具:npcap+Wireshark


喜欢 0

评论