初识网络编程

简介: 本文介绍了网络编程的重要概念,包括IP地址、端口号和协议。IP地址是设备在网络中的唯一标识,IPv4已用尽,IPv6提供了更多地址。端口号用于区分设备上的不同应用程序,取值范围为0~65535。协议定义了网络传输规则,常分为TCP/IP五层模型和OSI七层模型。文章还讨论了TCP与UDP的区别,并提供了UDP协议的简单示例。

1. 网络编程重要概念

1.1. IP

IP:设备在网络中的地址,是唯一的标识

IPv4是目前的主流方案,最多只能有2^32个IP,目前已经使用完了,为了解决这个问题而出现的IPv6最多有2^128个IP,

特殊IP地址:

127.0.0.1,也可以是localhost:是回送地址也称本地回环地址,也称本机IP,永远只会寻找当前所在本机

1.2. 端口号

端口号:应用程序在设备中唯一的标识,一个端口号只能被一个应用程序使用

由两个字节表示的整数,取值范围:0~65535,其中0~1023之间的端口号用于一些知名的网络服务或者应用

1.3. 协议

协议是指数据在网络传输中的规则,由于网络通信是非常复杂的事,如果使用一个协议来约定所有的网络通信细节,就会导致这个协议非常庞大且复杂,所以把这个大的协议拆分成多个小的协议,让每一个小的协议专注于解决一个问题,再让这些协议之间相互配合。协议分层就是把功能定位类似的协议,放到同一层中,并且约定好层与层之间的交互关系,上层协议调用下层协议,下层协议给上层提供服务

协议分层的优点:

  1. 降低了使用成本,使用某个协议的时候不必关注其他协议的实现细节
  2. 降低整个体系的耦合性,灵活的变更某个层次的协议

1.3.1. TCP/IP 五层协议模型

应用层:是最接近用户的一层,为用户提供各种网络应用服务

传输层:提供端到端的通信服务,确保数据可靠、有序地从源端传输到目的端,只关注网络通信中的“起点和终点”

网络层:主要功能是进行寻址和路由选择

数据链路层:针对上述规划好的路径进行具体的实施

物理层:描述的是硬件设备需要满足的条件

上述过程就相当于公司中董事长(传输层)指定公司的目标,高管(网络层)对目标进行规划,基层员工(数据链路层)按照规划具体实施,办公设备(物理层)

1.3.2. OSI 七层网络模型

相较于 TCP/IP 五层协议多出了表示层和会话层,TCP/IP 五层协议是把表示层和会话层和应用层融合到一起了

网络设备所在分层:

  1. 对于一台主机,它的操作系统内核实现了从传输层到物理层的内容,也就是 TCP/IP 五层协议模型的下四层
  2. 对于一台路由器,它实现了从网络层到物理层,也就是 TCP/IP 五层协议模型的下三层
  3. 对于一台交换机,它实现了从数据链路层到物理层,也就是 TCP/IP 五层协议模型的下两层
  4. 对于集线器,它实现了物理层

2. 数据在网络通信过程中的整体流程

假如需要通过 QQ 来发送 Hello 给另一个人,在发送方这里需要经过:

  1. 用户输入 Hello 点击发送,程序会把发送的内容读取到,构造成一个“应用层数据包”,应用层的网络协议就描述了这个数据包的构造,此处的协议往往是程序员自己制定的
  2. 程序调用操作系统的 api(传输层给应用层提供的 api),把上述组织好的数据包作为参数传入进来,此时传输层就会把上述的应用层数据再进一步封装(这里的封装类似于字符串拼接,因为发送的数据都是二进制的字符串)成一个传输层数据包,传输层用到的主要协议 TCP/ UDP 协议
  3. 传输层构造好数据之后,就会继续调用网络层给传输层提供的 api ,把数据继续传入网络层,网络层也有许多协议,主要的就是 IPv4 协议,IP协议就会把拿到的传输层数据包构造成网络层数据包
  4. 网络层继续调用数据链路层的 api 把数据交给数据链路层处理,数据链路层常见的协议(以太网)在 IP 数据包的基础上再一步进行包装
  5. 上述得到的数据需要进一步交给物理层(硬件设备),针对以上二进制文件进行真正的传输工作,把二进制序列转化为光电信号传输

