TCP粘包和拆包

TCP粘包和拆包

(1)TCP是面向连接的,面向流的,提供可靠性服务。收发两端(客户端和服务端)都要有一一成对的socket,因此,发送端为了将多个发给接收端的包,更有效的发给对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。这样做虽然提高了效率,但是接收端难于分辨出完整的数据包,因为面向流的通信是无消息保护边界的。
(2)由于TCP无消息保护边界,需要在接收端处理消息边界问题,也就是我们所说的粘包和拆包问题。
(3)TCP粘包、拆包图解
在这里插入图片描述
下面我们通过Netty实验还原场景:
我们通过这样的方法来还原粘包和拆包场景,客户端循环想服务端发送10条消息,服务端每次接收到消息即想客户端返回一个UUID,我们可以通过观察,这10条消息服务端接收过程中并不是一次性接收的,并且每次发送接收结果不一,说明部分消息发生了粘包现象(拆包现象)

服务端:
MyServer

package com.sgg.Netty.TCP;import com.sgg.Netty.simple.NettyServerhandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;public class MyServer {public static void main(String[] args) throws InterruptedException {//创建BossGroup和WorkerGroup//说明//1、创建两个线程组BossGroup和WorkerGroup//2、BossGroup只负责处理请求,真正和客户端的业务处理会交给WorkerGroup//3、两个都是无限循环EventLoopGroup bossGroup = new NioEventLoopGroup(1);EventLoopGroup workerGroup = new NioEventLoopGroup();//创建服务端的启动对象,配置启动参数ServerBootstrap bootstrap = new ServerBootstrap();//使用链式变成来进行设置bootstrap.group(bossGroup,workerGroup)       //设置两个线程组.channel(NioServerSocketChannel.class)     //使用NioServerSocketChannel作为服务器的通道实现.option(ChannelOption.SO_BACKLOG,128)       //设置线程队列的连接个数.childOption(ChannelOption.SO_KEEPALIVE,true)       //设置保持活动连接状态.childHandler(new MyServerInitializer());System.out.println(".....服务器准备好了");// Future-Listener机制//绑定一个端口并且同步,生成一个ChannelFuture对象ChannelFuture cf = bootstrap.bind(6668).sync();//给cf注册监听器,监控我们关心的事件cf.addListener(new ChannelFutureListener() {@Overridepublic void operationComplete(ChannelFuture channelFuture) throws Exception {if(cf.isSuccess()){System.out.println("监听端口成功");}else{System.out.println("监听端口失败");}}});//对关闭通道进行监听cf.channel().closeFuture().sync();}
}

MyServerInitializer

package com.sgg.Netty.TCP;import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;public class MyServerInitializer extends ChannelInitializer<SocketChannel> {@Overrideprotected void initChannel(SocketChannel socketChannel) throws Exception {ChannelPipeline pipeline = socketChannel.pipeline();pipeline.addLast(new MyServerhandler());}
}

MyServerhandler

package com.sgg.Netty.TCP;import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;import java.nio.charset.Charset;
import java.util.UUID;public class MyServerhandler extends SimpleChannelInboundHandler<ByteBuf> {private int count;@Overrideprotected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf) throws Exception {//byte[] buffer = new byte[byteBuf.readableBytes()];byteBuf.readBytes(buffer);//将buffer转成字符串String message = new String(buffer, Charset.forName("utf-8"));System.out.println("服务器接收到数据"+ message);System.out.println("服务器端接收到消息="+ (++this.count));//服务端回送数据给客户端,回送 一个随机的IDByteBuf responseByteBuf = Unpooled.copiedBuffer(UUID.randomUUID().toString()+"  ",Charset.forName("utf-8"));channelHandlerContext.writeAndFlush(responseByteBuf);}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {cause.printStackTrace();ctx.close();}
}

MyClient

package com.sgg.Netty.TCP;import com.sgg.Netty.http.TestServerInitializer;
import com.sgg.Netty.simple.NettyClienthandler;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;public class MyClient {public static void main(String[] args) throws InterruptedException {//客户端需要一个事件循环组NioEventLoopGroup eventLoopGroup = new NioEventLoopGroup();try {//创建客户端启动对象//注意客户端不是ServerBootstrap,是BootstrapBootstrap bootstrap = new Bootstrap();//设置相关参数bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class).handler(new MyClientInitializer());System.out.println("客户端OK");//启动客户端ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 6668).sync();//给关闭通道进行监听channelFuture.channel().closeFuture().sync();} finally {//关闭eventLoopGroup.shutdownGracefully();}}
}

MyClientInitializer

package com.sgg.Netty.TCP;import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;public class MyClientInitializer extends ChannelInitializer<SocketChannel> {@Overrideprotected void initChannel(SocketChannel socketChannel) throws Exception {ChannelPipeline pipeline = socketChannel.pipeline();pipeline.addLast(new MyClienthandler());}
}

MyClienthandler

package com.sgg.Netty.TCP;import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;import java.nio.charset.Charset;public class MyClienthandler extends SimpleChannelInboundHandler<ByteBuf> {private int count;@Overrideprotected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf) throws Exception {byte[] buffer = new byte[byteBuf.readableBytes()];byteBuf.readBytes(buffer);String message = new String(buffer,Charset.forName("utf-8"));System.out.println("客户端接收到消息="+message);System.out.println("客户端接收消息数量="+(++this.count));}@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {//使用客户端发送10条数据for(int i=0;i<10;i++){ByteBuf buffer =  Unpooled.copiedBuffer("hello,server"+i, Charset.forName("utf-8"));ctx.writeAndFlush(buffer);}}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {cause.printStackTrace();ctx.close();}
}

最终我们可以看到:
服务端入下,同样客户端返回的随机数个数与接收次数统一
在这里插入图片描述那么我们怎么解决TCP的粘包和拆包呢??

TCP粘包和拆包解决方案

(1)使用自定义协议+编解码器 来解决
(2)关键就是要解决 服务器每次读取数据长度的问题,这个问题解决,就不会出现服务器多读或少读数据的问题,从而避免TCP粘包和拆包

具体实例:
(1)要求客户端发送5个Message对象,客户端每次发送一个Message对象
(2)服务端每次接收一个Message,分5次进行解码,每读到一个Message,会回复一个Message对象给客户端。

下面我们来讲解一下我们的思路:
(1)首先我们需要定义一个协议包的对象,也就是定义协议传输的格式
(2)我们需要根据这个协议包的格式来编写编码解码器的handler:MyMessageEncoder、MyMessageDecoder
(3)我们编写具体的消息发送和消息响应的自定义handler
在这里插入图片描述
下面我们来看具体实现:
MessageProtocal (协议包类)

package com.sgg.Netty.protocolTCP;//协议包
public class MessageProtocal {private int len;// 关键private byte[] content;public int getLen() {return len;}public void setLen(int len) {this.len = len;}public byte[] getContent() {return content;}public void setContent(byte[] content) {this.content = content;}
}

MyMessageEncoder (编码器类:将消息转换成协议包对象)

package com.sgg.Netty.protocolTCP;import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;public class MyMessageEncoder extends MessageToByteEncoder<MessageProtocal> {@Overrideprotected void encode(ChannelHandlerContext channelHandlerContext, MessageProtocal messageProtocal, ByteBuf byteBuf) throws Exception {System.out.println("MyMessageEncoder encode 方法调用");byteBuf.writeInt(messageProtocal.getLen());     byteBuf.writeBytes(messageProtocal.getContent());}
}

MyMessageDecoder (解码器类:将协议包对象解码为消息)

package com.sgg.Netty.protocolTCP;import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ReplayingDecoder;import java.util.List;public class MyMessageDecoder extends ReplayingDecoder<Void> {@Overrideprotected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {System.out.println("MyMessageDecoder decode方法被调用");//获取length、contentint length= byteBuf.readInt();byte[] content = new byte[length];byteBuf.readBytes(content);//封装成MessageProtlcol对象,放入byteBuf,传递到下一个handlerMessageProtocal messageProtocal = new MessageProtocal();messageProtocal.setLen(length);messageProtocal.setContent(content);//将封装的messageProtocal对象放入listlist.add(messageProtocal);}
}

服务端类:MyServer、MyServerInitializer、MyServerhandler
MyServer

package com.sgg.Netty.protocolTCP;import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;public class MyServer {public static void main(String[] args) throws InterruptedException {//创建BossGroup和WorkerGroup//说明//1、创建两个线程组BossGroup和WorkerGroup//2、BossGroup只负责处理请求,真正和客户端的业务处理会交给WorkerGroup//3、两个都是无限循环EventLoopGroup bossGroup = new NioEventLoopGroup(1);EventLoopGroup workerGroup = new NioEventLoopGroup();//创建服务端的启动对象,配置启动参数ServerBootstrap bootstrap = new ServerBootstrap();//使用链式变成来进行设置bootstrap.group(bossGroup,workerGroup)       //设置两个线程组.channel(NioServerSocketChannel.class)     //使用NioServerSocketChannel作为服务器的通道实现.option(ChannelOption.SO_BACKLOG,128)       //设置线程队列的连接个数.childOption(ChannelOption.SO_KEEPALIVE,true)       //设置保持活动连接状态.childHandler(new MyServerInitializer());System.out.println(".....服务器准备好了");// Future-Listener机制//绑定一个端口并且同步,生成一个ChannelFuture对象ChannelFuture cf = bootstrap.bind(6668).sync();//给cf注册监听器,监控我们关心的事件cf.addListener(new ChannelFutureListener() {@Overridepublic void operationComplete(ChannelFuture channelFuture) throws Exception {if(cf.isSuccess()){System.out.println("监听端口成功");}else{System.out.println("监听端口失败");}}});//对关闭通道进行监听cf.channel().closeFuture().sync();}
}

MyServerInitializer

package com.sgg.Netty.protocolTCP;import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;public class MyServerInitializer extends ChannelInitializer<SocketChannel> {@Overrideprotected void initChannel(SocketChannel socketChannel) throws Exception {ChannelPipeline pipeline = socketChannel.pipeline();pipeline.addLast(new MyMessageDecoder());  //添加加码器handler到pipelinepipeline.addLast(new MyMessageEncoder());  //添加解码器handler到pipelinepipeline.addLast(new MyServerhandler());}
}

MyServerhandler

package com.sgg.Netty.protocolTCP;import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;import java.nio.charset.Charset;
import java.util.UUID;public class MyServerhandler extends SimpleChannelInboundHandler<MessageProtocal> {private int count;@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {cause.printStackTrace();ctx.close();}@Overrideprotected void channelRead0(ChannelHandlerContext channelHandlerContext, MessageProtocal messageProtocal) throws Exception {//接收到数据,并处理int len = messageProtocal.getLen();byte[] content = messageProtocal.getContent();System.out.println();System.out.println();System.out.println("服务器收到消息如下:");System.out.println("长度="+len);System.out.println("内容="+new String(content,Charset.forName("utf-8")));System.out.println("服务器接收到消息包数量:"+(++this.count));//回复消息//定义一个字符串String responseContent = UUID.randomUUID().toString();//将字符串转为byte数组byte[] rescontent = responseContent.getBytes("utf-8");//得到数据长度int reslength = responseContent.getBytes("utf-8").length;//封装成messageProtocal1对象MessageProtocal messageProtocal1 = new MessageProtocal();messageProtocal1.setLen(reslength);messageProtocal1.setContent(rescontent);channelHandlerContext.writeAndFlush(messageProtocal1);}
}

客户端类:MyClient、MyClientInitializer、MyClienthandler
MyClient

package com.sgg.Netty.protocolTCP;import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;public class MyClient {public static void main(String[] args) throws InterruptedException {//客户端需要一个事件循环组NioEventLoopGroup eventLoopGroup = new NioEventLoopGroup();try {//创建客户端启动对象//注意客户端不是ServerBootstrap,是BootstrapBootstrap bootstrap = new Bootstrap();//设置相关参数bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class).handler(new MyClientInitializer());System.out.println("客户端OK");//启动客户端ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 6668).sync();//给关闭通道进行监听channelFuture.channel().closeFuture().sync();} finally {//关闭eventLoopGroup.shutdownGracefully();}}
}

MyClientInitializer

package com.sgg.Netty.protocolTCP;import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;public class MyClientInitializer extends ChannelInitializer<SocketChannel> {@Overrideprotected void initChannel(SocketChannel socketChannel) throws Exception {ChannelPipeline pipeline = socketChannel.pipeline();pipeline.addLast(new MyMessageEncoder());pipeline.addLast(new MyMessageDecoder());pipeline.addLast(new MyClienthandler());}
}

MyClienthandler

package com.sgg.Netty.protocolTCP;import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;public class MyClienthandler extends SimpleChannelInboundHandler<MessageProtocal> {private int count;@Overrideprotected void channelRead0(ChannelHandlerContext channelHandlerContext, MessageProtocal messageProtocal) throws Exception {int len = messageProtocal.getLen();byte[] content = messageProtocal.getContent();System.out.println();System.out.println();System.out.println("客户端接收消息如下:");System.out.println("长度:"+len);System.out.println("内容:"+new String(content,Charset.forName("utf-8")));System.out.println("客户端接收消息数量"+(++this.count));}@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {//使用客户端发送10条数据for(int i=0;i<5;i++){String mes = "今天天气冷";byte[] content = mes.getBytes(Charset.forName("utf-8"));int length = mes.getBytes(Charset.forName("utf-8")).length;//创建协议包对象MessageProtocal messageProtocal = new MessageProtocal();messageProtocal.setLen(length);messageProtocal.setContent(content);ctx.writeAndFlush(messageProtocal);}}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {System.out.println("异常信息="+cause.getMessage());ctx.close();}}

如上我们可以得到结果如下,不论怎么发送,消息永远不会发生粘包和拆包的现象:
在这里插入图片描述
在这里插入图片描述

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

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

相关文章

Python拆包

Python拆包 怎么元组拆包&#xff1f;怎么字典拆包&#xff1f;怎么拆包赋值&#xff1f; 拆包&#xff1a; 对于函数中的多个返回数据, 去掉元组, 列表 或者字典直接获取里面数据的过程 &#xff08;只能对可迭代对象进行拆包&#xff09; Python拆包&#xff1a; 就是把元组…

python 拆包_python 拆包

广告关闭 腾讯云11.11云上盛惠 &#xff0c;精选热门产品助力上云&#xff0c;云服务器首年88元起&#xff0c;买的越多返的越多&#xff0c;最高返5000元&#xff01; 为了查明原因&#xff0c;我去查了 matplotlib 的源码&#xff0c;发现 plot 函数返回的是一个列表&#…

【JavaEE初阶】 线程安全

文章目录 &#x1f334;线程安全的概念&#x1f333;观察线程不安全&#x1f384;线程不安全的原因&#x1f6a9;修改共享数据&#x1f4cc;原子性&#x1f4cc; 可见性&#x1f4cc;代码顺序性 &#x1f332;解决之前的线程不安全问题⭕总结 &#x1f334;线程安全的概念 线程…

深度学习基础知识 学习率调度器的用法解析

深度学习基础知识 学习率调度器的用法解析 1、自定义学习率调度器**&#xff1a;**torch.optim.lr_scheduler.LambdaLR2、正儿八经的模型搭建流程以及学习率调度器的使用设置 1、自定义学习率调度器**&#xff1a;**torch.optim.lr_scheduler.LambdaLR 实验代码&#xff1a; i…

基于Java+SpringBoot+Vue民宿管理系统的设计与实现 前后端分离【Java毕业设计·文档报告·代码讲解·安装调试】

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

【Java】什么是API

API (Application Programming Interface,应用程序编程接口) Java中的API 指的就是 JDK 中提供的各种功能的 Java类&#xff0c;这些类将底层封装起来&#xff0c;我们不需要关心这些类是如何实现的&#xff0c;只需要学习这些类如何使用即可&#xff0c;我们可以通过帮助文档…

【C语言】阶乘实现

&#x1f389;博客主页&#xff1a;Luo-Kuang-何 &#x1f389;座右铭&#xff1a;一起走向人生巅峰的路上&#x1f601; &#x1f389;学习进度&#xff1a;【C语言】 &#x1f389;博客声明&#xff1a;我将尽我所能&#xff0c;用心写好每一份博客&#xff0c;让更多小伙伴能…

WatchOS开发教程之四: Watch与 iPhone的通信和数据共享

WatchOS 开发教程系列文章: WatchOS开发教程之一: Watch App架构及生命周期 WatchOS开发教程之二: 布局适配和系统Icon设计尺寸 WatchOS开发教程之三: 导航方式和控件详解 WatchOS开发教程之四: Watch与 iPhone的通信和数据共享 WatchOS开发教程之五: 通知功能开发 WatchOS开发…

Kali Linux 秘籍 第九章 无线攻击

第九章 无线攻击 作者&#xff1a;Willie L. Pritchett, David De Smet 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 简介 当今&#xff0c;无线网络随处可见。由于用户四处奔走&#xff0c;插入以太网网线来获取互联网访问的方式非常不方便。无线网络为了使用便利…

python3遍历目录查找文件

一直有一部分软件&#xff0c;他们的主要功能就是方便用户查找本地文件位置。python当然也可以完成这项功能&#xff0c;所以我写了一个简短的代码。 写完发现&#xff0c;python真的是一门简洁的语言啊&#xff01; 我完成这个功能主要就是用了os模块的功能&#xff0c;直接…

WinSvr:在 Windows Server 中启用无线连接

默认情况下,所有 Windows Server 2022/2019/2016/2012R2 版本都禁用无线 (Wi-Fi) 支持。如果将 Wi-Fi 网络适配器(USB 或 PCI)插入运行 Windows Server 的主机,则无法在控制面板中启用它。本文将在这篇简短的说明中展示如何在 Windows Server 上启用无线支持。 注意,在 W…

【运维笔记】Docker 部署Kibana-7.4.0(在线Docker版)

Docker 部署Kibana-7.4.0&#xff08;在线Docker版&#xff09; 一、准备工作&#xff1a; Centos 7.5 安装 Docker-24.0.6 详细步骤&#xff08;避坑版&#xff09;&#xff1a; https://blog.csdn.net/seesun2012/article/details/133674191注意1&#xff1a;本文的命令使用…

短视频账号矩阵系统源码saas===独立部署

前言&#xff1a; 短视频账号矩阵是指在不同的短视频平台上&#xff0c;一个个人或企业所拥有的账号数量和分布情况。由于不同的短视频平台受众人群和内容类型等因素不同&#xff0c;因此拥有更多账号可以在更广泛的受众中传播内容&#xff0c;提高曝光度和流量。短视频账号矩阵…

管理类联考——逻辑——真题篇——按知识分类——第十章 数学相关

第十章 数学相关 第一节 集合 真题&#xff08;2010-53&#xff09;-数学相关-集合-画饼集能力-朴素逻辑 53.参加某国际学术研讨会的 60 名学者中&#xff0c;亚裔学者 31 人&#xff0c;博士 33 人&#xff0c;非亚裔学者中无博士学位的 4 人。根据上述陈述&#xff0c;参…

2017年全国硕士研究生入学统一考试管理类专业学位联考逻辑试题——解析版

&#x1f3e0;个人主页&#xff1a;fo安方的博客✨ &#x1f482;个人简历&#xff1a;大家好&#xff0c;我是fo安方&#xff0c;考取过HCIE Cloud Computing、CCIE Security、CISP、RHCE、CCNP RS、PEST 3等证书。&#x1f433; &#x1f495;兴趣爱好&#xff1a;b站天天刷&…

爬虫小白系列01期: 从李白杜甫,来看爬虫本质 、 浏览器访问网页原理 、 请求头的概念

众所周知&#xff0c;爬虫的本质是&#xff0c;模拟浏览器打开网页&#xff0c;获取网页中我们需要的那部分数据。 那首先我们应该清楚&#xff0c;普通一般浏览器打开网页的流程和原理是怎样的&#xff1f; 根据生活经验&#xff0c;我们使用浏览器打开网页的步骤一般是这样…

神犇营my0001:春晓

本题来源于神犇营 题目: [my0001] 唐代诗人孟浩然所作的《春晓》是一首家喻户晓的诗,但是校园里更流行改编版的《春晓》。 春眠不觉晓, 处处蚊子咬。 夜里嗡嗡声, 脓包知多少。 现在我们要用刚才所学的知识来输出这首诗的前两句。首先在右边的输入C++程序的基本框架…

世界十大名诗

世界十大名诗 时间&#xff1a;2011-01-07 来源&#xff1a;网络 点击&#xff1a;318次 When You Are Old by William Butler Yeats (1865-1939) WHEN you are old and gray and full of sleep, And nodding by the fire, take down this book, And slowly re…

9月火气大,能认真写代码么?

不羡鸳鸯不羡仙&#xff0c;一行代码调半天。原创&#xff1a;小姐姐味道&#xff08;微信公众号ID&#xff1a;xjjdog&#xff09;&#xff0c;欢迎分享&#xff0c;转载请保留出处。 我在风中藏把刀&#xff0c;斩尽世间秋色。 这句注定要流传千古的名诗&#xff0c;是xjjdo…

html语言登黄鹤楼,《中国诗词大会》命题专家方笑一, 揭秘千古名诗《登黄鹤楼》为何格律“不合格”...

楚天都市报10月26日讯(记者舒均 李辉 通讯员王红念 江萌)10月26日上午&#xff0c;华东师范大学古籍研究所教授、央视 《中国诗词大会》命题专家暨现场学术顾问方笑一做客“黄鹤大讲堂”&#xff0c;带来一场《古诗词与天下名楼》品评锦绣诗词的讲座&#xff0c;受到江城上百名…