网络编程(TCP、UDP)

文章目录

  • 一、概念
    • 1.1 什么是网络编程
    • 1.2 网络编程中的基本知识
  • 二、Socket套接字
    • 2.1 概念及分类
    • 2.2 TCP VS UDP
    • 2.3 通信模型
    • 2.4 接口方法
      • UDP数据报套接字编程
      • TCP流套接字编程
  • 三、代码示例
    • 3.1 注意点
    • 3.2 回显服务器
      • 基于UDP
      • 基于TCP

一、概念

首先介绍了什么是网络编程,随后介绍了接收端发送端、请求响应等基本知识

1.1 什么是网络编程

网络编程
(1)网络上的主机,通过不同的进程,以编程的方式实现网络通信(或称为网络数据传输)。(2)在程序员层面上,即要写一个应用程序通过调用传输层提供的API实现网络通信

1.2 网络编程中的基本知识

  1. 发送端和接收端:字面意思,发送端是发送数据的,接收端是接收数据的,注意这个概念只是相对的,主要看数据是从哪发到哪。

  2. 请求和响应
    一般来说,获取一个网络资源,涉及到两次网络数据传输:

    • 第一次:请求数据的发送
    • 第二次:响应数据的发送
  3. 客户端和服务器
    服务端:在常见的网络数据传输场景下,给用户提供服务的一方进程
    客户端:获取服务的一方进程,是指给用户使用的程序

  4. 常见的客户端服务端模型

    • 客户端先发送请求到服务端
    • 服务端根据请求数据,执行相应的业务处理
    • 服务端返回响应:发送业务处理结果
    • 客户端根据响应数据,展示处理结果(展示获取的资源,或提示保存资源的处理结果)

二、Socket套接字

网络编程是基于Socket开发的,传输层上主要涉及TCP、UDP这两种协议,而他俩给出的API都不同,本段主要介绍概念,包括分类和区别。

2.1 概念及分类

(1)Socket套接字,是由系统提供用于网络通信的技术,是基于TCP/IP协议的网络通信的基本操作单元。基于Socket套接字的网络程序开发就是网络编程。

(2)Socket套接字主要针对传输层协议划分为如下三类:

  1. 流套接字:传输层TCP协议(传输控制协议)
  2. 数据报套接字:传输层UDP协议(用户数据报协议)
  3. 原始套接字:原始套接字用于自定义传输层协议,用于读写内核没有处理的IP协议数据

2.2 TCP VS UDP

TCP:有连接;可靠传输;面向字节流;有接收缓冲区和发送缓冲区;大小不限;全双工
UDP:无连接;不可靠传输;面向数据报;有接收缓冲区,无发送缓冲区;大小受限,一次最多传输64k

  1. 连接:客户端和服务器之间是否使用内存保存对端信息,互相承认,连接就建立了。
  2. 传输
    • 可靠传输:A尽可能把消息传过去,传输失败与否都能感知到,会降低传输效率。不可靠传输传输效率更高
    • 注意:这并不意味着,TCP比UDP更安全,因为“网络安全”指的是传输的数据是否容易被黑客截获,以及如果被截获后是否会泄漏一些重要信息
  3. 字节流VS数据报
    • 面向字节流:TCP流式操作
    • 面向数据报:读写的基本单位是一个UDP数据报,且一次只能传输一个
  4. 全双工VS半双工
    • 全双工:一个通道,可以双向通信。网线就是全双工的
    • 半双工:一个通道,只能单向通信

2.3 通信模型

数据报套接字模型

在这里插入图片描述
流套接字模型
在这里插入图片描述

2.4 接口方法

UDP数据报套接字编程

UDP提供的API主要通过两个类来实现,一个是DatagramSocket,一个是DatagramPacket。

DatagramSocket 方法:代表一个Socket对象,即一个网卡的文件
DatagramPacket 方法:代表一个UDP数据报

TCP流套接字编程

因为TCP是字节流式传输,所以不需要一个专门的类来表示TCP数据报

ServerSocket 方法:
(1)给服务器使用的
(2)负责拉客,在外场广撒网。

Socket 方法:
(1)服务端和客户端都能用
(2)像是一个耳麦,可以直接与对方进行通信,负责在内场细致服务。

三、代码示例

