Spark源码(二)-Netty简介

news2024/11/23 19:00:57

一、Netty简介

Netty 是一个异步事件驱动的网络通信应用框架,用于快速开发可维护的高性能服务器和客户端。简单地说Netty封装了JDK的NIO,不用再写一大堆复杂的代码,从NIO各种繁复的细节中脱离出来,让开发者重点关心业务逻辑。

二、Netty重要API

说明:大部分是模板代码,重点应该关注的是处理业务逻辑的Handler, 其按照角色不同分为ServerHandler和ClientHandler,按照数据处理流向不同,分为InboundHandler(对应接口是ChannelInboundHandlerAdapter)-处理输入数据,比如从客户端的角度,是处理从服务端发送过来的数据, 和OutboundHandler(对应接口是ChannelOutboundHandlerAdapter)-处理输出数据,比如从客户端的角度,是处理其发送给服务端的数据。

1、服务端

//用于接收客户端的连接请求的线程池
val bossGroup = new NioEventLoopGroup()
//用与处理客户端SocketChannel的网络读写的线程池
val workerGroup = new NioEventLoopGroup()
//是Netty用户启动NIO服务端的辅助启动类,降低服务端的开发复杂度
val bootstrap = new ServerBootstrap()
//将两个NIO线程组作为参数传入到ServerBootstrap
bootstrap.group(bossGroup, workerGroup)
  //创建NioServerSocketChannel
  .channel(classOf[NioServerSocketChannel])
  //绑定事件处理类
  .childHandler(new ChannelInitializer[SocketChannel] {
    override def initChannel(ch: SocketChannel): Unit = {
      // 日常主要要写的是ServerHandler逻辑 
      // 并且通常会添加多个ServerHandler
      ch.pipeline().addLast(new ServerHandler1)
    }
  })
//绑定端口地址端口
bootstrap.bind(host, port)

2、客户端

//创建客户端NIO线程组
val eventGroup = new NioEventLoopGroup
//创建客户端辅助启动类
val bootstrap = new Bootstrap
//将NIO线程组传入到Bootstrap
bootstrap.group(eventGroup)
  //创建NioSocketChannel
  .channel(classOf[NioSocketChannel])
  //绑定事件处理类
  .handler(new ChannelInitializer[SocketChannel] {
    override def initChannel(ch: SocketChannel): Unit = {
      // 日常主要要写的是ClientHandler逻辑 
      // 并且通常会添加多个ClientHandler
      ch.pipeline().addLast(new ClientHandler1)
    }
  })
//发送连接操作
bootstrap.connect(host, port)

三、Handler执行顺序

如第二部分所述,日常开发主要是写Handler,用于处理不同的业务逻辑,并且通常需要添加多个Handler,那么多个Handler的执行顺序如何呢?示例如下

比如一个客户端添加了5个Handler, 其中1和2是inBound,3和4是OutBound,5是InboundOutBound,

该客户端向外发送的消息会依次经过如下Handle的处理:5->4->3,而其接收到的外部信息会依次经过如下Handle的处理:1->2->5

四、应用举例

1、案例1

描述:客户端与服务端建立连接,会各自执行Handler中的channelActive方法

客户端:ClientHandler1d的channelActive方法被调用[已跟服务器建立连接]

服务端: ServerHandler1的channelActive方法被调用[一个客户端连接上]

(1) 服务端代码

① NettyServer

package top.doe.netty.demo1

import io.netty.bootstrap.ServerBootstrap
import io.netty.channel.ChannelInitializer
import io.netty.channel.nio.NioEventLoopGroup
import io.netty.channel.socket.SocketChannel
import io.netty.channel.socket.nio.NioServerSocketChannel

class NettyServer1 {

  def bind(host: String, port: Int): Unit = {
    //用于接收客户端的连接请求的线程池
    val bossGroup = new NioEventLoopGroup()
    //用与处理客户端SocketChannel的网络读写的线程池
    val workerGroup = new NioEventLoopGroup()
    //是Netty用户启动NIO服务端的辅助启动类,降低服务端的开发复杂度
    val bootstrap = new ServerBootstrap()
    //将两个NIO线程组作为参数传入到ServerBootstrap
    bootstrap.group(bossGroup, workerGroup)
      //创建NioServerSocketChannel
      .channel(classOf[NioServerSocketChannel])
      //绑定事件处理类
      .childHandler(new ChannelInitializer[SocketChannel] {
        override def initChannel(ch: SocketChannel): Unit = {
          ch.pipeline().addLast(new ServerHandler1)
        }
      })
    //绑定端口地址端口
    bootstrap.bind(host, port)
  }
}

