Java网络编程 - 实战(五)TCP快速入门
TCP是什么
英语:Transmission Control Protocol,缩写为TCP;
TCP是传输控制协议;是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义;
与UDP一样完成第四层传输层所指定的功能与职责;
TCP的机制:
三次握手、四次挥手;
具有校验机制、可靠、数据传输稳定。
.png)
TCP能做什么
聊天消息传输、推送;
单人语音、视频聊天等;
几乎UDP能做的都能做,但需要考虑复杂性、性能问题;
限制:无法进行广播,多播等操作。
TCP核心API
1、socket():创建一个Socket;
2、bind():绑定一个Socket到一个本地地址和端口上;
3、connect():连接到远程套接字;
4、accept():接受一个新的连接,会处于阻塞状态;
5、write():把数据写入到Socket输出流;
6、read():从Socket输入流读取数据。
TCP连接可靠性



TCP传输可靠性
1、排序、顺序发送、顺序组装;
2、丢弃、超时;
3、重发机制 - 定时器。

TCP传输初始化配置,代码实现
1、初始化服务器TCP连接监听;
2、初始化客户端发起连接操作;
3、服务器Socket连接处理。
客户端与服务器交互:
1、客户端发送简单字节;
2、服务器接收客户端发送数据;
3、服务器回送消息,客户端识别回送消息。
import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Inet4Address; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketException; import java.nio.ByteBuffer; public class Client { private static final int PORT = 20000; private static final int LOCAL_PORT = 20001; public static void main(String[] args) throws IOException { Socket socket = createSocket(); initSocket(socket); // 链接到本地20000端口,超时时间3秒,超过则抛出超时异常 socket.connect(new InetSocketAddress(Inet4Address.getLocalHost(), PORT), 3000); System.out.println("已发起服务器连接,并进入后续流程~"); System.out.println("客户端信息:" + socket.getLocalAddress() + " P:" + socket.getLocalPort()); System.out.println("服务器信息:" + socket.getInetAddress() + " P:" + socket.getPort()); try { // 发送接收数据 todo(socket); } catch (Exception e) { System.out.println("异常关闭"); } // 释放资源 socket.close(); System.out.println("客户端已退出~"); } private static Socket createSocket() throws IOException { /* // 无代理模式,等效于空构造函数 Socket socket = new Socket(Proxy.NO_PROXY); // 新建一份具有HTTP代理的套接字,传输数据将通过www.baidu.com:8080端口转发 Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(Inet4Address.getByName("www.baidu.com"), 8800)); socket = new Socket(proxy); // 新建一个套接字,并且直接链接到本地20000的服务器上 socket = new Socket("localhost", PORT); // 新建一个套接字,并且直接链接到本地20000的服务器上 socket = new Socket(Inet4Address.getLocalHost(), PORT); // 新建一个套接字,并且直接链接到本地20000的服务器上,并且绑定到本地20001端口上 socket = new Socket("localhost", PORT, Inet4Address.getLocalHost(), LOCAL_PORT); socket = new Socket(Inet4Address.getLocalHost(), PORT, Inet4Address.getLocalHost(), LOCAL_PORT); */ Socket socket = new Socket(); // 绑定到本地20001端口 socket.bind(new InetSocketAddress(Inet4Address.getLocalHost(), LOCAL_PORT)); return socket; } private static void initSocket(Socket socket) throws SocketException { // 设置读取超时时间为2秒 socket.setSoTimeout(2000); // 是否复用未完全关闭的Socket地址,对于指定bind操作后的套接字有效 socket.setReuseAddress(true); // 是否开启Nagle算法 socket.setTcpNoDelay(true); // 是否需要在长时无数据响应时发送确认数据(类似心跳包),时间大约为2小时 socket.setKeepAlive(true); // 对于close关闭操作行为进行怎样的处理;默认为false,0 // false、0:默认情况,关闭时立即返回,底层系统接管输出流,将缓冲区内的数据发送完成 // true、0:关闭时立即返回,缓冲区数据抛弃,直接发送RST结束命令到对方,并无需经过2MSL等待 // true、200:关闭时最长阻塞200毫秒,随后按第二情况处理 socket.setSoLinger(true, 20); // 是否让紧急数据内敛,默认false;紧急数据通过 socket.sendUrgentData(1);发送 socket.setOOBInline(true); // 设置接收发送缓冲器大小 socket.setReceiveBufferSize(64 * 1024 * 1024); socket.setSendBufferSize(64 * 1024 * 1024); // 设置性能参数:短链接,延迟,带宽的相对重要性 socket.setPerformancePreferences(1, 1, 0); } private static void todo(Socket client) throws IOException { // 得到Socket输出流 OutputStream outputStream = client.getOutputStream(); // 得到Socket输入流 InputStream inputStream = client.getInputStream(); byte[] buffer = new byte[256]; ByteBuffer byteBuffer = ByteBuffer.wrap(buffer); // byte byteBuffer.put((byte) 126); // char char c = 'a'; byteBuffer.putChar(c); // int int i = 2323123; byteBuffer.putInt(i); // bool boolean b = true; byteBuffer.put(b ? (byte) 1 : (byte) 0); // Long long l = 298789739; byteBuffer.putLong(l); // float float f = 12.345f; byteBuffer.putFloat(f); // double double d = 13.31241248782973; byteBuffer.putDouble(d); // String String str = "Hello你好!"; byteBuffer.put(str.getBytes()); // 发送到服务器 outputStream.write(buffer, 0, byteBuffer.position() + 1); // 接收服务器返回 int read = inputStream.read(buffer); System.out.println("收到数量:" + read); // 资源释放 outputStream.close(); inputStream.close(); } }
import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Inet4Address; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.nio.ByteBuffer; public class Server { private static final int PORT = 20000; public static void main(String[] args) throws IOException { ServerSocket server = createServerSocket(); initServerSocket(server); // 绑定到本地端口上 server.bind(new InetSocketAddress(Inet4Address.getLocalHost(), PORT), 50); System.out.println("服务器准备就绪~"); System.out.println("服务器信息:" + server.getInetAddress() + " P:" + server.getLocalPort()); // 等待客户端连接 for (;;) { // 得到客户端 Socket client = server.accept(); // 客户端构建异步线程 ClientHandler clientHandler = new ClientHandler(client); // 启动线程 clientHandler.start(); } } private static ServerSocket createServerSocket() throws IOException { // 创建基础的ServerSocket ServerSocket serverSocket = new ServerSocket(); // 绑定到本地端口20000上,并且设置当前可允许等待链接的队列为50个 //serverSocket = new ServerSocket(PORT); // 等效于上面的方案,队列设置为50个 //serverSocket = new ServerSocket(PORT, 50); // 与上面等同 // serverSocket = new ServerSocket(PORT, 50, Inet4Address.getLocalHost()); return serverSocket; } private static void initServerSocket(ServerSocket serverSocket) throws IOException { // 是否复用未完全关闭的地址端口 serverSocket.setReuseAddress(true); // 等效Socket#setReceiveBufferSize serverSocket.setReceiveBufferSize(64 * 1024 * 1024); // 设置serverSocket#accept超时时间 // serverSocket.setSoTimeout(2000); // 设置性能参数:短链接,延迟,带宽的相对重要性 serverSocket.setPerformancePreferences(1, 1, 1); } //客户端消息处理 private static class ClientHandler extends Thread { private Socket socket; ClientHandler(Socket socket) { this.socket = socket; } @Override public void run() { super.run(); System.out.println("新客户端连接:" + socket.getInetAddress() + " P:" + socket.getPort()); try { // 得到套接字流 OutputStream outputStream = socket.getOutputStream(); InputStream inputStream = socket.getInputStream(); byte[] buffer = new byte[256]; int readCount = inputStream.read(buffer); ByteBuffer byteBuffer = ByteBuffer.wrap(buffer, 0, readCount); // byte byte be = byteBuffer.get(); // char char c = byteBuffer.getChar(); // int int i = byteBuffer.getInt(); // bool boolean b = byteBuffer.get() == 1; // Long long l = byteBuffer.getLong(); // float float f = byteBuffer.getFloat(); // double double d = byteBuffer.getDouble(); // String int pos = byteBuffer.position(); String str = new String(buffer, pos, readCount - pos - 1); System.out.println("收到数量:" + readCount + " 数据:" + be + "\n" + c + "\n" + i + "\n" + b + "\n" + l + "\n" + f + "\n" + d + "\n" + str + "\n"); outputStream.write(buffer, 0, readCount); outputStream.close(); inputStream.close(); } catch (Exception e) { System.out.println("连接异常断开"); } finally { // 连接关闭 try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } System.out.println("客户端已退出:" + socket.getInetAddress() + " P:" + socket.getPort()); } } }
基本类型数据传输
1、byte(1个字节)、char(1个字节)、short(2个字节);
2、boolean(1个字节)、int(4个字节)、long(8个字节);
3、float(4个字节)、double(8个字节)、string(可变)。

浙公网安备 33010602011771号