3.1 注意点

  1. 服务器使用的端口需要手动指定,客户端的则需要系统自动分配
    • 因为服务器需要能被客户端百分百找到,万一服务器随便改变端口,那客户端按照以往的连接是无法连接到服务器的。
    • 客户端则没有这个需求,且客户端一旦指定端口号很可能回合其他程序冲突,系统自动分配的端口号一定是空闲的。
  2. flush() ------ 手动刷新
    • IO操作是比较有开销的,相比于访问内存,进行IO次数越多,程序的速度就越慢。为了解决这个问题,我们会使用一块内存作为缓冲区,写数据的时候,先写到缓冲区里。攒了一波数据之后,统一进行IO。
    • printWriter 内置了缓冲区
    • flush()可以实现手动刷新,可以确保这里的数据是真的通过网卡发出去了,而不是残留在内存缓冲区中的
    • 加上flush()可以让代码更稳妥,但是不加不一定会出错,因为缓冲区内置了一定的刷新策略
  3. 关于 close()
    close() 主要是删除文件描述符表中的记录。当然也并不是每个流对象都有文件描述符表,只有那种调用了操作系统提供的open()方法,涉及到了用户态和用户态。

3.2 回显服务器

基于UDP

(1)InetAddress 表示的是IP地址
(2)客户端要设置要传给的服务器的IP地址和端口号是多少,即示例中的 serverIp 和 serverPort 。服务器则是要手动指定一个端口号。
(3)难点在于 DatagramPacket 的构造,涉及了String 和 DatagramPacket 之间的转换。
(4)数据包的接收和发送,依靠 DatagramSocket 的 send 和 receive 方法。

//客户端
public class UdpEchoClient {
    private DatagramSocket socket = null;
    private String serverIp;
    private int serverPort;

    public UdpEchoClient(String ip, int port) throws SocketException {
        serverIp = ip;
        serverPort = port;
        socket = new DatagramSocket();
    }

    public void start() throws IOException {
        //获取用户输入的内容
        Scanner scanner = new Scanner(System.in);
        System.out.println("客户端启动!");

        //给服务器发送响应
        while (true){
            System.out.print("-> ");
            String request = scanner.next();
			
			//不能用request.length(),因为这是指字符长度,我们需要的是字节长度
			//除非是纯英文且由utf-8编码,那么两者会相同。Socket是按照字节来处理的。
            DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.getBytes().length,
                    InetAddress.getByName(serverIp), serverPort);
            socket.send(requestPacket);

            //接收响应
            DatagramPacket responsePacket = new DatagramPacket(new byte[4096], 4096);
            socket.receive(responsePacket);
            String response = new String(responsePacket.getData(), 0, responsePacket.getLength());

            System.out.println(response);
        }
    }

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

//服务器
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){
            DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096);
            socket.receive(requestPacket);

            String request = new String(requestPacket.getData(), 0, requestPacket.getLength());
            String response = process(request);

            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(), response.getBytes().length,
                    requestPacket.getSocketAddress());
            socket.send(responsePacket);

            System.out.printf("[%s:%d req:%s, resp:%s\n]", requestPacket.getAddress().toString(),
                    requestPacket.getPort(), request, response);

        }
    }

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

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

基于TCP

  1. 服务器对客户是多对多的关系,一个服务器内核会存许多客户端的连接(建立连接的过程,系统内核会自己完成)。连接就像是一个个代办事项,等待应用程序一个个完成。
  2. serverSocket.accept():将在内核中已经建立好的连接取出来给应用程序
  3. 原理是一个【生产者消费者模型】
    在这里插入图片描述
  4. TCP中的长短连接
    • 短连接:每次接收到数据并返回响应后,都关闭连接,即只能一次收发数据
    • 长连接:不关闭连接,一直保持连接状态,双方不停的收发数据
public class TcpServer {
    private ServerSocket socket = null;

    private ExecutorService service = Executors.newCachedThreadPool();

    public TcpServer(int port) throws IOException {
        socket = new ServerSocket(9090);
    }

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