object NettyServer1 {

  def main(args: Array[String]) {
    val host = "localhost"
    val port = 8888
    val server = new NettyServer1
    server.bind(host, port)
  }
}

②ServerHandler

package top.doe.netty.demo1

import io.netty.channel.{ChannelHandlerContext, ChannelInboundHandlerAdapter}

class ServerHandler1 extends ChannelInboundHandlerAdapter {

  /**
   * 有客户端与服务端建立连接后调用
   */
  override def channelActive(ctx: ChannelHandlerContext): Unit = {
    println("ServerHandler的channelActive方法被调用【一个客户端连接上】")
  }

  /**
   * 有客户端与服务端断开连接后调用
   */
  override def channelInactive(ctx: ChannelHandlerContext): Unit = {
    println("ServerHandler的channelInactive方法被调用【一个客户端与服务端断开连接了】")
  }

  /**
   * 接受客户端发送来的消息
   */
  override def channelRead(ctx: ChannelHandlerContext, msg: scala.Any): Unit = {
    println("ServerHandler的channelRead方法被调用【收到客户端发送的消息了】")
  }

}

(2) 客户端代码

① NettyClient

package top.doe.netty.demo1

import io.netty.bootstrap.Bootstrap
import io.netty.channel.ChannelInitializer
import io.netty.channel.nio.NioEventLoopGroup
import io.netty.channel.socket.SocketChannel
import io.netty.channel.socket.nio.NioSocketChannel

class NettyClient1 {

  def connect(host: String, port: Int): Unit = {
    //创建客户端NIO线程组
    val eventGroup = new NioEventLoopGroup
    //创建客户端辅助启动类
    val bootstrap = new Bootstrap
    //将NIO线程组传入到Bootstrap
    bootstrap.group(eventGroup)
      //创建NioSocketChannel
      .channel(classOf[NioSocketChannel])
      //绑定事件处理类
      .handler(new ChannelInitializer[SocketChannel] {
        override def initChannel(ch: SocketChannel): Unit = {
          ch.pipeline().addLast(new ClientHandler1)
        }
      })
    //发送连接操作
    bootstrap.connect(host, port)
  }
}

object NettyClient1 {
  def main(args: Array[String]) {
    val host = "localhost"
    val port = 8888
    val client = new NettyClient1
    client.connect(host, port)
  }
}

②ClientHandler

package top.doe.netty.demo1

import io.netty.channel.{ChannelHandlerContext, ChannelInboundHandlerAdapter}


class ClientHandler1 extends ChannelInboundHandlerAdapter {

  /**
   * 一旦跟服务端建立上连接,channelActive方法将被调用
   */
  override def channelActive(ctx: ChannelHandlerContext): Unit = {
    println("ClientHandler的channelActive方法被调用!【已经跟服务端连接上了】")
  }

  /**
   * 服务端返回消息后,channelRead方法被调用,该方法用于接送服务端返回的消息
   */
  override def channelRead(ctx: ChannelHandlerContext, msg: scala.Any): Unit = {
    println("ClientHandler的channelRead方法被调用!")
  }

}

(3) 执行结果

>>客户端

>>服务端

2、案例2

在案例1的基础上,服务端和客户端给彼此发送一条消息。

(1) 基本流程

 (2) 服务端代码

① NettyServer

package com.wakedata.demo2

import io.netty.bootstrap.ServerBootstrap
import io.netty.channel.ChannelInitializer
import io.netty.channel.nio.NioEventLoopGroup
import io.netty.channel.socket.SocketChannel
import io.netty.channel.socket.nio.NioServerSocketChannel

class NettyServer2 {
  def bind(host:String,port:Int) = {

    val bossGroup = new NioEventLoopGroup()
    val workerGroup = new NioEventLoopGroup()

    val bootstrap = new ServerBootstrap()
    bootstrap.group(bossGroup,workerGroup)
      .channel(classOf[NioServerSocketChannel])
      .childHandler(new ChannelInitializer[SocketChannel] {
        override def initChannel(ch: SocketChannel): Unit = {
          ch.pipeline().addLast(new ServerHandler2)
        }
      })

    bootstrap.bind(host,port)
  }

}

