基于UDP和TCP套接字实现简单的回显客户端服务器程序

news2024/11/24 16:08:16

目录

1. 套接字

2. 基于UDP 套接字实现的简单客户端 服务器程序

3. 基于TCP套接字实现的简单客户端 服务器程序


1. 套接字

       之前我们有分享到协议分层这个概念,其中就讲到上层协议调用下层协议,下层协议给上层协议提供支持,这里支持指的是就是socket套接字,它是操作系统给应用程序提供的一组用于网络编程的API(传输层给应用层的支持).


2. 基于UDP 套接字实现的简单客户端 服务器程序

       UDP是传输层协议之一,它的特点是无连接,不可靠,面向数据报传输,半双工(数据传输允许数据在两个方向上传输,但在某一时刻,只允许数据在一个方向传输).

       ● UDP的套接字

          DatagramSocket(指定端口号为服务器,不指定端口号则为客户端)类是用于发送和接收数据报的套接字,而DatagramPacket类是表示UDP数据报,客户端和服务器双方传输的都是数据报.

          1) DatagramSocket常用构造方法有:

//不指定端口号,表示客户端
DatagramSocket socket = new DatagramSocket();
//指定端口号,表服务端
DatagramSocket socket = new DatagramSocket(port);

              其它的构造方法参考jdk文档:

 主要的普通方法有receive():从此套接字接收数据报包 :

它的参数是一个输出型参数,传入一个空的数据报,那么就会将从网卡读取到的数据填充进去.打个比方,就是你拿一个空的饭盒去食堂窗口打菜,有菜食堂就会往你饭盒打菜.等你从窗口处拿到你的饭盒,你的饭盒就已经有菜了.这时你的饭盒就相当于输出型参数.当然如果窗口菜还没炒好,你就需要等待.所以receive()方法可能会产生阻塞.

                              send():从此套接字发送数据报包:

                              ,close():关闭该数据报套接字.

注:要确定该数据报套接字不会再使用才能关闭该资源.还有其它很多该套接字的方法,大家想了解的话可以去查看jdk文档学习.

          2) DatagramPacket常用构造方法有:

               其它的构造方法参考jdk文档:

               其它的方法主要分为获取和设置:包括(数据报发送或接收的计算机IP地址,端口号),数据缓冲区,发送或接收数据的长度等等,根据我们自己的实际需求去调用.这里我们用到的就是以下方法:

       ● 示例代码

          这里先将示例代码展示,然后再根据代码跟大家分享一下我的思路.

//服务器
public class UdpEchoServer {
    DatagramSocket socket;
    // 1.初始化一个带端口号的DatagramSocket对象(作为服务器)
    public UdpEchoServer(int port) throws SocketException {
        socket = new DatagramSocket(port);
    }

    public void start() throws IOException {
        System.out.println("服务器启动!");
        while (true) {
            // 2.准备一个数据报(空的饭盒),调用receive方法阻塞等待接收客户端的请求数据
            DatagramPacket request = new DatagramPacket(new byte[2023],2023);
            // 5.收到客户端发送的数据报数据并填入步骤2准备的数据报中
            socket.receive(request);

            // 将请求数据转换为字符串
            String req = new String(request.getData(),0,request.getLength());

            // 6.根据请求计算响应(这里由于是回显服务器,所以请求和响应是一样的)
            String response = process(req);

            // 7.将响应数据放入一个数据报中,使用send()方法返回给客户端
            DatagramPacket resp = new DatagramPacket(response.getBytes(),0,response.getBytes().length,request.getSocketAddress());
            socket.send(resp);

            // (最后打印请求和响应数据)
            System.out.printf("[ip:%s port:%s] req:%s res:%s\n",resp.getAddress(),socket.getPort(),req ,response);
        }
    }

    private String process(String request) {
        return request;
    }

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

//客户端
public class UdpEchoClient {
    DatagramSocket socket;
    // 发送请求数据报的服务端ip地址和端口
    public String ip;
    public int port;