接收方视角:

  1. 接收方物理层接收到光电信号还原成二进制序列
  2. 物理层转回来的数据交给数据链路层,以太网对数据包进行解析,拿出这里的报头和载荷,根据报头信息决定是转发,丢弃还是保留(向上进行解析)然后继续向上传输网络层
  3. 网络层拿到数据之后,通过 IP 协议对数据包进行解析,同样的根据报头的信息来判断保留
  4. 传输层这里根据 UDP/ TCP 协议也是执行上述操作,进一步传输给应用层
  5. 到应用层这里就是针对上面的数据进行反序列化操作

传输层只关注起点和终点,中间过程的操作可能存在很多的交换机和路由器来完成数据转发的过程

交换机:封装分用到数据链用层,就可以决定数据是转发还是丢弃了(不再分用)(二层转发)

路由器:封装分用到网络层(三层转发)

3. 面试题

TCP 和 UDP 的区别:

  1. 连接方式:TCP 是有链接的协议(通信双方保存了通信对端的信息),UDP 是无连接的协议(没有保存)
  2. 可靠性:TCP 提供可靠的数据传输,通过确认机制,超时重传等机制来确保数据的完整性和准确性,如果说发送方发送的数据没有被接收方正确接收,发送方就会重新发送数据。UDP 则不提供可靠的数据传输,不会关心发送的数据是否被正确接收
  3. 传输效率:TCP 相对与 UDP 来说效率较低
  4. 传输的大小:TCP 传输是面向字节流的,UDP 传输是面向数据报的,传输的单位就不是字节了,一次发送 / 接收完整的数据报
  5. TCP 支持全双工(一个通信链路可以发送数据,也可以接收数据),UDP 支持半双工(一个通信链路只能发送/接收)

4. UDP 协议的简单示例

回显服务器:客户端发送什么请求,服务器就返回什么相应(不包含任何逻辑处理)

对于服务器端来说,需要在 socket 对象创建的时候,就指定一个端口号,作为构造方法的参数,后续服务器运行之后,操作系统就会把端口号和该进程关联起来

对于同一个系统来说,同一时刻,一个端口号只能被一个进程绑定,但是一个进程可以绑定多个端口号(创建多个 socket 对象),端口号就是为了区分进程

创建 socket 对象之后开始接收客户端的请求:

//获取用户请求并解析
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);
socket.send(responsePacket);

这里把相应的内容写回客户端时,由于原来是一个字符串,直接获取长度获取的是字符的个数,所以需要获取字节数组之后再获取字节数组的长度

还有就是,由于 UDP 是无连接的(没有保存通信双方的 ip 和端口),DatagramSocket 这个类中不持有对方的信息,进行 send 的时候就需要在 send 的数据包里,把要发给谁这样的信息写进去

//把响应写回客户端
DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),
                                                   response.getBytes().length,requestPacket.getSocketAddress());
socket.send(responsePacket);

服务端整体逻辑:

public class UdpEchoServer {
    private DatagramSocket socket = null;
    public UdpEchoServer(int port) throws SocketException {
        //对于服务器端来说,需要在 socket 对象创建的时候,就指定一个端口号,作为构造方法的参数
        //后续服务器运行之后,操作系统就会把端口号和该进程关联起来
        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(),requestPacket.getPort(),
                              request,response);
        }
    }
    //根据请求确定响应
    private String process(String request){
        return request;
    }
    public static void main(String[] args) throws IOException {
        UdpEchoServer udpEchoServer = new UdpEchoServer(8090);
        udpEchoServer.start();
    }
}

接下来看客户端:

由于是客户端主动发送的请求,所以需要知道服务端的端口号才能找到服务器,所以客户端不需要指定端口号,但不是客户端不需要端口号,而是系统随机分配了一个端口给客户端

此外,如果客户端指定了端口之后,由于客户端是在用户的电脑上运行的,所指定的端口就可能和现有的端口冲突

构造方法里需要拿到服务端的 IP 和端口号

public UdpEchoClient(String serverIP,int serverPort) throws SocketException {
    socket = new DatagramSocket();
    this.serverIP = serverIP;
    this.serverPort = serverPort;
}

接下来创建请求数据报时,传入请求内容的字节数组和长度,再传入服务器的 IP 和端口号,但是发现报错了

因为原来定义的 IP 是字符串类型的,所以需要转化一下:

DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),request.getBytes().length,
                                                  InetAddress.getByName(this.serverIP),this.serverPort);
