TCP/IP协议以及UDP(超详细,看这一篇就够了)

news2024/9/21 2:36:42

💓 博客主页:从零开始的-CodeNinja之路

⏩ 收录专栏:TCP/IP协议以及UDP(超详细,看这一篇就够了)

🎉欢迎大家点赞👍评论📝收藏⭐文章

TCP/IP协议以及UDP(超详细,看这一篇就够了

    • 前提概括
        • 接收端和发送端
        • 客户端和服务端
        • 客户端和服务端交流过程
  • 一: TCP协议
      • 1.1:TCP协议的六大特性
      • 1.2:Socket
      • 1.3:ServerSocket
      • 1.4:TCP的实现
        • TCP Echo Server服务器
        • TCP Echo Client客服端
  • 二: UDP协议
      • 2.1:UDP协议的六大特特性
      • 2.2:DatagramSocket
      • 2.3:DatagramPacket
      • 2.4:UDP的实现
        • UDP Echo Server服务器
        • UDP Echo Client客服端

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

前提概括

首先进入本章知识重点之前,我们要先了解清楚计算机在客户端以及服务端扮演的角色以及交流过程.

接收端和发送端

在⼀次网络数据传输时:
发送端:数据的发送方进程,称为发送端。发送端主机即网络通信中的源主机。
接收端:数据的接收方进程,称为接收端。接收端主机即网络通信中的目的主机。
注意:发送端和接收端只是相对的,只是⼀次网络数据传输产生数据流向后的概念。

客户端和服务端

服务端:在常见的网络数据传输场景下,把提供服务的一方进程,称为服务端,可以提供对外服务。
客户端:获取服务的一方进程,称为客户端。

客户端和服务端交流过程

最常见的场景,客户端是指给用户使用的程序,服务端是提供用户服务的程序:

  1. 客户端先发送请求到服务端
  2. 服务端根据请求数据,执行相应的业务处理
  3. 服务端返回响应:发送业务处理结果
  4. 客户端根据响应数据,展示处理结果(展示获取的资源,或提示保存资源的处理结果). 在这里插入图片描述

一: TCP协议

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

1.1:TCP协议的六大特性

  • 有连接
  • 双全工
  • 可靠传输
  • 面向字节流
  • 有接收缓冲区,也有发送缓冲区
  • 大小不限

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

1.2:Socket

Socket 是客户端Socket,或服务端中接收到客户端建立连接(accept方法)的请求后,返回的服
务端Socket。
不管是客户端还是服务端Socket,都是双方建立连接以后,保存的对端信息,及用来与对方收发数据
的。

⽅法签名⽅法说明
Socket(String host,int port)创建⼀个客户端流套接字Socket,并与对应IP的主机上,对应端口的进程建立连接

Socket 方法:

⽅法签名⽅法说明
InetAddress getInetAddress()返回套接字所连接的地址
InputStream getInputStream()返回此套接字的输入流
OutputStream getOutputStream()返回此套接字的输出流

1.3:ServerSocket

ServerSocket 是创建TCP服务端Socket的API。
ServerSocket 构造方法:

⽅法签名⽅法说明
ServerSocket(int port)创建⼀个服务端流套接字Socket,并绑定到指定端口

ServerSocket 方法:

⽅法签名⽅法说明
Socket accept()开始监听指定端口(创建时绑定的端口),有客户端连接后,返回⼀个服务端Socket对象,并基于该Socket建立与客户端的连接,否则阻塞等待
void close()关闭此套接字

1.4:TCP的实现

  1. 客户端和服务端:开发时,经常是基于⼀个主机开启两个进程作为客户端和服务端,但真实的场
    景,⼀般都是不同主机。
  2. 注意目的IP和目的端口号,标识了⼀次数据传输时要发送数据的终点主机和进程
  3. Socket编程我们是使用流套接字和数据报套接字,基于传输层的TCP或UDP协议,但应用层协议,
    也需要考虑,这块我们在后续来说明如何设计应用层协议。
  4. 关于端口被占用的问题
  5. 如果⼀个进程A已经绑定了⼀个端口,再启动⼀个进程B绑定该端口,就会报错,这种情况也叫端口
    被占用。对于java进程来说,端口被占用的常见报错信息如下:
    在这里插入图片描述
TCP Echo Server服务器
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;

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("服务器启动!");
        while (true) {
            Socket clientSocket = serverSocket.accept();
            processConnection(clientSocket);
        }
    }
    // 通过这个⽅法来处理⼀个连接的逻辑.
    private void processConnection(Socket clientSocket) {
        System.out.printf("[%s:%d] 客⼾端上线!\n", clientSocket.getInetAddress().t
// 接下来就可以读取请求, 根据请求计算响应, 返回响应三步⾛了.
// Socket 对象内部包含了两个字节流对象, 可以把这俩字节流对象获取到, 完成后续的读写
        try (InputStream inputStream = clientSocket.getInputStream();
             OutputStream outputStream = clientSocket.getOutputStream()) {
// ⼀次连接中, 可能会涉及到多次请求/响应
            while (true) {
// 1. 读取请求并解析. 为了读取⽅便, 直接使⽤ Scanner.
               Scanner scanner = new Scanner(inputStream);
                if (!scanner.hasNext()) {
// 读取完毕, 客⼾端下线.
                    System.out.printf("[%s:%d] 客⼾端下线!\n", clientSocket.getIne
                    break;
                }
// 这个代码暗含⼀个约定, 客⼾端发过来的请求, 得是⽂本数据, 同时, 还得带有
                String request = scanner.next();
// 2. 根据请求计算响应
                String response = process(request);
// 3. 把响应写回给客⼾端. 把 OutputStream 使⽤ PrinterWriter 包裹⼀下
                PrintWriter writer = new PrintWriter(outputStream);
// 使⽤ PrintWriter 的 println ⽅法, 把响应返回给客⼾端.
// 此处⽤ println, ⽽不是 print 就是为了在结尾加上 \n . ⽅便客⼾端
                writer.println(response);
// 这⾥还需要加⼀个 "刷新缓冲区" 操作.
                writer.flush();
// ⽇志, 打印当前的请求详情.
                System.out.printf("[%s:%d] req: %s, resp: %s\n", clientSocket.ge
                        request, response);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
// 在 finally 中加上 close 操作, 确保当前 socket 被及时关闭!!
            try {
                clientSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    public String process(String request) {
        return request;
    }
    public static void main(String[] args) throws IOException {
        TcpEchoServer server = new TcpEchoServer(9090);
        server.start();
    }
}
TCP Echo Client客服端
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;

public class TcpEchoClient {
    private Socket socket = null;
    // 要和服务器通信, 就需要先知道, 服务器所在的位置.
    public TcpEchoClient(String serverIp, int serverPort) throws IOException {
// 这个 new 操作完成之后, 就完成了 tcp 连接的建⽴.
        socket = new Socket(serverIp, serverPort);
    }
    public void start() {
        System.out.println("客⼾端启动");
        Scanner scannerConsole = new Scanner(System.in);
        try (InputStream inputStream = socket.getInputStream();
             OutputStream outputStream = socket.getOutputStream()) {
            while (true) {
// 1. 从控制台输⼊字符串.
                System.out.print("-> ");
                String request = scannerConsole.next();
// 2. 把请求发送给服务器
                PrintWriter printWriter = new PrintWriter(outputStream);
// 使⽤ println 带上换⾏. 后续服务器读取请求, 就可以使⽤ scanner.n
                printWriter.println(request);
// 不要忘记 flush, 确保数据是真的发送出去了!!
                printWriter.flush();
// 3. 从服务器读取响应.
                Scanner scannerNetwork = new Scanner(inputStream);
                String response = scannerNetwork.next();
// 4. 把响应打印出来
                System.out.println(response);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) throws IOException {
        TcpEchoClient client = new TcpEchoClient("127.0.0.1", 9090);
        client.start();
    }
}

二: UDP协议

传输层UDP协议
UDP,即User Datagram Protocol(用户数据报协议),传输层协议。

2.1:UDP协议的六大特特性

  • 无连接
  • 双全工
  • 不可靠传输
  • 面向数据报
  • 有接收缓冲区,无发送缓冲区
  • 大小受限:⼀次最多传输64k

对于数据报来说,可以简单的理解为,传输数据是⼀块⼀块的,发送⼀块数据假如100个字节,必须⼀
次发送,接收也必须⼀次接收100个字节,而不能分100次,每次接收1个字节。

2.2:DatagramSocket

DatagramSocket 是UDP Socket,用于发送和接收UDP数据报。

DatagramSocket 构造方法

⽅法签名⽅法说明
DatagramSocket()创建⼀个UDP数据报套接字的Socket,绑定到本机任意⼀个随机端⼝(⼀般⽤于客⼾端)
DatagramSocket(int port)创建⼀个UDP数据报套接字的Socket,绑定到本机指定的端⼝(⼀般⽤于服务端)

DatagramSocket 方法

⽅法签名⽅法说明
void receive(DatagramPacket p)从此套接字接收数据报(如果没有接收到数据报,该⽅法会阻塞等待)
void send(DatagramPacket p)从此套接字发送数据报包(不会阻塞等待,直接发送)
void close()关闭此数据报套接字

2.3:DatagramPacket

DatagramPacket是UDP Socket发送和接收的数据报。
DatagramPacket 构造方法:

⽅法签名⽅法说明
DatagramPacket(byte[]buf,int length)构造⼀个DatagramPacket以⽤来接收数据报,接收的数据保存在字节数组(第⼀个参数buf)中,接收指定⻓度(第⼆个参数length)
DatagramPacket(byte[] buf,int offset,int length,SocketAddress address)构造⼀个DatagramPacket以用来发送数据报,发送的数据为字节数组(第⼀个参数buf)中,从0到指定长度(第⼆个参数length)。address指定⽬的主机的IP和端⼝号

DatagramPacket 方法:

⽅法签名⽅法说明
InetAddress getAddress()从接收的数据报中,获取发送端主机IP地址;或从发送的数据报中,获取接收端主机IP地址
int getPort()从接收的数据报中,获取发送端主机的端口号;或从发送的数据报中,获取接收端主机端⼝号
byte[] getData()获取数据报中的数据构造UDP发送的数据报时

2.4:UDP的实现

对于UDP协议来说,具有无连接,面向数据报的特征,即每次都是没有建立连接,并且⼀次发送全部
数据报,⼀次接收全部的数据报。
java中使用UDP协议通信,主要基于 DatagramSocket 类来创建数据报套接字,并使用
DatagramPacket 作为发送或接收的UDP数据报。对于⼀次发送及接收UDP数据报的流程如下:
在这里插入图片描述

UDP Echo Server服务器
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

public class UdpEchoServer {
    private DatagramSocket socket = null;
    // 参数是服务器要绑定的端⼝
    public UdpEchoServer(int port) throws SocketException {
        socket = new DatagramSocket(port);
    }
    // 使⽤这个⽅法启动服务器.
    public void start() throws IOException {
        System.out.println("服务器启动!");
        while (true) {
// 反复的, ⻓期的执⾏针对客⼾端请求处理的逻辑.
// ⼀个服务器, 运⾏过程中, 要做的事情, 主要是三个核⼼环节.
// 1. 读取请求, 并解析
            DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 40
                    socket.receive(requestPacket);
// 这样的转字符串的前提是, 后续客⼾端发的数据就是⼀个⽂本的字符串.
            String request = new String(requestPacket.getData(), 0, requestPacke
// 2. 根据请求, 计算出响应
                    String response = process(request);
// 3. 把响应写回给客⼾端
// 此时需要告知⽹卡, 要发的内容是啥, 要发给谁.
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes
                    requestPacket.getSocketAddress());
            socket.send(responsePacket);
// 记录⽇志, ⽅便观察程序执⾏效果.
          System.out.printf("[%s:%d] req: %s, resp: %s\n", requestPacket.getAd
                    request, response);
        }
    }
    // 根据请求计算响应. 由于是回显程序, 响应内容和请求完全⼀样.
    public String process(String request) {
        return request;
    }
    public static void main(String[] args) throws IOException {
        UdpEchoServer server = new UdpEchoServer(9090);
        server.start();
    }
}
UDP Echo Client客服端
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.Scanner;

public class UdpEchoClient {
    private DatagramSocket socket = null;
    private String serverIp;
    private int serverPort;
    // 服务器的 ip 和 服务器的端⼝.
    public UdpEchoClient(String ip, int port) throws SocketException {
        serverIp = ip;
        serverPort = port;
// 这个 new 操作, 就不再指定端⼝了. 让系统⾃动分配⼀个空闲端⼝.
        socket = new DatagramSocket();
    }
    // 让这个客⼾端反复的从控制台读取⽤⼾输⼊的内容. 把这个内容构造成 UDP 请求, 发给服务器
// 最终再显⽰在客⼾端的屏幕上.
    public void start() throws IOException {
        Scanner scanner = new Scanner(System.in);
        System.out.println("客⼾端启动!");
        while (true) {
// 1. 从控制台读取⽤⼾输⼊的内容
            System.out.print("-> "); // 命令提⽰符, 提⽰⽤⼾要输⼊字符串.
            String request = scanner.next();
// 2. 构造请求对象, 并发给服务器.
            DatagramPacket requestPacket = new DatagramPacket(request.getBytes()
                    InetAddress.getByName(serverIp), serverPort);
           socket.send(requestPacket);
// 3. 读取服务器的响应, 并解析出响应内容.
            DatagramPacket responsePacket = new DatagramPacket(new byte[4096], 4
                    socket.receive(responsePacket);
            String response = new String(responsePacket.getData(), 0, responsePa
// 4. 显⽰到屏幕上.
                    System.out.println(response);
        }
    }
    public static void main(String[] args) throws IOException {
        UdpEchoClient client = new UdpEchoClient("127.0.0.1", 9090);
// UdpEchoClient client = new UdpEchoClient("42.192.83.143", 9090);
        client.start();
    }
}

在这里插入图片描述
如果觉得文章不错,期待你的一键三连哦,你个鼓励是我创作的动力之源,让我们一起加油,顶峰相见!!!💓 💓 💓

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

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

相关文章

[Java][算法 双指针]Day 02---LeetCode 热题 100---04~07

LeetCode 热题 100---04~07 第一题:移动零 思路 找到每一个为0的元素 然后移到数组的最后 但是需要注意的是 要在给定的数组原地进行修改 并且其他非零元素的相对顺序不能改变 我们采用双指针法 定义两个指针i和j i和j一开始分别都在0索引位置 然后判断j所…

Node.js之npm单独与批量升级依赖包的方式

Node.js之npm单独与批量升级依赖包的方式 文章目录 Node.js之npm单独与批量升级依赖包的方式npm查看与升级依赖包1. 单独安装或升级最新版本2. 查看依赖但不升级1. npm outdated2. npm update 3. 批量升级新版本4. npm-check-updates1. 全局安装2. ncu查看可升级的版本3. 升级依…

排序算法---堆排序

原创不易,转载请注明出处。欢迎点赞收藏~ 堆排序(Heap Sort)是一种基于二叉堆数据结构的排序算法。它将待排序的元素构建成一个最大堆(或最小堆),然后逐步将堆顶元素与堆的最后一个元素交换位置&#xff0c…

新年新展望

去年其实是收获颇丰的一年,除了工作中各项工作都得到了很大的推进,个人生活中也有很多变化,其中还拿到了功能安全工程师的证书,以及功能安全经理的证书。 展望一下2024年准备输出的内容,一个是对ISO26262的解读&#x…

手把手教你开发Python桌面应用-PyQt6图书管理系统-图书添加模块-图书类别ComboBox下拉框初始化数据实现

锋哥原创的PyQt6图书管理系统视频教程: PyQt6图书管理系统视频教程 Python桌面开发 Python入门级项目实战 (无废话版) 火爆连载更新中~_哔哩哔哩_bilibiliPyQt6图书管理系统视频教程 Python桌面开发 Python入门级项目实战 (无废话版) 火爆连载更新中~共计24条视频&…

Java玩转《啊哈算法》纸牌游戏之小猫钓鱼

缘起性空 文章目录 缘起代码地址纸牌游戏分析代码演示优化 缘起 各位小伙伴们好呀,还有几天就要过年了,祝大家新年快乐,万事胜意! 本人最近看了下《啊哈算法》,确实阔以。 但稍显遗憾的是,书籍示例代码是…

Web后端开发:登录认证案例

登录功能 需求分析 在登录界面中,输入用户的用户名以及密码,然后点击 “登录” ,服务端判断用户输入的用户名和密码是否都正确。如果正确,则返回成功结果,前端跳转至系统首页面;否则报错,停留在…

火星符号运算 - 华为OD统一考试

OD统一考试(C卷) 分值: 100分 题解: Java / Python / C 题目描述 已知火星人使用的运算符号为 #和$ 其与地球人的等价公式如下 x#y2*x3*y4 x$y3*xy2x y是无符号整数。地球人公式按照c语言规则进行计算。火星人公式中&#xff0…

Qt PCL学习(三):点云滤波

注意事项 版本一览:Qt 5.15.2 PCL 1.12.1 VTK 9.1.0前置内容:Qt PCL学习(一):环境搭建、Qt PCL学习(二):点云读取与保存 0. 效果演示 1. pcl_open_save.pro QT core guigr…

【RT-DETR改进涨点】更加聚焦的边界框损失Focaler-IoU、InnerFocalerIoU(二次创新)

一、本文介绍 本文给大家带来的改进机制是更加聚焦的边界框损失Focaler-IoU已经我进行二次创新的InnerFocalerIoU同时本文的内容支持现阶段的百分之九十以上的IoU,比如Focaler-IoU、Focaler-ShapeIoU、Inner-Focaler-ShapeIoU包含非常全的损失函数,边界框的损失函数只看这一…

MySQL-视图/储存过程/触发器

一、视图 1.介绍 视图(View)是一种虚拟存在的表。视图中的数据并在数据库中实际存在,行和列数据来自定义视图的查询中使用的表,并且是在使用视图时动态生成的。 通俗的讲,视图只保存了查询的SQL逻辑,不保…

幻兽帕鲁服务器搭建,阿里云和腾讯云随便选,看看哪个简单?

幻兽帕鲁官方服务器不稳定?自己搭建幻兽帕鲁服务器,低延迟、稳定不卡,目前阿里云和腾讯云均推出幻兽帕鲁专用服务器,腾讯云直接提供幻兽帕鲁镜像系统,阿里云通过计算巢服务,均可以一键部署,鼠标…

Redis核心技术与实战【学习笔记】 - 23.Redis 主从切换故障,有哪些坑

前言 Redis 的主从同步机制不仅可以让从库服务更多的读请求,分担主库的压力,而且还能在主库发生故障时,进行主从库切换,提供高可靠服务。 不过,在实际使用主从机制时会踩到一些“坑”:主从数据不一致、读…

数据结构第十四天(树的存储/双亲表示法)

目录 前言 概述 接口: 源码: 测试函数: 运行结果: 往期精彩内容 前言 孩子,一定要记得你的父母啊!!! 哈哈,今天开始学习树结构中的双亲表示法,让孩…

YOLOv5独家原创改进:大核卷积涨点系列| Shift-ConvNets,稀疏/移位操作让小卷积核也能达到大卷积核效果 | 2024年最新论文

💡💡💡本文独家改进:大的卷积核设计成为使卷积神经网络(CNNs)再次强大的理想解决方案,Shift-ConvNets稀疏/移位操作让小卷积核也能达到大卷积核效果,创新十足实现涨点,助力YOLOv8 💡💡💡在多个私有数据集和公开数据集VisDrone2019、PASCAL VOC实现涨点 收录…

VSCode如何让先前打开的文件不被自动关闭,一直保持在标签栏里(关闭预览模式)

第一次接触VSCode-Huawei IDE编辑器,每次打开一个新的代码文件,旧的代码文件都会被自动关闭(现在才知道是因为文件默认是以预览模式打开展示的)。 那么如何才能让先前打开的文件一直保持在标签栏里呢? 我们需要去设置…

Mac电脑如何通过终端隐藏应用程序?

在我们使用Mac电脑的时候难免会遇到想要不想看到某个应用程序又不想卸载它们。值得庆幸的是,macOS具有一些强大的文件管理功能,允许用户轻松隐藏(以及稍后显示)文件甚至应用程序。 那么,Mac电脑如何通过终端隐藏应用程…

Composition Local

1.显示传参 package com.jmj.jetpackcomposecompositionlocalimport org.junit.Testimport org.junit.Assert.*/*** 显示传参*/ class ExplicitText {private fun Layout(){var color:String "黑色";//参数需要通过层层传递,比较繁琐Text(color)Grid(c…

idea: 无法创建Java Class文件(SpringBoot)已解决

第一:点击file-->project Sructure... 第二步:点击Moudules 选择自己需要创建java的文件夹(我这里选择的是main)右键点击Sources,然后点击OK即可 然后就可以创建java类了

EasyRecovery免费版2024电脑数据恢复利器

在数字化时代,我们的生活和工作都离不开电脑,电脑硬盘中的数据却时常面临丢失的风险,无论是因为误删除、格式化、病毒感染还是硬件故障,都可能让我们付出沉重的代价,在这种情况下,一款强大的数据恢复软件就…