    // 1.初始化一个不带端口号的DatagramSocket对象(作为客户端)
    public UdpEchoClient(String ip, int port) throws SocketException {
        socket = new DatagramSocket();
        this.ip = ip;
        this.port = port;
    }

    public void start() throws IOException {
        System.out.println("客户端启动");
        while (true) {
            Scanner s = new Scanner(System.in);
            String temp = s.nextLine();
            // 3.将请求的数据放入一个数据报中,使用send()发送
            DatagramPacket request = new DatagramPacket(temp.getBytes(),temp.getBytes().length,InetAddress.getByName(ip),port);
            socket.send(request);

            // 4.准备一个空的数据报接收响应数据,调用receive()方法阻塞等待服务器返回的响应数据
            DatagramPacket response = new DatagramPacket(new byte[2023],2023);
            // 8.收到响应数据报,将响应数据放入步骤4准备的空数据报中
            socket.receive(response);

            // 将数据报数据转换为字符串
            String res = new String(response.getData(),0,response.getLength());
            // (最后打印请求和响应数据)
            System.out.printf("req:%s res:%s\n", temp,res);
        }
    }

    private String process(String request) {
        return request;
    }

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

       ● 根据示例代码分析服务器和客户端的交互流程(图解)

注: ① 应用层可以通过编程实现UDP全双工通信,在某一时刻既可以发送数据,又可以接收数据.但在网络通信中,UDP本身仍然是半双工的协议.

      ② 这里的socket其实是一个特殊的文件,是网卡这个硬件设备的抽象表示(我们可以通过它间接读取或修改网卡数据).


3. 基于TCP套接字实现的简单客户端 服务器程序

      TCP也是传输层协议之一,它的特点是有连接,可靠传输,面向字节流,全双工(允许数据在两个方向同时传输).

       ● TCP的套接字

           可以分为两类,一个是ServerSocket类,它是服务器套接字,给服务器使用(一定要绑定端口号),另一个是Socket类,也是个套接字,但是客户端和服务器都可以使用它(和DatagramSocket相似,没绑定端口号给客户端使用,绑定端口号给服务器使用).

          1) ServerSocket常用构造方法有:

              其它构造方法:

 

 

           它的常用普通方法:accept() 与客户端建立连接(得到的是一个Socket对象):

                                         close() 关闭套接字(一定要确定该套接字不再使用之后再关闭)

          2) Socket常用构造方法有:

  

              其它构造方法:

         Socket中我们在这里用到的普通方法: 

 

         其它方法大家可以根据需求查看jdk文档使用. 

       ● 示例代码

// 服务器
public class TcpEchoServer {
    ServerSocket socket;

    public TcpEchoServer(int port) throws IOException {
        // 该服务器位于哪个端口
        socket = new ServerSocket(port);
    }

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

        while (true) {
            // 请求连接
            Socket clientSocket = socket.accept();
            // 可连接多个客户端,每个客户端在一个线程中处理自己的逻辑
            Thread thread = new Thread(() -> {
                try {
                    processClient(clientSocket);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });
            thread.start();
        }
    }