        while (true){
            Socket clientSocket = socket.accept();
			
			//为了能够接收多个客户端,这里使用了线程池
			//当客户端进一步增加,线程数目也会增加,系统的负担也就越来越重,响应速度也就越来越慢
			//为了解决这个问题,就需要开源(引入更多的硬件资源)节流(减少每种硬件资源占用的资源)
            service.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        processConnection(clientSocket);
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            });

        }
    }

    public void processConnection(Socket clientSocket) throws IOException {
        try(InputStream inputStream = clientSocket.getInputStream();
            OutputStream outputStream = clientSocket.getOutputStream()){
            while(true){
                Scanner scannerClient = new Scanner(inputStream);
                String request = scannerClient.next();

                String response = process(request);

                PrintWriter writer = new PrintWriter(outputStream);
                writer.println(response);
                writer.flush();

                System.out.printf("[%s:%d] req:%s, resp:%s\n", clientSocket.getInetAddress().toString(),
                        clientSocket.getPort(), request, response);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }finally {
            clientSocket.close();
        }
    }

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

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

public class TcpClient {
    private Socket socket = null;

    public TcpClient(String serverIp, int serverPort) throws IOException {
        socket = new Socket(serverIp, serverPort);
    }

    public void start(){
        System.out.println("客户端启动");
        Scanner scanner = new Scanner(System.in);

        try(InputStream inputStream = socket.getInputStream();
            OutputStream outputStream = socket.getOutputStream()){
            while (true){
                System.out.print("---->");
                String request = scanner.next();
                PrintWriter writer = new PrintWriter(outputStream);
                writer.println(request);
                writer.flush();

                Scanner scammerConsole = new Scanner(inputStream);
                String response = scammerConsole.next();
                System.out.println(response);

            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【C语言】_文件内容操作:顺序读写

目录 常用函数 1. 字符输入、输出函数 2. 文本行输入、输出函数 3. 格式化输入、输出函数 4. 二进制输入、输出函数 常用函数 功能函数名适用于字符输入函数fgetc所有输入流字符输出函数fputc所有输出流文本行输入函数fgets所有输入流文本行输出函数fputs所有输出流格式化…

材料物理 笔记-4

原内容请参考哈尔滨工业大学何飞教授:https://www.bilibili.com/video/BV18b4y1Y7wd/?p12&spm_id_frompageDriver&vd_source61654d4a6e8d7941436149dd99026962 或《材料物理性能及其在材料研究中的应用》(哈尔滨工业大学出版社) 离…

基于springboot+vue+Mysql的招生管理系统

开发语言:Java框架:springbootJDK版本:JDK1.8服务器:tomcat7数据库:mysql 5.7(一定要5.7版本)数据库工具:Navicat11开发软件:eclipse/myeclipse/ideaMaven包:…

RAID 0阵列扩容后的磁盘扩展操作

正文共:777 字 21 图,预估阅读时间:1 分钟 上次操作RAID 0的扩容(如何操作RAID 0阵列的扩容?),因为耗时太长进度没有变化,就没再管它了。后来发现,右上角有一个“重新扫描…

Prometheus+grafana环境搭建方法及流程两种方式(docker和源码包)(一)

1.选型对比 最近项目上有对项目服务及中间件的监控需求,要做实现方案调研,总结一下自己的成果,目前业界主流可选的方案有: 国外开源: Prometheus:Prometheus - Monitoring system & time series dat…

C++——模板初阶

泛型编程 C语言中交换两个变量数据的内容一般是这样实现的 #include<iostream>using namespace std;void swap(int* x, int* y) {int tmp *x;*x *y;*y tmp; } int main() {int x 5;int y 7;swap(&x,&y);cout << "x" << x << …

基于栈结构的非递归二叉树结点关键字输出算法

基于栈结构的非递归二叉树结点关键字输出算法 一、引言二、二叉树基本概念三、非递归遍历算法基础四、算法设计五、算法实现六、C代码示例七、算法分析八、优化与讨论 一、引言 在计算机科学中&#xff0c;二叉树是一种重要的数据结构&#xff0c;它广泛应用于各种算法和数据结…

基于深度学习的条形码二维码检测系统(网页版+YOLOv8/v7/v6/v5代码+训练数据集)

摘要&#xff1a;本文深入研究了基于YOLOv8/v7/v6/v5的条形码二维码检测系统。核心采用YOLOv8并整合了YOLOv7、YOLOv6、YOLOv5算法&#xff0c;进行性能指标对比&#xff1b;详述了国内外研究现状、数据集处理、算法原理、模型构建与训练代码&#xff0c;及基于Streamlit的交互…

前端优化gzip

gzip是GNUzip的缩写&#xff0c;是一种文件的压缩格式&#xff08;也可以说是若干种文件压缩程序&#xff09;&#xff0c;类似的压缩格式还有compress&#xff08;webpack&#xff09;&#xff0c;deflate等 主要用于组件的压缩 压缩的话主要分为两种&#xff0c; 服务器在…

记事本打开时总是会自动打开之前打开过的文件

记事本打开文件总是会自动打开之前打开过的文件_win11记事本关闭之后打开上一个还在-CSDN博客 感谢该博主&#xff0c;我一直以为是自己电脑的问题&#xff0c;不知道为什么要这么更新&#xff0c;影响我的很多文本内容消失。

我的领导马斯克:痛恨开会,不要非技术中层,推崇裁员

ChatGPT狂飙160天&#xff0c;世界已经不是之前的样子。 新建了免费的人工智能中文站https://ai.weoknow.com 新建了收费的人工智能中文站https://ai.hzytsoft.cn/ 更多资源欢迎关注 马斯克称得上是个“魔鬼老板”这事儿&#xff0c;已经出了名了。 现在&#xff0c;他的老部…

【面试八股总结】进程(一)

参考资料 &#xff1a;小林Coding、阿秀、代码随想录 一、什么是进程&#xff1f; 1. 基本概念 进程是具有独立功能的程序在一个数据集合上运行的过程&#xff0c;是系统进行资源分配和调度的一个独立单位。 2. 进程控制块 系统通过进程控制块PCB描述进程的进本情况…

leetcode代码记录(打家劫舍 II

目录 1. 题目&#xff1a;2. 我的代码&#xff1a;小结&#xff1a; 1. 题目&#xff1a; 一个专业的小偷&#xff0c;计划偷窃一个环形街道上沿街的房屋&#xff0c;每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 &#xff0c;这意味着第一个房屋和最后一个房屋是…

基于小程序实现的校园二手物品交易系统

作者主页&#xff1a;Java码库 主营内容&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app等设计与开发。 收藏点赞不迷路 关注作者有好处 文末获取源码 技术选型 【后端】&#xff1a;Java 【框架】&#xff1a;spring…

openlayers 入门教程(九):overlay 篇

还是大剑师兰特&#xff1a;曾是美国某知名大学计算机专业研究生&#xff0c;现为航空航海领域高级前端工程师&#xff1b;CSDN知名博主&#xff0c;GIS领域优质创作者&#xff0c;深耕openlayers、leaflet、mapbox、cesium&#xff0c;canvas&#xff0c;webgl&#xff0c;ech…

vulhub中Apache Solr RemoteStreaming 文件读取与SSRF漏洞复现

Apache Solr 是一个开源的搜索服务器。在Apache Solr未开启认证的情况下&#xff0c;攻击者可直接构造特定请求开启特定配置&#xff0c;并最终造成SSRF或任意文件读取。 访问http://your-ip:8983即可查看Apache Solr后台 1.访问http://your-ip:8983/solr/admin/cores?indexI…

Windows10安装CloudCompare(图文安装)

CloudCompare是一个3D点云&#xff08;和三角网格&#xff09;处理软件。它最初被设计用于在两个密集的3D点云&#xff08;例如用激光扫描仪获取的点云&#xff09;之间或点云和三角形网格之间进行比较。它依赖于专用于此任务的特定八叉树结构。 之后&#xff0c;它已经扩展到一…

使用 CloudDM 操作 PostgrgSQL 数据库

CloudDM 简介 CloudDM 是 ClouGence 公司推出的一款一站式数据库管理工具&#xff0c;使用它可以方便地访问和管理 MySQL、Oracle、PostgreSQL、阿里云 RDS、Greenplum、TiDB、Redis、StarRocks、Doris、SelectDB、SQL SERVER、ClickHouse、OceanBase 、PolarDB-X 、IBM Db2 等…

LearnOpenGL_part1

创建窗口 - LearnOpenGL CN (learnopengl-cn.github.io) 最原始的黑框框&#xff1a; #include <glad/glad.h> #include <GLFW/glfw3.h> #include <iostream> int main() {glfwInit();//初始化GLFWglfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);//配置G…

【JavaScript 漫游】【052】Proxy

文章简介 本篇文章为【JavaScript 漫游】专栏的第 052 篇文章&#xff0c;记录了 ES6 规范中 Proxy 的知识点。 概述 Proxy 用于修改某些操作的默认行为&#xff0c;等同于在语言层面做出修改&#xff0c;所以属于一种“元编程”&#xff08;meta programming&#xff09;&a…