public class UdpEchoClient {
    private DatagramSocket socket = null;
    private String serverIP;
    private int serverPort;
    public UdpEchoClient(String serverIP,int serverPort) throws SocketException {
        socket = new DatagramSocket();
        this.serverIP = serverIP;
        this.serverPort = serverPort;
    }
    public void start() throws IOException {
        System.out.println("客户端开始运行...");
        Scanner scanner = new Scanner(System.in);
        while (true){
            System.out.print("->");
            String request = scanner.next();
            //构造出 UDP 请求
            DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),request.getBytes().length,
                                                              InetAddress.getByName(this.serverIP),this.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 udpEchoClient = new UdpEchoClient("127.0.0.1",8090);
        udpEchoClient.start();
    }
}
相关文章
|
2天前
|
弹性计算 人工智能 架构师
阿里云携手Altair共拓云上工业仿真新机遇
2024年9月12日,「2024 Altair 技术大会杭州站」成功召开,阿里云弹性计算产品运营与生态负责人何川,与Altair中国技术总监赵阳在会上联合发布了最新的“云上CAE一体机”。
阿里云携手Altair共拓云上工业仿真新机遇
|
28天前
|
运维 Cloud Native Devops
一线实战:运维人少,我们从 0 到 1 实践 DevOps 和云原生
上海经证科技有限公司为有效推进软件项目管理和开发工作,选择了阿里云云效作为 DevOps 解决方案。通过云效,实现了从 0 开始,到现在近百个微服务、数百条流水线与应用交付的全面覆盖,有效支撑了敏捷开发流程。
19257 29
|
29天前
|
人工智能 自然语言处理 搜索推荐
阿里云Elasticsearch AI搜索实践
本文介绍了阿里云 Elasticsearch 在AI 搜索方面的技术实践与探索。
18797 20
|
28天前
|
Rust Apache 对象存储
Apache Paimon V0.9最新进展
Apache Paimon V0.9 版本即将发布,此版本带来了多项新特性并解决了关键挑战。Paimon自2022年从Flink社区诞生以来迅速成长,已成为Apache顶级项目,并广泛应用于阿里集团内外的多家企业。
17504 13
Apache Paimon V0.9最新进展
|
30天前
|
存储 人工智能 前端开发
AI 网关零代码解决 AI 幻觉问题
本文主要介绍了 AI Agent 的背景,概念,探讨了 AI Agent 网关插件的使用方法,效果以及实现原理。
18692 15
|
28天前
|
人工智能 自然语言处理 搜索推荐
评测:AI客服接入钉钉与微信的对比分析
【8月更文第22天】随着人工智能技术的发展,越来越多的企业开始尝试将AI客服集成到自己的业务流程中。本文将基于《10分钟构建AI客服并应用到网站、钉钉或微信中》的解决方案,详细评测AI客服在钉钉和微信中的接入流程及实际应用效果,并结合个人体验分享一些心得。
9905 9
|
1月前
|
消息中间件 弹性计算 关系型数据库
函数计算驱动多媒体文件处理解决方案体验评测
从整体解读到部署体验,多方位带你了解如何利用函数计算驱动多媒体文件处理,告别资源瓶颈。
10441 13
|
22天前
|
存储 JSON Serverless
西游再现,函数计算一键部署 Flux 超写实文生图模型部署
参与体验活动生成西游人物图像,既有机会赢取好礼!本次实验在函数计算中内置了flux.1-dev-fp8大模型,通过函数计算+Serverless应用中心一键部署Flux模型,快速生成超写实图像。首次开通用户可领取免费试用额度,部署过程简单高效。完成部署后,您可以通过修改提示词生成各种风格的图像,体验Flux模型的强大绘图能力。
西游再现,函数计算一键部署 Flux 超写实文生图模型部署
|
1月前
|
SQL 容灾 关系型数据库
让X不断延伸, 从跨AZ到跨Region再到跨Cloud
本文从“空间”这一维度,聊一聊PolarDB-X在跨空间部署能力上的不断发展和延伸,以及在不同空间范围下的高可用和容灾能力,并着重介绍一下最新的产品能力——GDN(Global Database Network)。
|
1月前
|
缓存 测试技术 调度
PolarDB-X的TPC-H列存执行计划
本文从官方的角度逐条解析PolarDB-X在TPC-H列存执行计划的设计要点。这些要点不仅包含了各项优化的原理,还提供了相关的证明与代码实现,希望帮助读者更深入地理解PolarDB-X的列存优化器。
7872 11