    private void processClient(Socket clientSocket) throws IOException {
        System.out.println("客户端上线!");
        // try catch with resource操作会让这里创建的字节流在使用后自动关闭
        try (InputStream inputStream = clientSocket.getInputStream();
             OutputStream outputStream = clientSocket.getOutputStream()) {
            // 使用字符流处理
            Scanner input = new Scanner(inputStream);
            PrintWriter print = new PrintWriter(outputStream);
            while (true) {
                // 读取到字符流末尾
                if (!input.hasNext()) {
                    System.out.println("客户端下线!");
                    break;
                }
                // 获取请求
                String request = input.next();

                // 处理请求并返回响应
                String response = process(request);

                // 写回响应
                print.println(response);
                //刷新缓冲区
                print.flush();
                //打印结果
                System.out.printf("[ip:%s port:%s] request:%s response:%s\n", clientSocket.getInetAddress().toString(), clientSocket.getPort(), request, response);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 关闭资源
            clientSocket.close();
        }
    }

    private String process(String request) {
        return request;
    }

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

// 客户端
public class TcpEchoClient {
    Socket socket;

    public TcpEchoClient(String ip,int port) throws IOException {
        // 连接对应ip地址及端口的服务器
        socket = new Socket(ip,port);
    }

    public void start() throws IOException {
        System.out.println("客户端开启");
        Scanner s = new Scanner(System.in);
        try (InputStream inputStream = socket.getInputStream();
             OutputStream outputStream = socket.getOutputStream()){
            PrintWriter printWriter = new PrintWriter(outputStream);
            Scanner input = new Scanner(inputStream);
            while (true) {
                // 发送请求
                String request = s.next();
                printWriter.println(request);
                // 刷新缓冲区
                printWriter.flush();

                // 得到响应
                String response = input.next();
                // 打印结果
                System.out.printf("request:%s response:%s\n",request,response);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

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

注:我们这里约定请求和响应都是字符串,需要使用字符流来处理.

       ● 根据示例代码分析服务器和客户端的交互流程(图解)

注:这里缓冲区策略和线程池策略类似,将数据先写到缓冲区,等缓冲区满了再统一自动写入网卡.我们这里手动刷新缓冲区是为了将数据直接写入网卡中.


 分享完毕~欢迎大家一起学习讨论~

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

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

相关文章

宁波市天一杯 --- Crypto wp

文章目录 secretrsa secret 题目: p134261118796789547851478407090640074022214132682000430136383795981942884853000826171189906102866323044078348933419038543719361923320694974970600426450755845839235949167391987970330836004768360774676424958554946…

坦克大战进阶--发射子弹

坦克大战进阶–发射子弹 1. 坦克大战0.3 1.1 分析 利用线程基础的知识,把坦克大战再次进阶一下:当我们按下J键,坦克就能够发射一颗子弹。 1.2 思路 当发射一颗子弹后,就相当于启动一个线程Mytank 有子弹的对象,当…

MSP432笔记5——外部中断

所用单片机型号:MSP432P401r 今日继续我的MSP432电赛速通之路。 外部中断是个很有用的配置 STM32几乎每个I/O口都能配置复用为外部中断 但MSP432并不是这样。 我经过查阅数据手册发现支持中断的引脚为: P1^0~ P1^7 P3^0~ P3^7 P5^0~ P5^…

Gateway服务网关入门

Gateway服务网关 Spring Cloud Gateway 是 Spring Cloud 的一个全新项目,该项目是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等响应式编程和事件流技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。…

【网络字节序】

网络字节序 我们已经知道,内存中的多字节数据相对于内存地址有大端和小端之分,磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分。网络数据流同样有大端小端之分,那么如何定义网络数据流的地址呢?发送主机通常将发送…

【C++】21年精通C++之泛型编程和模板初阶知识

❤️前言 大家好!今天和大家一起学习关于C泛型编程和模板初阶的相关知识。 正文 我们之前已经学习了C中非常重要的一个特性——函数重载,函数重载很好地提高了我们代码的可读性。但是对于适配多种参数的某种函数来说,我们如果使用函数重载就…

感知程序从ros切换到cyber_rt框架下,pcl相关问题

1.在ubuntu20.04下,原感知程序需要的是pcl1.8.1,车上其他程序使用的是pcl.1.10.0或者pcl1.10.0,在编译pcl1.10.0时会编译通不过,而pcl1.10.1可以顺利编译通过,安装pcl1.8.1时遇到的问题可能如下,及对应的修…

CTF必看~ PHP反序列化漏洞6:绝妙_wakeup绕过技巧

作者:Eason_LYC 悲观者预言失败,十言九中。 乐观者创造奇迹,一次即可。 一个人的价值,在于他所拥有的。可以不学无术,但不能一无所有! 技术领域:WEB安全、网络攻防 关注WEB安全、网络攻防。我的…

iptables防火墙2

iptables防火墙 一:SNAT原理与应用 SNAT 应用环境:局域网主机共享单个公网IP地址接入Internet(私有不能早Internet中正常路由)SNAT原理:修改数据包的源地址。 SNAT转换前提条件: 1.局域网各主机已正确设…

新星计划 Electron+vue2 桌面应用 2 搭建及运行

基础内容:新星计划 Electronvue2 桌面应用 1 基础_lsswear的博客-CSDN博客 根据使用过的经验和官网的描述,大概可以有四种方式: 自己创建项目(仅使用npm)用Electron脚手架HBuilder编译为web,再用Electron…

MSP432笔记4:时钟与滴答计时器

所用单片机型号:MSP432P401r 今日继续更新我的MSP432电赛速通笔记: 提示: 本节内容相当于讲述delay_ms() 和delay_us() 俩延时函数的由来, 所以不需要花费过多时间斟酌 MSP432单…

论文阅读_音频表示_wav2vec_2.0

论文信息 name_en: wav2vec 2.0: A Framework for Self-Supervised Learning of Speech Representations name_ch: wav2vec 2.0:语音表示自监督学习框架 paper_addr: http://arxiv.org/abs/2006.11477 date_read: 2023-04-27 date_publish: 2020-10-22 tags: [‘深…

C++深度解析:虚函数的使用与避免

C深度解析:虚函数的使用与避免 1. 虚函数的基本概念与原理 (Basic Concepts and Principles of Virtual Functions)1.1 虚函数的定义与作用 (Definition and Role of Virtual Functions)1.2 虚函数的底层实现 (Underlying Implementation of Virtual Functions)1.3 …

【CANN训练营0基础赢满分秘籍】进阶班 Atlas 200I DK 智能小车

1 智能小车三维结构设计 1.1 基本模块 坚固酷炫结构模块运动控制模块超声波传感器模块摄像头视觉模块其他传感器模块 1.2 结构设计基本原则 从零开始设计并搭建智能小车,在满足外观要求的基础上,要满足小车运转过程中的运动干涉率为O,并且…

【CANN训练营0基础赢满分秘籍】进阶班 应用开发深入讲解

1 AIPP AIPP (Artificial Intelligence Pre-Processing)人工智能预处理,在AI Corfe上完成数据预处理。 1.1 静态AIPP 构造AIPP配置文件*.cfg使能静态AIPP,将其配置参数保存在模型文件中。 atc --framework3--soc_versionS[soc_version) --model SHOM…

基于51单片机的电子琴Protues仿真设计

一、设计背景 基于51单片机的电子琴是一款由51单片机控制器、音频模块和硬件阵列组成的数字化乐器。它可以模拟各种乐器的音效,同时也具有许多常规电子琴所没有的高级功能。 首先,这种电子琴是以数字信号处理技术为基础的。通过软件编程,将…

【JUC】Java对象内存布局和对象头

【JUC】Java对象内存布局和对象头 文章目录 【JUC】Java对象内存布局和对象头1. 对象的内存布局1.1 对象头1.1.1 对象标记1.1.2 类元信息/类型指针 1.2 实例数据1.3 对齐填充 2. 测试 1. 对象的内存布局 在 HotSpot 虚拟机里,对象在堆内存中的存储布局可以划分为三…

MSP432学习笔记6:中断优先级管理

所用型号:MSP432P401R 今日继续我的MSP432电赛速通之路。 主要学习的是:中断优先级管理、软件挂起中断、屏蔽中断优先级 目录 MSP432具有8级可编程的中断优先级。 中断优先级管理库函数: 软件挂起中断: 屏蔽中断优先级&#…

微信小程序富文本插件mp-html

使用场景: 偏偏后端传过来的数据又要用到富文本标签,然后找了很多组件,要不就是下载量低,要不就是里面功能太少,只有这款mp-html组件深得我心,里面功能丰富,简单实用,真的绝绝子&…

DMA直接存储器存取

目录 存储器映像 寄存器 DMA框图 DMA基本结构 DMA请求映射 数据宽度与对齐 ​编辑 存储器到存储器 ​编辑 外设与存储器 来源b站江科大stm3入门教程 存储器映像 寄存器 DMA框图 AHB从设备(DMA自身的寄存器)连接在总线矩阵右侧的AHB总线上 所以DMA既…