object NettyServer2{
  def main (args: Array[String] ): Unit = {
    val host = "localhost"
    val port = 8888
    val server2 = new NettyServer2
    server2.bind(host,port)
}
}

 ②ServerHandler

package com.wakedata.demo2

import io.netty.buffer.{ByteBuf, Unpooled}
import io.netty.channel.{ChannelHandlerContext, ChannelInboundHandlerAdapter}

class ServerHandler2 extends ChannelInboundHandlerAdapter {
  override def channelActive(ctx: ChannelHandlerContext): Unit = {
    println("ServerHandler1的channelActive方法被调用[一个客户端连接上]")

  }

  override def channelInactive(ctx: ChannelHandlerContext): Unit = {
    println("ServerHandler1的channelInactive方法被调用[一个客户端断开连接]")

  }

  override def channelRead(ctx: ChannelHandlerContext, msg: scala.Any): Unit = {
    //接收客户端发送过来的消息
    val byteBuf = msg.asInstanceOf[ByteBuf]
    val bytes = new Array[Byte](byteBuf.readableBytes())
    byteBuf.readBytes(bytes)
    val message = new String(bytes, "UTF-8")
    println("ServerHandler2的channelRead方法被调用[收到客户端发送的消息了]" + message)

    //将数据发送到客户端
    val back = "你好"
    val resp = Unpooled.copiedBuffer(back.getBytes("UTF-8"))
    ctx.writeAndFlush(resp)
  }
}

 (3) 客户端代码

① NettyClient

package com.wakedata.demo2

import io.netty.bootstrap.Bootstrap
import io.netty.channel.ChannelInitializer
import io.netty.channel.nio.NioEventLoopGroup
import io.netty.channel.socket.SocketChannel
import io.netty.channel.socket.nio.NioSocketChannel

class NettyClient2 {
  def connect(host:String,port:Int):Unit = {
    //创建客户端的NIO线程组
    val eventGroup = new NioEventLoopGroup()
    //创建客户端辅助启动类
    val bootstrap = new Bootstrap()
    //将NIO线程组传入到Bootstrap
    bootstrap.group(eventGroup)
      .channel(classOf[NioSocketChannel])
    //绑定事件处理类
      .handler(new ChannelInitializer[SocketChannel] {
        override def initChannel(ch: SocketChannel): Unit = {
          ch.pipeline().addLast(new ClientHandler2)
        }
      })
    //发送连接操作
    bootstrap.connect(host,port)
  }
}

object NettyClient2{
  def main(args: Array[String]): Unit = {
    val host = "localhost"
    val port = 8888
    val client = new NettyClient2()
    client.connect(host,port)
  }
}


 ②ClientHandler

package com.wakedata.demo2

import io.netty.buffer.{ByteBuf, Unpooled}
import io.netty.channel.{ChannelHandlerContext, ChannelInboundHandlerAdapter}

class ClientHandler2 extends ChannelInboundHandlerAdapter{
  override def channelActive(ctx: ChannelHandlerContext): Unit = {
    println("ClientHandler2的channelActive方法被调用[已跟服务器建立连接]")
    //向服务端发送消息
    val msg = "hello"
    ctx.writeAndFlush(Unpooled.copiedBuffer(msg.getBytes("UTF-8")))
  }

  override def channelRead(ctx: ChannelHandlerContext, msg: Any): Unit = {

    //读取服务端返回的消息
    val byteBuf = msg.asInstanceOf[ByteBuf]
    val bytes = new Array[Byte](byteBuf.readableBytes())
    byteBuf.readBytes(bytes)
    val message = new String(bytes, "UTF-8")
    print("ClientHandler2的channelRead方法被调用,接收到服务器端发送过来的消息:" + message)

  }
}

(4) 执行结果

>> 客户端

 >> 服务端

3、案例3

在案例2的基础上,添加多个Handler处理器(共计3个)。因为发送的case class对象,所以消息发出之前,会经过一个OurBoundHandler进行序列化,然后接受消息时,首先经过一个InBoundHandler进行解码,然后再经过另外一个 InBoundHandler对解码后的数据进行读取。

 (1) 服务端代码

① NettyServer

package com.wakedata.demo3

