JAVAEE值网络编程(2)_TCP流套接字及通信模型、TCP网络编程及代码实例

news2024/12/28 3:30:24

前言

 在上一节内容中,我们介绍了什么是套接字,以及使用UDP数据报套接字网络编程, 最后我们还介绍了Java数据报套接字通信模型以及相关代码实例。在这一节我们将会介绍TCP流套接字编程。


一、流套接字及通信模型

1.1 TCP套接字

TCP,即Transmission Control Protocol(传输控制协议),是传输层协议。

TCP主要特点:(会在后续单独章节中详细介绍)

  1. 有连接
  2. 可靠传输
  3. 面向字节流
  4. 有接收、发送缓冲区
  5. 大小不限

对于字节流来说,可以简单的理解为,传输数据是基于IO流,流式数据的特征就是在IO流没有关闭的情况下,是无边界的数据,可以多次发送,也可以分开多次接收。

1.2 原始套接字

 原始套接字用于自定义传输层协议,用于读写内核没有处理的IP协议数据。

1.3 Java流套接字通信模型

在这里插入图片描述
注意事项:

  1. 客户端和服务端:开发时,经常是基于一个主机开启两个进程作为客户端和服务端,但真实的场景,一般都是不同主机。
  2. 注意目的IP和目的端口号,标识了一次数据传输时要发送数据的终点主机和进程
  3. Socket编程我们是使用流套接字和数据报套接字,基于传输层的TCP或UDP协议,但应用层协议也需要考虑。
  4. 端口占用问题:如果一个进程A已经绑定了一个端口,再启动一个进程B绑定该端口,就会报错,这种情况也叫端口被占用
  5. 解决端口被占用的问题:
    如果占用端口的进程A不需要运行,就可以关闭A后,再启动需要绑定该端口的进程B;
    如果需要运行A进程,则可以修改进程B的绑定端口,换为其他没有使用的端口。

二、TCP流套接字编程

2.1 ServerSocket API

ServerSocket 是创建TCP服务端Socket的API,ServerSocket构造方法如下:
在这里插入图片描述
 另外,ServerSocket 相关方法如下:
在这里插入图片描述

2.2 Socket API

Socket 是客户端Socket,或服务端中接收到客户端建立连接(accept方法)的请求后,返回的服务端Socket。不管是客户端还是服务端Socket,都是双方建立连接以后,保存的对端信息,及用来与对方收发数据的。
 Socket的构造方法如下:
在这里插入图片描述
 Socket相关方法:
在这里插入图片描述

2.3 TCP中的长短连接

TCP发送数据时,需要先建立连接,什么时候关闭连接就决定是短连接还是长连接:

短连接:每次接收到数据并返回响应后,都关闭连接,短连接只能一次收发数据。
长连接:不关闭连接,一直保持连接状态,双方不停的收发数据,长连接可以多次收发数据。

TCP长短连接对比:

  1. 建立连接、关闭连接的耗时:短连接每次请求、响应都需要建立连接,关闭连接;而长连接只需要第一次建立连接,之后的请求、响应都可以直接传输。相对来说建立连接,关闭连接也是要耗时的,长连接效率更高。
  2. 主动发送请求不同:短连接一般是客户端主动向服务端发送请求;而长连接可以是客户端主动发送请求,也可以是服务端主动发。
  3. 两者的使用场景有不同:短连接适用于客户端请求频率不高的场景,如浏览网页等。长连接适用于客户端与服务端通信频繁的场景,如聊天室,实时游戏等。

三、代码实例

3.1 TCP 客户端

package network;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.lang.ref.SoftReference;
import java.net.Socket;
import java.util.Scanner;

/**
 * @author Zhang
 * @date 2024/5/2115:13
 * @Description:
 */
public class TcpEchoClient {
    private Socket socket = null;

    public TcpEchoClient(String serverIP,int serverPort) throws IOException {
        // 此时,需要在创建 Socket 的同时,和服务器”建立连接“,此时就得告诉 Socket 服务器在哪里
        socket = new Socket(serverIP,serverPort);
    }

