【Java 网络编程】TCP 数据传输示例 ( 客户端参数设置 | 服务器端参数设置 | ByteBuffer 存放读取数据类型 )

简介: 【Java 网络编程】TCP 数据传输示例 ( 客户端参数设置 | 服务器端参数设置 | ByteBuffer 存放读取数据类型 )

文章目录

I 客户端代码示例

II 服务器端代码示例

III 运行结果



I 客户端代码示例


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.nio.ByteBuffer;
/**
 * TCP 客户端
 */
public class Client {
    public static void main(String[] args) {
        try {
            //I. 创建 Socket 对象并绑定本地端口
            //1. 创建空的 Socket 对象 , 方便之后设置参数
            Socket socket = new Socket();
            //2. 绑定本地端口
            socket.bind(new InetSocketAddress(Inet4Address.getLocalHost(), 8887));
            System.out.println("客户端 Socket 创建完毕");
            //II. 设置 Socket 对象参数 , 注意这些参数只能在客户端没有连接服务器的时候设置 , 连接服务器之后设置是无效的
            //1. 设置从 Socket 对象输入流中读取数据的阻塞等待超时时间
            //      当与 Socket 对象关联的 InputStream 输入流执行 read() 操作时 , 其阻塞时间为这个超时时间
            //      如果超过了该时间还没有收到任何数据 , 就会抛出异常
            socket.setSoTimeout(3000);
            //2. 设置是否可以复用 Socket 绑定的地址和端口号
            //      Socket 连接在建立时 , 会使用之前绑定本地的 IP 地址和端口号
            //      这个端口号在使用之后 , 2 分钟之内不允许再次使用
            //      进行了该设置之后 , 可以在连接关闭之后 , 马上使用该本地 IP 地址和端口号
            socket.setReuseAddress(true);
            //3. 设置是否开启 Nagle 算法 , Nagle 算法会导致多次发送的少量数据合并 , 即沾包情况出现
            //      在需要低延迟传输的情况下是需要关闭该算法的 , 该算法会导致数据沾包情况出现
            socket.setTcpNoDelay(true);
            //4. 在长时间 ( 2 小时 ) 没有数据交互 , 是否需要发送心跳包确认连接
            socket.setKeepAlive(true);
            //5. 调用 Socket 对象的 close 方法之后的处理方式
            //   1> 默认情况 : false , 0
            //      如果 boolean on 设置成false , 不处理连接的缓存数据 , 调用 close 会立刻关闭连接
            //      系统底层会操作输出流发送剩余缓存数据 , 将缓冲区中的数据发送给连接对方
            //      如果设置 false 不会产生阻塞操作
            //   2> setSoLinger( true , 20 ) 情况 :
            //      如果设置 boolean on 参数为 true , int linger 参数设置一个大于等于 0 的参数
            //      那么在关闭的时候 , 阻塞 linger 毫秒 , 之后缓冲区如果还有数据 , 就会被丢弃
            //      直接向连接对方发送结束命令 , 无需经过超时等待
            //   3> setSoLinger( true , 0 ) 情况 :
            //      如果设置成 0 , 那么其后果是不阻塞 , 也不让系统接管输出流
            //      立刻丢弃缓冲区数据 , 向对方发送 RST 命令
            socket.setSoLinger(true, 10);
            //6. 设置紧急数据是否内敛 , 默认情况时 false 关闭的
            //      紧急数据 : 紧急数据是 Socket 对象通过调用 sendUrgentData 发送出去的数据
            //                该方法参数是一个 int 值 , 仅有最低的 8 位是有效的
            socket.setOOBInline(true);
            //7. 设置发送接收缓冲区大小
            socket.setReceiveBufferSize(64 * 1024 * 1024);
            socket.setSendBufferSize(64 * 1024 * 1024);
            //8. 设置性能参数 : ① 连接时长 , ② 最低延迟 , ③ 带宽
            //      设置的值不是具体的参数 , 而是连接的性能权重 , 对哪个性能要求比较高 ;
            //      上面的延迟和带宽的性能是互斥的 , 低延迟新能好 , 带宽性能就差
            socket.setPerformancePreferences(0, 2, 0);
            System.out.println("客户端 Socket 参数设置完毕");
            //III. 连接服务器
            //1. 连接到服务器端的 8888 端口 , 设置连接超时 3000 毫秒
            socket.connect(new InetSocketAddress(Inet4Address.getLocalHost(), 8888), 3000);
            System.out.println("客户端 Socket 连接服务器完毕");
            //IV. 数据发送与接收
            //1. 获取输出流和输入流
            OutputStream outputStream = socket.getOutputStream();
            InputStream inputStream = socket.getInputStream();
            //2. 使用 ByteBuffer 向 byte[] 数组中存储数据
            byte[] buffer = new byte[256];
            ByteBuffer byteBuffer = ByteBuffer.wrap(buffer);
            //3. 向数组写入 byte 类型数据
            byteBuffer.put((byte) 0x01);
            //4. 向数组中写入 short 类型数据
            byteBuffer.putShort((short) 1);
            //5. 向数组中写入 int 类型数据
            byteBuffer.putInt(1);
            //6. 向数组中写入 char 类型数据
            byteBuffer.putChar('a');
            //7. 向数组中写入 boolean 类型数据
            //      此处使用 byte 类型模拟 , true 为 1, false 为 0
            boolean bool = true;
            byteBuffer.put((byte) (bool ? 1 : 0));
            //8. 向数组中写入 long 类型数据
            byteBuffer.putLong((long)1);
            //9. 向数组中写入 float 类型数据
            byteBuffer.putFloat(3.14f);
            //10. 向数组中写入 double 类型数据
            byteBuffer.putDouble(3.14);
            //11. 向数组中写入 String 类型数据
            //      先把 String 字符串转为 byte[] 数组, 在放入 byteBuffer 中
            byteBuffer.put("Hello World".getBytes());
            //12. 将 byte[] 数据发送到服务器端
            outputStream.write(buffer, 0, byteBuffer.position() + 1);
            System.out.println("客户端 Socket 将各种类型数据发送到了服务器端");
            //13. 接收服务器端反馈的数据
            int readLen = inputStream.read(buffer);
            System.out.println("客户端 Socket 接收到服务器端数据 " + readLen + " 字节");
            //V. 释放资源
            //1. 关闭输入输出流
            outputStream.close();
            inputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}




II 服务器端代码示例


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;
/**
 * TCP 服务器端
 */
public class Server {
    public static void main(String[] args) {
        try {
            //I. 设置服务器套接字
            //1. 创建服务器端 , 注意创建一个空的服务器套接字 , 一遍后面设置更详细的参数
            ServerSocket serverSocket = new ServerSocket();
            System.out.println("服务器端端 ServerSocket 创建完毕");
            //2. 设置从 Socket 对象输入流中读取数据的阻塞等待超时时间
            //      当与 Socket 对象关联的 InputStream 输入流执行 read() 操作时 , 其阻塞时间为这个超时时间
            //      如果超过了该时间还没有收到任何数据 , 就会抛出异常
            serverSocket.setSoTimeout(30000);
            //3. 设置是否可以复用 Socket 绑定的地址和端口号
            //      Socket 连接在建立时 , 会使用之前绑定本地的 IP 地址和端口号
            //      这个端口号在使用之后 , 2 分钟之内不允许再次使用
            //      进行了该设置之后 , 可以在连接关闭之后 , 马上使用该本地 IP 地址和端口号
            serverSocket.setReuseAddress(true);
            //4. 设置发送接收缓冲区大小
            serverSocket.setReceiveBufferSize(64 * 1024 * 1024);
            //5. 设置性能参数 : ① 连接时长 , ② 最低延迟 , ③ 带宽
            //      设置的值不是具体的参数 , 而是连接的性能权重 , 对哪个性能要求比较高 ;
            //      上面的延迟和带宽的性能是互斥的 , 低延迟新能好 , 带宽性能就差
            serverSocket.setPerformancePreferences(0, 2, 0);
            System.out.println("服务器端端 ServerSocket 设置完毕");
            //6. 绑定本地端口 , 只有绑定了本地端口 , 服务器端套接字才能正式工作
            //      服务器端才算是正式创建完毕
            //      上面的设置一定要在绑定接口之前设置完毕 , 之后在设置 serverSocket 是无效的
            serverSocket.bind(new InetSocketAddress(Inet4Address.getLocalHost(), 8888), 88);
            System.out.println("服务器端端 ServerSocket 绑定 8888 端口完毕");
            //II. 等待服务器端连接
            //1. 服务器端阻塞 , 等待客户端连接服务器端的 8888 端口号
            Socket clientSocket = serverSocket.accept();
            //2. 创建客户端异步处理线程 , 处理服务器端与该客户端之间的交互 , 创建之后直接启动线程即可
            ClientHandler clientHandler = new ClientHandler(clientSocket);
            clientHandler.start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
     * 客户端异步处理线程
     *  每当有客户端连接服务器 , 就开启一个线程处理与该客户端之间的交互
     */
    private static class ClientHandler extends Thread {
        /**
         * 客户端线程
         */
        private Socket clientSocket;
        public ClientHandler(Socket clientSocket) {
            this.clientSocket = clientSocket;
        }
        @Override
        public void run() {
            super.run();
            System.out.println("客户端 : " + clientSocket.getInetAddress() + " 连接到服务器端");
            try {
                //I. 获取数据交互的输入流 , 输出流 , 及缓冲区
                //1. 从客户端 Socket 中获取与客户端进行数据交互的输入输出流
                OutputStream outputStream = clientSocket.getOutputStream();
                InputStream inputStream = clientSocket.getInputStream();
                //2. 从客户端读取数据 , 并使用 ByteBuffer 读取其中各种类型的数据
                byte[] buffer = new byte[256];
                int readCount = inputStream.read(buffer);
                ByteBuffer byteBuffer = ByteBuffer.wrap(buffer, 0, readCount);
                //II. 按照顺序读取存放的数据
                //注意 : 要按照存放的顺序读取
                //1. 读取 byte 类型数据
                byte var_byte = byteBuffer.get();
                System.out.println("① byte 类型数据 : " + var_byte);
                //2. 读取 short 类型数据
                short var_short = byteBuffer.getShort();
                System.out.println("② short 类型数据 : " + var_short);
                //3. 读取 int 类型数据
                int var_int = byteBuffer.getInt();
                System.out.println("③ int 类型数据 : " + var_int);
                //4. 读取 char 类型数据
                char var_char = byteBuffer.getChar();
                System.out.println("④ char 类型数据 : " + var_char);
                //5. 读取 short 类型数据
                boolean var_boolean = byteBuffer.get() == 1;
                System.out.println("⑤ boolean 类型数据 : " + var_boolean);
                //6. 读取 long 类型数据
                long var_long = byteBuffer.getLong();
                System.out.println("⑥ long 类型数据 : " + var_long);
                //7. 读取 float 类型数据
                float var_float = byteBuffer.getFloat();
                System.out.println("⑦ float 类型数据 : " + var_float);
                //8. 读取 double 类型数据
                double var_double = byteBuffer.getDouble();
                System.out.println("⑧ double 类型数据 : " + var_double);
                //9. 读取 short 类型数据
                int start = byteBuffer.position();
                String var_string = new String(buffer, start, readCount - start - 1);
                System.out.println("⑨ String 类型数据 : " + var_string);
                //III. 将接收的数据再发送回去, 并关闭连接
                outputStream.write(buffer, 0, readCount);
                outputStream.close();
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                // 连接关闭
                try {
                    clientSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    System.out.println("客户端与服务器端交互完成");
                }
            }
        }
    }
}




III 运行结果


1. 先运行服务器端 :

服务器端端 ServerSocket 创建完毕
服务器端端 ServerSocket 设置完毕
服务器端端 ServerSocket 绑定 8888 端口完毕


2. 在运行客户端 :


客户端 Socket 创建完毕
客户端 Socket 参数设置完毕
客户端 Socket 连接服务器完毕
客户端 Socket 将各种类型数据发送到了服务器端
客户端 Socket 接收到服务器端数据 42 字节


3. 最终查看服务器端打印 :


服务器端端 ServerSocket 创建完毕
服务器端端 ServerSocket 设置完毕
服务器端端 ServerSocket 绑定 8888 端口完毕
客户端 : /192.168.87.2 连接到服务器端
① byte 类型数据 : 1
② short 类型数据 : 1
③ int 类型数据 : 1
④ char 类型数据 : a
⑤ boolean 类型数据 : true
⑥ long 类型数据 : 1
⑦ float 类型数据 : 3.14
⑧ double 类型数据 : 3.14
⑨ String 类型数据 : Hello World
客户端与服务器端交互完成
相关实践学习
自建数据库迁移到云数据库
本场景将引导您将网站的自建数据库平滑迁移至云数据库RDS。通过使用RDS,您可以获得稳定、可靠和安全的企业级数据库服务,可以更加专注于发展核心业务,无需过多担心数据库的管理和维护。
Sqoop 企业级大数据迁移方案实战
Sqoop是一个用于在Hadoop和关系数据库服务器之间传输数据的工具。它用于从关系数据库(如MySQL,Oracle)导入数据到Hadoop HDFS,并从Hadoop文件系统导出到关系数据库。 本课程主要讲解了Sqoop的设计思想及原理、部署安装及配置、详细具体的使用方法技巧与实操案例、企业级任务管理等。结合日常工作实践,培养解决实际问题的能力。本课程由黑马程序员提供。
目录
相关文章
|
12月前
|
JSON 移动开发 网络协议
Java网络编程:Socket通信与HTTP客户端
本文全面讲解Java网络编程,涵盖TCP与UDP协议区别、Socket编程、HTTP客户端开发及实战案例,助你掌握实时通信、文件传输、聊天应用等场景,附性能优化与面试高频问题解析。
|
10月前
|
JSON 网络协议 安全
【Java】(10)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
449 1
|
10月前
|
JSON 网络协议 安全
【Java基础】(1)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
421 1
|
存储 网络协议 安全
Java网络编程,多线程,IO流综合小项目一一ChatBoxes
**项目介绍**:本项目实现了一个基于TCP协议的C/S架构控制台聊天室,支持局域网内多客户端同时聊天。用户需注册并登录,用户名唯一,密码格式为字母开头加纯数字。登录后可实时聊天,服务端负责验证用户信息并转发消息。 **项目亮点**: - **C/S架构**:客户端与服务端通过TCP连接通信。 - **多线程**:采用多线程处理多个客户端的并发请求,确保实时交互。 - **IO流**:使用BufferedReader和BufferedWriter进行数据传输,确保高效稳定的通信。 - **线程安全**:通过同步代码块和锁机制保证共享数据的安全性。
718 23
|
SQL 监控 安全
网络安全与信息安全:漏洞、加密与安全意识
随着互联网的迅猛发展,网络安全和信息安全问题日益受到关注。本文深入探讨了网络安全漏洞、加密技术以及提高个人和组织的安全意识的重要性。通过分析常见的网络攻击手段如缓冲区溢出、SQL注入等,揭示了计算机系统中存在的缺陷及其潜在威胁。同时,详细介绍了对称加密和非对称加密算法的原理及应用场景,强调了数字签名和数字证书在验证信息完整性中的关键作用。此外,还讨论了培养良好上网习惯、定期备份数据等提升安全意识的方法,旨在帮助读者更好地理解和应对复杂的网络安全挑战。
|
SQL 安全 网络安全
网络安全与信息安全:知识分享####
【10月更文挑战第21天】 随着数字化时代的快速发展,网络安全和信息安全已成为个人和企业不可忽视的关键问题。本文将探讨网络安全漏洞、加密技术以及安全意识的重要性,并提供一些实用的建议,帮助读者提高自身的网络安全防护能力。 ####
552 17
|
SQL 安全 网络安全
网络安全与信息安全:关于网络安全漏洞、加密技术、安全意识等方面的知识分享
随着互联网的普及,网络安全问题日益突出。本文将从网络安全漏洞、加密技术和安全意识三个方面进行探讨,旨在提高读者对网络安全的认识和防范能力。通过分析常见的网络安全漏洞,介绍加密技术的基本原理和应用,以及强调安全意识的重要性,帮助读者更好地保护自己的网络信息安全。
410 10
|
存储 SQL 安全
网络安全与信息安全:关于网络安全漏洞、加密技术、安全意识等方面的知识分享
随着互联网的普及,网络安全问题日益突出。本文将介绍网络安全的重要性,分析常见的网络安全漏洞及其危害,探讨加密技术在保障网络安全中的作用,并强调提高安全意识的必要性。通过本文的学习,读者将了解网络安全的基本概念和应对策略,提升个人和组织的网络安全防护能力。
|
SQL 安全 网络安全
网络安全与信息安全:关于网络安全漏洞、加密技术、安全意识等方面的知识分享
在数字化时代,网络安全和信息安全已成为我们生活中不可或缺的一部分。本文将介绍网络安全漏洞、加密技术和安全意识等方面的内容,并提供一些实用的代码示例。通过阅读本文,您将了解到如何保护自己的网络安全,以及如何提高自己的信息安全意识。
389 10
|
监控 安全 网络安全
网络安全与信息安全:漏洞、加密与意识的交织
在数字时代的浪潮中,网络安全与信息安全成为维护数据完整性、保密性和可用性的关键。本文深入探讨了网络安全中的漏洞概念、加密技术的应用以及提升安全意识的重要性。通过实际案例分析,揭示了网络攻击的常见模式和防御策略,强调了教育和技术并重的安全理念。旨在为读者提供一套全面的网络安全知识框架,从而在日益复杂的网络环境中保护个人和组织的资产安全。