import io.netty.bootstrap.ServerBootstrap
import io.netty.channel.ChannelInitializer
import io.netty.channel.nio.NioEventLoopGroup
import io.netty.channel.socket.SocketChannel
import io.netty.channel.socket.nio.NioServerSocketChannel
import io.netty.handler.codec.serialization.{ClassResolvers, ObjectDecoder, ObjectEncoder}

class NettyServer3 {

  def bind(host: String, port: Int): Unit = {
    //配置服务端线程池组
    //用于服务器接收客户端的连接
    val bossGroup = new NioEventLoopGroup()
    //用户进行SocketChannel的网络读写
    val workerGroup = new NioEventLoopGroup()

    //是Netty用户启动NIO服务端的辅助启动类,降低服务端的开发复杂度
    val bootstrap = new ServerBootstrap()
    //将两个NIO线程组作为参数传入到ServerBootstrap
    bootstrap.group(bossGroup, workerGroup)
      //创建NioServerSocketChannel
      .channel(classOf[NioServerSocketChannel])
      //绑定I/O事件处理类
      .childHandler(new ChannelInitializer[SocketChannel] {
        override def initChannel(ch: SocketChannel): Unit = {
          //处理输入的数据执行顺序 decoder -> handler
          //处理返回的数据执行顺序 encoder
          ch.pipeline().addLast("encoder", new ObjectEncoder) //实现了ChannelOutboundHandler
          ch.pipeline().addLast("decoder", new ObjectDecoder(ClassResolvers.cacheDisabled(getClass.getClassLoader))) //实现了ChannelInboundHandler
          ch.pipeline().addLast("handler", new ServerHandler3) //实现了ChannelInboundHandler
        }
      })
    val channelFuture = bootstrap.bind(host, port)
    channelFuture.syncUninterruptibly
  }
}

object NettyServer3 {
  def main(args: Array[String]) {
    val host = "localhost"
    val port = 8888
    val server = new NettyServer3
    server.bind(host, port)
  }
}

② ServerHandler

package com.wakedata.demo3

import io.netty.channel.{ChannelHandlerContext, ChannelInboundHandlerAdapter}




class ServerHandler3 extends ChannelInboundHandlerAdapter {

  /**
   * 有客户端建立连接后调用
   */
  override def channelActive(ctx: ChannelHandlerContext): Unit = {
    println("一个客户端连接上了...")
  }

  /**
   * 接受客户端发送来的消息
   */
  override def channelRead(ctx: ChannelHandlerContext, msg: scala.Any): Unit = {
    //进行模式匹配
    msg match {
      case RequestMsg(msg) => {
        println("收到客户端发送的消息:" + msg)
        //将数据发送到客户端
        ctx.writeAndFlush(ResponseMsg("haha"))
      }
    }
  }


}

③ ResponseMsg

package com.wakedata.demo3

case class ResponseMsg(msg: String)

 (2) 服务端代码

① NettyClient

package com.wakedata.demo3

import io.netty.bootstrap.Bootstrap
import io.netty.channel.ChannelInitializer
import io.netty.channel.nio.NioEventLoopGroup
import io.netty.channel.socket.SocketChannel
import io.netty.channel.socket.nio.NioSocketChannel
import io.netty.handler.codec.serialization.{ClassResolvers, ObjectDecoder, ObjectEncoder}

class NettyClient3 {

  def connect(host: String, port: Int): Unit = {
    //创建客户端NIO线程组
    val eventGroup = new NioEventLoopGroup
    //创建客户端辅助启动类
    val bootstrap = new Bootstrap
    //将NIO线程组传入到Bootstrap
    bootstrap.group(eventGroup)
      //创建NioSocketChannel
      .channel(classOf[NioSocketChannel])
      //绑定I/O事件处理类
      .handler(new ChannelInitializer[SocketChannel] {
        override def initChannel(ch: SocketChannel): Unit = {
          ch.pipeline().addLast("encoder", new ObjectEncoder)
          ch.pipeline().addLast("decoder", new ObjectDecoder(ClassResolvers.cacheDisabled(getClass.getClassLoader)))
          ch.pipeline().addLast("handler", new ClientHandler3)
        }
      })
    //发起异步连接操作
    val channelFuture = bootstrap.connect(host, port)
    channelFuture.syncUninterruptibly
  }
}

object NettyClient3 {
  def main(args: Array[String]) {
    val host = "localhost"
    val port = 8888
    val client = new NettyClient3
    client.connect(host, port)
  }
}

② ClientHandler