    public void start(){

        Scanner scanner = new Scanner(System.in);

        try(InputStream  inputStream = socket.getInputStream();
            OutputStream outputStream = socket.getOutputStream()){
            PrintWriter writer = new PrintWriter(outputStream);
            Scanner scannerNetwork = new Scanner(inputStream);
            while (true){
                // 1. 从控制台读取用户输入的内容
                System.out.print("-->");
                String request = scanner.next();

                // 2. 把字符串作为请求,发给服务器
                //    这里用println,是为了让后后面请求带上换行,也就是和服务器读取请求,scanner.next 呼应
                writer.println(request);
                writer.flush();

                // 3. 读取服务器返回的响应
                String response = scannerNetwork.next();
                
                // 4. 在界面上显示内容
                System.out.println(response);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

    public static void main(String[] args) throws IOException {
        TcpEchoClient client = new TcpEchoClient("127.0.0.1",9090);
        client.start();
    }
}

3.2 TCP 服务端

package network;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author Zhang
 * @date 2024/5/2115:13
 * @Description:
 */
public class TcpEchoServer {

    private ServerSocket serverSocket  = null;
    public TcpEchoServer(int port) throws IOException {
        serverSocket = new ServerSocket(port);// 指定端口号
    }

    public void start() throws IOException {
        System.out.println("服务器启动!");

        ExecutorService service = Executors.newCachedThreadPool();  //创建线程池
        while (true){
            //通过 accept 方法,把内核中已经建立好的连接那回到应用程序中
            Socket clientSocket = serverSocket.accept();

            /**
             *  此处不能直接调用processConnection,这会导致服务器不能处理多个客户端,通过创建新的线程来调用是更合理的方法
             *             Thread t = new Thread(()->{
             *                 try {
             *                     processConnection(clientSocket);
             *                 } catch (IOException e) {
             *                     throw new RuntimeException(e);
             *                 }
             *             });
             *            t.start();
             */
            //使用创建线程的方式开销会比较大,此处我们使用线程池的方式
            service.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        processConnection(clientSocket);
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            });
        }
    }


    // 通过这个方法,来处理当前的连接
    private void processConnection(Socket clientSocket) throws IOException {
        //进入方法,先打印一个日志
        System.out.printf("[%s:%d] 客户端上线\n",clientSocket.getInetAddress(),clientSocket.getPort());


        //InputStream 和OutputStream就是在字节流,可以借助这两个对象,完成数据的“发送”和“接收”
        //通过InputStream 进行read 操作,就是“接收”操作(站在CPU角度)
        //通过OutputStream 进行 writ额操作,就是“发送”操作。
        try(InputStream inputStream = clientSocket.getInputStream();
            OutputStream outputStream = clientSocket.getOutputStream()){
            //使用try()方式,避免后续使用完了流对象,忘记关闭

            //由于客户端发来的可能是“多条数据”,针对对条数据,进行循环处理
            while (true){
                Scanner scanner = new Scanner(inputStream);
                if (!scanner.hasNext()){
                    //连接断开了,此时循环就应该结束
                    System.out.printf("[%s:%d] 客户端下线!\n",clientSocket.getInetAddress(),clientSocket.getPort());
                    break;
                }

                // 1. 读取请求并解析, 此处就以next来作为读取请求的方式,next 的规则:读取到“空白符”就返回。
                String request = scanner.next();

                // 2. 根据请求,计算响应
                String response = process(request);

                //3. 把响应写回客户端
                //     方式一:可以把String转成字符数组,写入到OutputStream
                //      方式二:使用PrintWriter 把OutputStream 包裹一下,来写入字符串
                PrintWriter printWriter = new PrintWriter(outputStream);
                printWriter.println(response);  //此处的打印是写入到outputStream 对应的流对象中,也就是写入到 clientSocket
                printWriter.flush();  //刷新缓冲区,如果没有此操作,数据仍然可能在内存中,没有被写入网卡

                // 4.打印一下这次请求交互的内容
                System.out.printf("[%s:%d] req=%s resp=%s\n",clientSocket.getInetAddress(),clientSocket.getPort(),request,response);

            }
        }catch (IOException e){
            e.printStackTrace();
        }finally {
            //在这个地方进行clientSocket的关闭,processConnection就是处理一个连接,如果这个方法执行完毕,这个连接也就处理完了
            clientSocket.close();
        }

    }

     public String process(String request){
        // 此处也是写的服务器,响应和请求一样
        return request;
     }

    public static void main(String[] args) throws IOException {
        TcpEchoServer server = new TcpEchoServer(9090);
        server.start();
    }
}

启动客户端和服务器

运行结果:

//客户端发送请求
TcpEchoClient:

-->张三
张三
-->
----------------------------------
//服务器响应
TcpEchoServer:

服务器启动!
[/127.0.0.1:60107] 客户端上线
[/127.0.0.1:60107] req=张三 resp=张三

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1807624.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

elasticsearch hanlp 插件安装操作

elasticsearch hanlp 插件安装操作 下载 hanlp 插件上传hanlp插件到elasticsearch服务器安装hanlp插件kibana测试 下载 hanlp 插件 这里大家根据自己对应的 elasticsearch 版本下载匹配版本的 hanlp 插件,由于 hanlp 及 elasticsearch 各个版本之间差别较大&#x…

基于小波样条框架的一维时间序列信号降噪方法(MATLAB R2018A)

1952年,DUFFIN在研究非调和Fourier级数时引入了Hilbert空间中框架的概念,然而并没有引起很大的反响。1986年,DAUBECHIES研究发现利用框架可以将L2(R)中的函数展开成类似标准正交基的级数,并且用框架研究函数时所需的条件要比用标准…

C#操作MySQL从入门到精通(17)——使用联结

前言: 我们在查询数据的过程中有时候查询的数据不是来自一个表而是来自多个表,本文使用的测试数据如下: 本文使用了两个表student_info、address_info student_info的数据如下: address_info的数据如下: 1、内联结 所谓内联结就是求交集,两个表都有的数据才是有效数…

机器学习周记(第四十二周:AT-LSTM)2024.6.3~2024.6.9

目录 摘要Abstract一、文献阅读1. 题目2. abstract3. 网络架构3.1 LSTM3.2 注意力机制概述3.3 AT-LSTM3.4 数据预处理 4. 文献解读4.1 Introduction4.2 创新点4.3 实验过程4.3.1 训练参数4.3.2 数据集4.3.3 实验设置4.3.4 实验结果 5. 基于pytorch的transformer 摘要 本周阅读…

Python魔法之旅专栏(导航)

目录 推荐阅读 1、Python筑基之旅 2、Python函数之旅 3、Python算法之旅 4、博客个人主页 首先,感谢老铁们一直以来对我的支持与厚爱,让我能坚持把Python魔法方法专栏更新完毕! 其次,为了方便大家查阅,我将此专栏…

初识 java 2

1. idea 的调试 1. 点击鼠标左键设置断点 2.运行到断点处 点击 或点击鼠标右键,再点击 使代码运行到断点处,得到 2. 输出到控制台 System.out.println(value);//输出指定的内容,并换行 value 要打印的内容System.out.print(value);…

半年了,3588来了

端午两天,LAB1964又搞了新东西,RK3588,已经算是千呼万唤始出来,等的时间也是足够久了。 ——价格贵 RK3588 是真的贵,实验室老板贴了10片3588套片,就花了将近4000块钱,所以说,决定要…

12. Django 第三方功能应用

12. 第三方功能应用 因为Django具有很强的可扩展性, 所以延伸了第三方功能应用. 通过本章的学习, 读者能够在网站开发过程中快速实现API接口开发, 验证码生成与使用, 站内搜索引擎, 第三方网站实现用户注册, 异步任务和定时任务, 即时通信等功能.12.1 Django Rest Framework框…

将克隆到本地的6.824项目上传到自己的github

前置知识见:把自己在本地完成的mit6.s081项目上传到自己的github仓库里_mit6.s081 lab上传-CSDN博客 先在github建立一个自己的仓库 由于github可以给自己的主分支改名了,我这次是勾选了创建README文件 在本地同样是建立一条remote分支 git remote add…

C语言详解文件操作

目录 什么是文件? 为什么使用文件? 程序文件和数据文件、文本文件和二进制文件 1.程序文件和数据文件 1.1程序文件 1.2数据文件 2.文本文件和二进制文件 文件的打开和关闭(流、标准流、文件指针和文件的打开与关闭) 1.流和标…

体素技术在AI绘画中的革新作用

随着人工智能技术的不断进步,AI绘画已经成为艺术创作和视觉设计领域的一大趋势。在众多推动AI绘画发展的技术中,体素技术以其独特的优势,正在逐渐改变着我们对计算机生成图像的认识。本文旨在探讨体素技术在AI绘画中的应用与影响,…

电脑存储设备,固态硬盘介绍,usb接口

简介 存储设备分为两大类主存和辅存,另外还有专门提供存储服务的网络存储 主存储器 随机存取存储器(RAM, Random Access Memory) 特点:高速、易失性存储器,断电后数据丢失。用途:临时存储正在使用的数据…

超详解——python数字和运算——基础篇

目录 1.位运算 2. 常用内置函数/模块 math模块: random模块: decimal模块: 3.内置函数: 总结: 1.位运算 位运算是对整数在内存中的二进制表示进行操作。Python支持以下常见的位运算符: 按位与&…

adb shell进入设备后的命令

目录 一、查看删除手机 /data/local/tmp/下的文件 二、设置权限 三、查看手机设备正在运行的服务 四、可能需要的adb 命令 一、查看删除手机 /data/local/tmp/下的文件 可以通过以下命令: adb shell # 进入设备 ls /data/local/tmp/ # 查看文件夹下的内容…

文章解读与仿真程序复现思路——电力自动化设备EI\CSCD\北大核心《计及电力不平衡风险的配电网分区协同规划》

本专栏栏目提供文章与程序复现思路,具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源…

基于小波的多元信号降噪-基于马氏距离和EDF统计(MATLAB R2018a)

马氏距离是度量学习中一种常用的距离指标,通常被用作评定数据样本间的相似度,可以应对高维线性分布数据中各维度间非独立同分布的问题,计算方法如下。 (1)计算样本向量的平均值。 (2)计算样本向…

【数据结构】【版本1.0】【线性时代】——顺序表

快乐的流畅:个人主页 个人专栏:《算法神殿》《数据结构世界》《进击的C》 远方有一堆篝火,在为久候之人燃烧! 文章目录 引言一、顺序表的概念1.1 最基础的数据结构:数组1.2 数组与顺序表的区别 二、静态顺序表三、动态…

HTML做成一个粒子漩涡特效页面

大家好,今天制作制作一个粒子漩涡特效的页面! 先看具体效果: 要在一个单一的 index.html 页面中实现粒子漩涡特效,我们可以使用HTML、CSS和JavaScript(不需要外部库)。下面是一个简单的例子,展…

探索基于订阅式的电视App:Android TV 端强大的开源视频播放器

探索基于订阅式的电视App:Android TV 端强大的开源视频播放器 在智能电视和流媒体日益普及的今天,一款强大的视频播放器是家庭娱乐的重要组成部分。正是这样一款为Android TV设计的开源视频播放器。本文将深入探讨电视盒子OSC的技术特点、使用方法以及其…

直接用sql语句来查询和分析excel表,不需要导数据,提供了sql语句自动生成,不会sql也能用

用sql语句来查询excel表,我们需要把excel表格导入到数据库中,然后用数据库的管理工具写sql语句来进行查询。方法有很多,我们不一一描述。 今天我们要说的是直接用sql语句来查询和分析excel表。为什么有这么一个想法呢?程…