package com.wakedata.demo3

import io.netty.channel.{ChannelHandlerContext, ChannelInboundHandlerAdapter}


class ClientHandler3 extends ChannelInboundHandlerAdapter {

  //一旦跟服务端建立上连接channelActive将被调用
  override def channelActive(ctx: ChannelHandlerContext): Unit = {
    println("已经跟服务端连接上了")
    //向服务端发送case class实例
    ctx.writeAndFlush(RequestMsg("hello"))
  }


  override def channelRead(ctx: ChannelHandlerContext, msg: scala.Any): Unit = {
    msg match {
      case ResponseMsg(msg) => {
        println("收到服务端返回的消息:" + msg)
      }
    }
  }
}

③ RequestMsg

package com.wakedata.demo3

case class RequestMsg(content: String)

(3) 执行结果

>>客户端

>>服务端

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

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

相关文章

新书速递——《可解释AI实战(PyTorch版)》

本书旨在帮助你实施最新的可解释AI技术,以构建公平且可解释的AI系统。可解释AI是当今AI研究中的热门话题,但只有少数资源和指南涵盖了所有重要技术,这些技术对实践者来说非常有价值。本书旨在填补这一空白。 本书读者对象 本书既适合那些有兴…

阿里云2核4G云服务器支持多少人同时在线?并发数计算?

阿里云2核4G服务器多少钱一年?2核4G配置1个月多少钱?2核4G服务器30元3个月、轻量应用服务器2核4G4M带宽165元一年、企业用户2核4G5M带宽199元一年。可以在阿里云CLUB中心查看 aliyun.club 当前最新2核4G服务器精准报价、优惠券和活动信息。 阿里云官方2…

04 | Swoole 源码分析之 epoll 多路复用模块

首发原文链接:Swoole 源码分析之 epoll 多路复用模块 大家好,我是码农先森。 引言 在传统的IO模型中,每个IO操作都需要创建一个单独的线程或进程来处理,这样的操作会导致系统资源的大量消耗和管理开销。 而IO多路复用技术通过…

第十四届蓝桥杯JavaA组省赛真题 - 棋盘

解题思路: 暴力 棋盘类题目取反操作: f[a][b]^1; 或者f[a][b] 1 - f[a][b]; import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner scan new Scanner(System.in);int n scan.nextInt();int m scan.nex…

(南京观海微电子)——GOA介绍

GOA是Gate on Array的简写,简单可以理解为gate IC集成在玻璃上了,面板就可以不用gate ic了,是一种低成本的设计,窄边框面板大多数都用了GOA技术。还有一些公司叫GIP(Gate in Panel),GDM等等。 …

已上线项目,突然有一天网站虽进得去,但是接口拿不到数据,作为前端的你如何排查问题?

在开始写这篇博客之前,想说几句题外话哈,虽然自己的粉丝不多,但自己每篇博客都是用心在写,可能后面会针对部分文章开启只有VIP才能访问,原因你们也懂得(▽),无非是想赚点外块呗,不过主要现在也是知识付费时代,毕竟自己写出的东西也是本人亲身经历着,也是具有一定的价值…

针对pycharm打开新项目需要重新下载tensorflow的问题解决

目录 一、前提 二、原因 三、解决办法 一、前提 下载包之前,已经打开了,某个项目。 比如:我先打开了下面这个项目: 然后在terminal使用pip命令下载: 如果是这种情况,你下载的这个包一般都只能用在这一个…

【已解决】centos执行Hangfire任务时间隔八个小时

参数说明: centos系统是7.9版本的 hangfire是Hangfire 1.8.7 centos系统的目前设置时区是cts的东八时区,截图如下 原因: hangfire任务设定每天执行时间东八实际偏移的执行时间 比如:你设定的是早上6点执行东八实际下午两点执行 …

【web自动化】selenium的使用(二)

目录 复选框操作多层框架操作下拉框操作弹窗操作 复选框操作 示例&#xff1a;批量操作对象&#xff0c;比如将页面上所有的checkbox 都勾上 方法&#xff1a;先用标签选择器选择页面上所有的input&#xff0c;然后从中过滤出属性type是checkbox的 并勾选 List<WebElemen…

Debian linux版本下运行的openmediavault网盘 千兆网卡升级万兆

一、适用场景 1、使用vmware ESXi虚拟化平台运行多种不同应用服务器时&#xff0c;其中网盘服务器采用开源的openmediavault搭建&#xff1b; 2、将老专业服务器升级千兆网为万兆网&#xff1b; 3、需要转移的数据量大的企业或用户&#xff1b; 4、从服务器到服务器的数据转移…

Mac上的Gatekeeper系统跟运行时保护

文章目录 问题&#xff1a;无法打开“xxx.xxx”&#xff0c;因为无法验证开发者。macOS无法验证此App是否包含恶意软件。如何解决&#xff1f; 参考资料门禁运行时保护 问题&#xff1a;无法打开“xxx.xxx”&#xff0c;因为无法验证开发者。macOS无法验证此App是否包含恶意软件…

vue3+vite模版框架 tabs右键刷新时丢失路由参数

问题&#xff1a; 标题栏的tabs的右键&#xff1a;刷新时&#xff0c;没有保存上一个页面传递过来的参数 分析&#xff1a; TagView.vue刷新事件 function refreshSelectedTag(view: TagView) {console.log(|--执行刷新, view)tagsViewStore.delCachedView(view);const {full…

IDEA的使用(概念,安装,配置,)以及什么是字符集,模版

目录 Intellij IDEA IDE的概念 IntelliJ IDEA的安装 IntelliJ IDEA的使用 基本配置 JDK配置 创建Module 基本用法 字体配置 主题配置 字符集 设置IDEA默认字符集 注释模板 字符集 字符集简介 常见字符集 Intellij IDEA 我们不可能一直使用记事本之类变成&#…

Win7共享文件夹无法访问怎么办,win7共享文件夹访问不了

在win7系统中,用户可以通过局域网建立共享,然后将一些文件夹进行共享,方便用户访问。“共享文件”一般常用于办公室,某位同事的计算机用来和其它计算机间相互分享的文件夹。然而有时候需要访问共享文件时,却出现无法访问的情况,要怎么解决这样的问题呢?接下来,小编带来…

3D转换1111

1.三维坐标系 1.3D位移: translate3d(x,y,z)  translform:translateX(100px)&#xff1a;仅仅是在x轴上移动  translform:translateY(100px)&#xff1a;仅仅是在Y轴上移动  translform:translateZ(100px)&#xff1a;仅仅是在Z轴上移动&#xff08;注意&#xff1…

linux yum install jdk如何查找安装目录并配置环境变量

Linux服务已安装jdk1.8&#xff0c;此时需要在该服务器部署的应用要求依赖openJDK11&#xff0c;可按照以下命令进行安装openJDK11。 搜索查看安装包&#xff1a;yum search java-11-openjdk 如果能查找到安装包&#xff0c;执行以下安装命令进行安装。 安装openjdk11&#x…

互联网、因特网、万维网的区别

互联网 internet&#xff1a;凡是能彼此通信的设备组成的网络就叫互联网&#xff0c;即使只有两台计算机&#xff0c;无论以何种技术使其彼此通信&#xff0c;都叫互联网。所以&#xff0c;根据互联网的覆盖规模可以分为&#xff1a; 局域网&#xff08;Local Area Network&am…

Java接口实战:模拟咖啡制作、订购与消费完整流程(day14)

定义接口&#xff1a; // 咖啡制作接口 interface CoffeeMaker { Coffee makeCoffee(String type); } // 咖啡店接口 interface CoffeeShop { void orderCoffee(String type, CoffeeConsumer consumer); } // 咖啡消费者接口 interface CoffeeConsumer { void …

预处理详解(一) -- 预定义符号与#define定义

目录 一. 预定义符号二. #define1.#define定义常量2.#define定义宏3.带有副作用的宏参数4.宏替换的规则5.宏和函数的对比 一. 预定义符号 %s _ _FILE_ _ //文件 %s _ _ DATE_ _ //日期 %s _ _ TIME_ _ //时间 %d _ _ LINE_ _ //行号 %d _ _ STDC_ _ //如果编译器支持 ANSI C,那…

python基础——异常捕获【try-except、else、finally】

&#x1f4dd;前言&#xff1a; 这篇文章主要介绍一下python基础中的异常处理&#xff1a; 1&#xff0c;异常 2&#xff0c;异常的捕获 3&#xff0c;finally语句 &#x1f3ac;个人简介&#xff1a;努力学习ing &#x1f4cb;个人专栏&#xff1a;C语言入门基础以及python入门…