Chemmy's Blog

chengming0916@outlook.com

DotNetty完全教程(八)

Excerpt

EventLoop介绍我们先回顾一下,EventLoop就是我们在最开始的示意图中的Selector,每个EventLoop和一个线程绑定,用于处理多个Channel。任务调度如果我们想实现延时任务的调度,比如连接成功5s之后发送一包数据,就可以用到EventLoop的计划任务ctx.Channel.EventLoop.Schedule(() =>{ Console.Wr…


版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

EventLoop

介绍

我们先回顾一下,EventLoop就是我们在最开始的示意图中的Selector,每个EventLoop和一个线程绑定,用于处理多个Channel。

任务调度

  1. 如果我们想实现延时任务的调度,比如连接成功5s之后发送一包数据,就可以用到EventLoop的计划任务

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    ctx.Channel.EventLoop.Schedule(() =>
    {
    Console.WriteLine("delay 1s");
    }, new TimeSpan(1000));
    // 如果需要提前取消,可以调用Cancel方法
    IScheduledTask task = ctx.Channel.EventLoop.Schedule(() =>
    {
    Console.WriteLine("delay 1s");
    }, new TimeSpan(1000));
    tsak.Cancel();
  2. 一个任务引发后,会判断当前是否在需要处理这个任务的EventLoop中(程序知道自己目前在执行哪个线程,线程又跟EventLoop对应),如果在就直接执行该任务,如果不在该任务中,则任务入队稍后处理

  3. 永远不要把一个需要耗费长时间的任务放到EventLoop执行队列来执行,需要使用我们前面介绍的EventExecutor的方法。

Group

许多Channel对应一个EventLoop,但是EventLoop能分配给她的Channel个数是有限的,要处理可以扩展的无数个Channel就需要EventLoopGroup。他们的结构关系如下图:

我们之前讲过,Netty不仅能够完成NIO系统的搭建,也能通过一些简单的配置,变成OIO阻塞IO系统,阻塞IO的话,就不能多个Channel共享一个EventLoop了,就需要一个Channel分配一个EventLoop。总的来说,EventLoop跟线程的关系是不会改变的。

需要注意的是:

  1. 给Channel分配EventLoop的是EventLoopGroup。而他将尽量均衡的将Channel进行分配。

DotNetty完全教程(六)

Excerpt

资源管理目的在处理数据的过程中,我们需要确保没有任何的资源泄漏。这时候我们就得很关心资源管理。引用计数的处理使用完ByteBuf之后,需要调整其引用计数以确保资源的释放内存内漏探测Netty提供了ResourceLeakDetector来检测内存泄漏,因为其是采样检测的,所以相关开销并不大。泄露日志检测级别手动释放消息ReferenceCountUtil.SafeRelea…

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

资源管理

目的

在处理数据的过程中,我们需要确保没有任何的资源泄漏。这时候我们就得很关心资源管理。

引用计数的处理

使用完ByteBuf之后,需要调整其引用计数以确保资源的释放

内存内漏探测

Netty提供了ResourceLeakDetector来检测内存泄漏,因为其是采样检测的,所以相关开销并不大。

  1. 泄露日志

  2. 检测级别

  3. 手动释放消息

    1
    ReferenceCountUtil.SafeRelease(this.Message);
  4. 分析SimpleChannelInboundHandler

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    public override void ChannelRead(IChannelHandlerContext ctx, object msg)
    {
    bool release = true;
    try
    {
    if (this.AcceptInboundMessage(msg))
    {
    I imsg = (I)msg;
    this.ChannelRead0(ctx, imsg);
    }
    else
    {
    release = false;
    ctx.FireChannelRead(msg);
    }
    }
    finally
    {
    if (autoRelease && release)
    {
    ReferenceCountUtil.Release(msg);
    }
    }
    }

由上面的源码可以看出,Read0事实上是Read的封装,区别就是Read0方法在调用的时候,消息一定是被释放了,这就是手动释放的例子。

DotNetty完全教程(十一)

Excerpt

编码器和解码器定义编码器负责将应用程序可以识别的数据结构转化为可传输的数据流,解码器反之。对于应用程序来说,编码器操作出站数据,解码器操作入站数据。解码器和Handler解码器因为是处理入站数据的,所以继承了ChannelInBoundHandler.我们理解的时候可以认为解码器就是一种特殊的Handler,用于处理信息。解码器的类型ByteToMessageDecoderRepl…

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

编码器和解码器

定义

编码器负责将应用程序可以识别的数据结构转化为可传输的数据流,解码器反之。对于应用程序来说,编码器操作出站数据,解码器操作入站数据。

解码器和Handler

解码器因为是处理入站数据的,所以继承了ChannelInBoundHandler.我们理解的时候可以认为解码器就是一种特殊的Handler,用于处理信息。

解码器的类型

  • ByteToMessageDecoder
  • ReplayingDecoder
  • MessageToMessageDecoder

解码器实例

ByteToMessageDecoder

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// ByteToMessageDecoder
public class ToIntDecoder : ByteToMessageDecoder
{
protected override void Decode(IChannelHandlerContext context, IByteBuffer input, List<object> output)
{
if (input.ReadableBytes >= 4) output.Add(input.ReadInt());
}
}
// 测试代码
[Fact]
public void TestIntDecoder()
{
IByteBuffer buf = Unpooled.Buffer();
for (int i = 0; i < 8; i++)
{
buf.WriteByte(i);
}
// 构建Channel
EmbeddedChannel channel = new EmbeddedChannel(new ToIntDecoder());
// 测试
Assert.True(channel.WriteInbound(buf));
Assert.True(channel.Finish());

// 比如 0 1 2 3
// 3*2^0+2*2^8+1*2^16+0*2^24
Assert.Equal(66051, channel.ReadInbound<int>());

}

ReplayingDecoder

1
2
3
4
5
6
7
8
9
10
11
12
// 不需要判断ReadableBytes的ReplayingDecoder
public class ToIntDecoder2 : ReplayingDecoder<int>
{
public ToIntDecoder2(int initialState) : base(initialState)
{
}

protected override void Decode(IChannelHandlerContext context, IByteBuffer input, List<object> output)
{
output.Add(input.ReadInt());
}
}

MessageToMessageDecoder

1
2
3
4
5
6
7
public class IntToStringDecoder : MessageToMessageDecoder<int>
{
protected override void Decode(IChannelHandlerContext context, int message, List<object> output)
{
output.Add(message.ToString());
}
}

更多解码器

  • LineBaseFrameDecoder 使用行尾控制符解析数据,可以把数据一行一行解析出来
  • HttpObjectDecoder HTTP解码器

编码器

根据我们之前的知识可以轻易的推导出,Encoder继承了ChannelOutBoundHandler

  • MessageToByteEncoder
  • MessageToMessageEncoder

编码器实例

MessageToByteEncoder

1
2
3
4
5
6
7
public class ShortToByteEncoder : MessageToByteEncoder<short>
{
protected override void Encode(IChannelHandlerContext context, short message, IByteBuffer output)
{
output.WriteShort(message);
}
}

MessageToMessageEncoder

1
2
3
4
5
6
7
public class IntToStringEncoder : MessageToMessageEncoder<int>
{
protected override void Encode(IChannelHandlerContext context, int message, List<object> output)
{
output.Add(message.ToString());
}
}

编解码器

MessageToMessageCodec,它拥有encode和decode两个方法,用于实现来回的转换数据,这种编解码器我们在后面实例的时候再举例说明。
这种编解码器可以把数据的转换,逆转换过程封装,但是同时他的缺点是,不如分开写重用方便。那我们就会想了,既然如此的话,为什么我们不能把一个编码器,一个解码器结合起来,作为一个编解码器呢?这样的话,编码器解码器分别可以重用,结合出来的编解码器也可以方便的使用 CombinedChannelDuplexHandler 就可以实现这样的作用。

1
2
3
4
5
// 提供结合的编解码器
public class CombinedCodec : CombinedChannelDuplexHandler<ToIntDecoder, ShortToByteEncoder>
{

}

DotNetty完全教程(十)

Excerpt

单元测试EmbeddedChannel介绍EmbeddedChannel是专门为了测试ChannelHandler的传输。我们先看一下他的API用一张图来描述这样的一个模拟过程编写基于xUnit的单元测试新建一个xUnit工程 UnitTest新建一个用于测试EmbededChannel的工程 EmbededChannelTestEmbededChannelTest工程需要引用…

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

单元测试

EmbeddedChannel介绍

EmbeddedChannel是专门为了测试ChannelHandler的传输。我们先看一下他的API

用一张图来描述这样的一个模拟过程

编写基于xUnit的单元测试

  1. 新建一个xUnit工程 UnitTest
  2. 新建一个用于测试EmbededChannel的工程 EmbededChannelTest
  3. EmbededChannelTest工程需要引用DotNetty的类库,这里因为我们需要测试一个解码器,所以除了原先的Buffer Common Transport之外我们还需要引用Codecs
  4. xUnit工程需要引用EmbededChannelTest工程
  5. 在EmbededChannelTest工程之下新建FixedLengthFrameDecoder待测试类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
using DotNetty.Buffers;
using DotNetty.Codecs;
using DotNetty.Transport.Channels;
using System;
using System.Collections.Generic;
using System.Text;

namespace EmbededChannelTest
{
public class FixedLengthFrameDecoder : ByteToMessageDecoder
{
private int _frameLength;
public FixedLengthFrameDecoder(int frameLength)
{
if (frameLength <= 0)
{
throw new Exception("不合法的参数。");
}
_frameLength = frameLength;
}
protected override void Decode(IChannelHandlerContext context, IByteBuffer input, List<object> output)
{
// 解码器实现固定的帧长度
while (input.ReadableBytes >= _frameLength)
{
IByteBuffer buf = input.ReadBytes(_frameLength);
output.Add(buf);
}
}
}
}

我们可以看到这个解码器将buffer中的字节流转化为每3个一帧。接下来我们需要编写测试类,我们在UnitTest工程下新建一个类,名字叫做UnitTester,编写如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
using DotNetty.Buffers;
using DotNetty.Transport.Channels.Embedded;
using EmbededChannelTest;
using System;
using System.Collections.Generic;
using System.Text;
using Xunit;

namespace UnitTest
{
public class UnitTester
{
[Fact]
public void testFrameDecoder()
{
IByteBuffer buf = Unpooled.Buffer();
for (int i = 0; i < 9; i++)
{
buf.WriteByte(i);
}
IByteBuffer input = buf.Duplicate();
EmbeddedChannel channel = new EmbeddedChannel(new FixedLengthFrameDecoder(3));
// 写数据
// retain能够将buffer的引用计数加1,并且返回这个buffer本身
Assert.True(channel.WriteInbound(input.Retain()));
Assert.True(channel.Finish());
// 读数据
IByteBuffer read = channel.ReadInbound<IByteBuffer>();
Assert.Equal(buf.ReadSlice(3), read);
read.Release();

read = channel.ReadInbound<IByteBuffer>();
Assert.Equal(buf.ReadSlice(3), read);
read.Release();

read = channel.ReadInbound<IByteBuffer>();
Assert.Equal(buf.ReadSlice(3), read);
read.Release();

Assert.Null(channel.ReadInbound<object>());
buf.Release();
}
}
}

编写完成之后直接右键点击运行测试即可。同理我们可以测试用于出站数据的Encoder,这里不贴代码了,感兴趣的可以去工程中自己看源码进行学习。

DotNetty完全教程(四)

Excerpt

ByteBufferNetty中ByteBuffer的介绍Netty 的数据处理 API 通过两个组件暴露——abstract class ByteBuf 和 interfaceByteBufHolderDotNetty中有AbstractByteBuffer IByteBuffer IByteBufferHolder优点:它可以被用户自定义的缓冲区类型扩展;通过内置的复合缓冲区…


ByteBuffer

Netty中ByteBuffer的介绍

Netty 的数据处理 API 通过两个组件暴露——abstract class ByteBuf 和 interface
ByteBufHolder

DotNetty中有AbstractByteBuffer IByteBuffer IByteBufferHolder

优点:

  • 它可以被用户自定义的缓冲区类型扩展;
  • 通过内置的复合缓冲区类型实现了透明的零拷贝;
  • 容量可以按需增长(类似于 JDK 的 StringBuilder);
  • 在读和写这两种模式之间切换不需要调用 ByteBuffer 的 flip()方法;
  • 读和写使用了不同的索引;
  • 支持方法的链式调用;
  • 支持引用计数;
  • 支持池化

原理:

每一个ByteBuf都有两个索引,读索引和写索引,read和write会移动索引,set和get不会引动索引。

使用ByteBuf

  1. 堆缓冲区(使用数组的方式展示和操作数据)

使用支撑数组给ByteBuf提供快速的分配和释放的能力。适用于有遗留数据需要处理的情况。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public override void ChannelRead(IChannelHandlerContext ctx, object msg)
{
IByteBuffer message = msg as IByteBuffer;
// 检查是否有支撑数组
if (message.HasArray)
{
// 获取数组
byte[] array = message.Array;
// 计算第一个字节的偏移
int offset = message.ArrayOffset + message.ReaderIndex;
// 获取可读字节数
int length = message.ReadableBytes;
// 调用方法,处理数据
HandleArray(array, offset, length);
}
Console.WriteLine("收到信息:" + message.ToString(Encoding.UTF8));
ctx.WriteAsync(message);
}
  1. 直接缓冲区
1
2
3
4
5
6
7
8
9
10
11
12
13
public override void ChannelRead(IChannelHandlerContext ctx, object msg)
{
IByteBuffer message = msg as IByteBuffer;
if (message.HasArray)
{
int length = message.ReadableBytes;
byte[] array = new byte[length];
message.GetBytes(message.ReaderIndex, array);
HandleArray(array, 0, length);
}
Console.WriteLine("收到信息:" + message.ToString(Encoding.UTF8));
ctx.WriteAsync(message);
}
  1. CompositeByteBuffer 复合缓冲区

如果要发送的命令是由两个ByteBuf拼接构成的,那么就需要复合缓冲区,比如Http协议中一个数据流由头跟内容构成这样的逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public override void ChannelRead(IChannelHandlerContext ctx, object msg)
{
IByteBuffer message = msg as IByteBuffer;
// 创建一个复合缓冲区
CompositeByteBuffer messageBuf = Unpooled.CompositeBuffer();
// 创建两个ByteBuffer
IByteBuffer headBuf = Unpooled.CopiedBuffer(message);
IByteBuffer bodyBuf = Unpooled.CopiedBuffer(message);
// 添加到符合缓冲区中
messageBuf.AddComponents(headBuf, bodyBuf);
// 删除
messageBuf.RemoveComponent(0);

Console.WriteLine("收到信息:" + message.ToString(Encoding.UTF8));
ctx.WriteAsync(message);
}

字节级操作

  1. 读取(不移动索引)
1
2
3
4
5
6
7
8
9
10
11
12
13
public override void ChannelRead(IChannelHandlerContext ctx, object msg)
{
IByteBuffer message = msg as IByteBuffer;
for (int i = 0; i < message.Capacity; i++)
{
// 如此使用索引访问不会改变读索引也不会改变写索引
byte b = message.GetByte(i);
Console.WriteLine(b);
}

Console.WriteLine("收到信息:" + message.ToString(Encoding.UTF8));
ctx.WriteAsync(message);
}
  1. 丢弃可丢弃字节
    所谓可丢弃字节就是调用read方法之后,readindex已经移动过了的区域,这段区域的字节称为可丢弃字节。
1
message.DiscardReadBytes();

只有在内存十分宝贵需要清理的时候再调用这个方法,随便调用有可能会造成内存的复制,降低效率。
3. 读取所有可读字节(移动读索引)

1
2
3
4
while (message.IsReadable())
{
Console.WriteLine(message.ReadByte());
}
  1. 写入数据
1
2
3
4
5
// 使用随机数填充可写区域
while (message.WritableBytes > 4)
{
message.WriteInt(new Random().Next(0, 100));
}
  1. 管理索引
  • MarkReaderIndex ResetReaderIndex 标记和恢复读索引
  • MarkWriterIndex ResetWriterIndex 标记和恢复写索引
  • SetReaderIndex(int) SetWriterIndex(int) 直接移动索引
  • clear() 重置两个索引都为0,但是不会清除内容
  1. 查找
  • IndexOf()
  • 使用Processor
1
2
// 查找\r
message.ForEachByte(ByteProcessor.FindCR);
  1. 派生

派生的意思是创建一个新的ByteBuffer,这个ByteBuf派生于其他的ByteBuf,派生出来的子ByteBuf具有自己的读写索引,但是本质上指向同一个对象,这样就导致了改变一个,另一个也会改变。

  • duplicate();
  • slice();
  • slice(int, int);
  • Unpooled.unmodifiableBuffer(…);
  • order(ByteOrder);
  • readSlice(int)。
  1. 复制
    复制不同于派生,会复制出一个独立的ByteBuf,修改其中一个不会改变另一个。
  • copy
  1. 释放
1
2
// 显式丢弃消息
ReferenceCountUtil.release(msg);
  1. 增加引用计数防止释放
1
ReferenceCountUtil.retain(message)
  1. 其他api
    在这里插入图片描述

ByteBufHolder

  1. 目的
    再数据处理的过程中不仅仅有字节数据内容本身,还会有一些附加信息,比如HTTP响应的状态码,Cookie等。给ByteBuf附加信息就要用到ByteBufHolder.
  2. API

管理ByteBuffer

  1. 按需分配 ByteBufAllocator

    注意分配是池化的,最大程度上降低分配和释放内存的开销。

    1
    2
    3
    4
    5
    6
    7
    // 获取Allocator
    // 1
    IChannelHandlerContext ctx = null;
    IByteBufferAllocator allocator = ctx.Allocator;
    // 2
    IChannel channel = null;
    allocator = channel.Allocator;

有两种ByteBufAllocator的实现:PooledByteBufAllocator和UnpooledByteBufAllocator,前者池化了ByteBuf的实例,极大限度的提升了性能减少了内存碎片。
2. Unpooled缓冲区
获取不到 ByteBufAllocator的引用的时候我们可以使用Unpooled工具类来操作ByteBuf。
在这里插入图片描述

  1. ByteBufUtil
    这个类提供了一些通用的API,都是静态的辅助方法,例如hexdump方法可以以十六进制的方式打印ByteBuf的内容。还有equal方法判断bytebuf是否相等。

引用计数

  1. 目的

    ByteBuf和ByteBufHolder都有计数的机制。引用计数都从1开始,如果计数大于0则不被释放,如果等于0就会被释放。它的目的是为了支持池化的实现,降低了内存分配的开销。

  2. 异常

    如果访问一个计数为0的对象就会引发IllegalReferenceCountException。

DotNetty系列三:编码解码器,IdleStateHandler心跳机制

Excerpt

在上一节基础上,实现编码解码器。1.创建一个类库项目。用于实现编码解码器。编码器: public class CommonServerEncoder : MessageToByteEncoder<string> { protected override void Encode(IChannelHandlerContext context, s…


在上一节基础上,实现编码解码器。

1.创建一个类库项目。用于实现编码解码器。

编码器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class CommonServerEncoder : MessageToByteEncoder<string>    
{
protected override void Encode(IChannelHandlerContext context, string message, IByteBuffer output)
{
byte[] messageBytes = Encoding.UTF8.GetBytes(message);
IByteBuffer initialMessage = Unpooled.Buffer(messageBytes.Length);
initialMessage.WriteBytes(messageBytes);
output.WriteBytes(initialMessage);
}
}

public class CommonClientEncoder : MessageToByteEncoder<string>
{
protected override void Encode(IChannelHandlerContext context, string message, IByteBuffer output)
{
byte[] messageBytes = Encoding.UTF8.GetBytes(message);
IByteBuffer initialMessage = Unpooled.Buffer(messageBytes.Length);
initialMessage.WriteBytes(messageBytes);
output.WriteBytes(initialMessage);
}
}

解码器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class CommonServerDecoder : ByteToMessageDecoder    
{
protected override void Decode(IChannelHandlerContext context, IByteBuffer input, List<object> output)
{
byte[] array = new byte[input.ReadableBytes];
input.GetBytes(input.ReaderIndex, array, 0, input.ReadableBytes);
input.Clear();
output.Add(array);
}
}

public class CommonClientDecoder : ByteToMessageDecoder
{
protected override void Decode(IChannelHandlerContext context, IByteBuffer input, List<object> output)
{
byte[] array = new byte[input.ReadableBytes];
input.GetBytes(input.ReaderIndex, array, 0, input.ReadableBytes);
input.Clear();
output.Add(array);
}
}

2.服务端里添加:

                        //配置编码解码器
                        pipeline.AddLast(new CommonServerEncoder());
                        pipeline.AddLast(new CommonServerDecoder());

客户端里添加:

                        //配置编码解码器
                        pipeline.AddLast(new CommonClientEncoder());
                        pipeline.AddLast(new CommonClientDecoder());

3.服务端接收和发送:

1
2
3
4
5
6
7
8
9
public override void ChannelRead(IChannelHandlerContext context, object message)        
{
if (message is byte[] o)
{
Console.WriteLine($"解码器方式,从客户端接收:{Encoding.UTF8.GetString(o)}:{DateTime.Now}");
}
string msg = "服务端从客户端接收到内容后返回,我是服务端";
context.WriteAsync(msg);
}

客户端接收和发送:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public override void ChannelActive(IChannelHandlerContext context)        
{
Console.WriteLine("我是客户端.");
Console.WriteLine($"连接至服务端{context}.");
string message = "客户端1";
context.WriteAndFlushAsync(message);
}

public override void ChannelRead(IChannelHandlerContext context, object message)
{
if (message is byte[] o)
{
Console.WriteLine($"解码器方式,从服务端接收:{Encoding.UTF8.GetString(o)}:{DateTime.Now}");
}
}

实现了上一节一样的效果。

4.IdleStateHandler心跳机制:

4.1服务端添加IdleStateHandler心跳检测处理器,添加自定义处理Handler类实现userEventTriggered()方法作为超时事件的逻辑处理.

IdleStateHandler心跳检测每十五秒进行一次读检测,如果十五秒内ChannelRead()方法未被调用则触发一次userEventTrigger()方法.

                        // IdleStateHandler 心跳
                        //服务端为读IDLE
                        pipeline.AddLast(new IdleStateHandler(15, 0, 0));//第一个参数为读,第二个为写,第三个为读写全部

4.2服务端Handler重载UserEventTriggered:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private int lossConnectCount = 0;
public override void UserEventTriggered(IChannelHandlerContext context, object evt) {
Console.WriteLine("已经15秒未收到客户端的消息了!");
if (evt is IdleStateEvent eventState)
{
if (eventState.State == IdleState.ReaderIdle)
{
lossConnectCount++;if (lossConnectCount > 2)
{
Console.WriteLine("关闭这个不活跃通道!");
context.CloseAsync();
} }
}
else
{
base.UserEventTriggered(context, evt);
}
}

接收部分改为判断心跳:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public override void ChannelRead(IChannelHandlerContext context, object message)        
{
if (message is byte[] o)
{
Console.WriteLine($"解码器方式,从客户端接收:{Encoding.UTF8.GetString(o)}:{DateTime.Now}");
if (Encoding.UTF8.GetString(o).Contains("biubiu:"))
{
string temp = "服务端接收到心跳连接";
context.WriteAsync(temp);return;
}
}
string msg = "服务端从客户端接收到内容后返回,我是服务端";
context.WriteAsync(msg);
}

4.3客户端添加IdleStateHandler心跳检测处理器,并添加自定义处理Handler类实现userEventTriggered()方法作为超时事件的逻辑处理;

设定IdleStateHandler心跳检测每十秒进行一次写检测,如果十秒内write()方法未被调用则触发一次userEventTrigger()方法,实现客户端每十秒向服务端发送一次消息;

                        // IdleStateHandler 心跳
                        //客户端为写IDLE
                        pipeline.AddLast(new IdleStateHandler(0, 10, 0));//第一个参数为读,第二个为写,第三个为读写全部

4.4客户端Handler重载UserEventTriggered:

1
2
3
4
5
6
7
8
9
10
public override void UserEventTriggered(IChannelHandlerContext context, object evt)        {           
Console.WriteLine("客户端循环心跳监测发送: " + DateTime.Now);
if (evt is IdleStateEvent eventState)
{
if (eventState.State == IdleState.WriterIdle)
{
context.WriteAndFlushAsync($"biubiu:{DateTime.Now}");
}
}
}

4.5实现效果:

5.群发:将客户端上下线通知,群发至所有客户端。只在服务端修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
static volatile IChannelGroup groups;
public override void HandlerAdded(IChannelHandlerContext context)
{
Console.WriteLine($"客户端{context}上线.");

base.HandlerAdded(context);
IChannelGroup g = groups;if (g == null)
{
lock (this)
{
if (groups == null)
{
g = groups = new DefaultChannelGroup(context.Executor);
}
}
}
g.Add(context.Channel);
groups.WriteAndFlushAsync($"欢迎{context.Channel.RemoteAddress}加入.");
}

public override void HandlerRemoved(IChannelHandlerContext context)
{
Console.WriteLine($"客户端{context}下线.");
base.HandlerRemoved(context);
groups.Remove(context.Channel);
groups.WriteAndFlushAsync($"恭送{context.Channel.RemoteAddress}离开.");
}

实现效果:

项目下载地址:项目下载

原文参考:CentOS 8 都发布了,你还不会用 nftables?

一、引言:为什么是 nftables?

随着 CentOS 8 和 RHEL 8 的发布,一个重要的变化是:**nftables 框架已取代 iptables 成为默认的网络包过滤工具**。如果你还在使用 iptables,现在是时候了解它的继任者了。

nftables 是什么?
它是 netfilter 项目推出的新框架,旨在全面替代现有的 {ip,ip6,arp,eb}tables。它提供了一个全新的包过滤框架、用户空间工具 nft 以及向后兼容层。

核心优势对比 iptables:

  • 数据结构:iptables 使用数组式布局,nftables 使用链式布局(链表),更灵活。
  • 可扩展性:iptables 功能扩展需修改内核;nftables 大部分工作在用户态完成,添加功能更容易。
  • 性能:iptables 存在内置链和计数器,有空链开销;nftables 无内置链,按需创建。
  • 易用性:原生支持集合、字典和映射,简化了 IPv4/IPv6 双栈管理。

本文主要介绍用户空间命令行工具 nft 的使用方法。

二、基础概念与快速开始

2.1 核心组件

nftables 由三个核心层级构成:

  1. 内核:提供 netlink 配置接口和规则集评估。
  2. libnl:提供与内核通信的基础库。
  3. **nft (用户空间)**:供用户交互的命令行工具。

2.2 规则结构

与 iptables 类似,nftables 也由 表(Table) -> 链(Chain) -> 规则(Rule) 层级构成。规则是真正的执行动作。

检查当前规则集:

1
nft list ruleset

初始状态下,如果没有启用 firewalld 等服务,输出为空,这印证了 nftables 没有内置链的特性。

三、实战操作:从创建到管理

3.1 创建表(Table)

表是规则的顶级容器,每个表只属于一个特定的“地址簇”。

nftables 地址簇 对应的传统工具 说明
ip iptables 仅处理 IPv4 数据包
ip6 ip6tables 仅处理 IPv6 数据包
inet iptables & ip6tables 处理 IPv4 和 IPv6(推荐)
arp arptables 处理 ARP 层数据包
bridge ebtables 处理桥接数据包

创建表示例(使用 inet 簇):

1
nft add table inet my_table

列出所有规则以验证:

1
2
3
4
nft list ruleset
# 输出:
# table inet my_table {
# }

3.2 创建链(Chain)

链是规则的载体,必须显式创建。链有两种类型:

  • 常规链:用于规则分类和跳转,无需指定钩子。
  • 基本链:数据包的入口点,必须指定钩子类型和优先级。

创建常规链:

1
nft add chain inet my_table my_utility_chain

创建基本链(作为数据包入口):

1
nft add chain inet my_table my_filter_chain '{ type filter hook input priority 0; }'
  • type filter:链类型为过滤。
  • hook input:钩子点为输入。
  • priority 0:优先级(数值越小,优先级越高)。
  • 注意:分号 ; 需要用反斜杠 \ 转义,或直接用引号包裹整个参数。

3.3 创建规则(Rule)

规则由语句构成,放置在链中。

在链末尾添加规则(允许 SSH):

1
nft add rule inet my_table my_filter_chain tcp dport ssh accept

在链开头插入规则(允许 HTTP):

1
nft insert rule inet my_table my_filter_chain tcp dport http accept

在指定位置插入规则:
有两种方式,推荐使用稳定的 handle 而非易变的 index

  1. 使用 index(从0开始):
    1
    2
    # 在索引1的规则前插入(即第二条规则前)
    nft insert rule inet my_table my_filter_chain index 1 tcp dport nfs accept
  2. 使用 handle(句柄值唯一且不变):
    1
    2
    3
    4
    5
    # 首先查看规则的句柄
    nft --handle list ruleset
    # 假设 `tcp dport http accept` 的句柄是 4
    # 在该句柄的规则后添加新规则
    nft add rule inet my_table my_filter_chain handle 4 tcp dport 2345 accept
    创建规则时获取句柄:
    1
    2
    nft --echo --handle add rule inet my_table my_filter_chain udp dport 3333 accept
    # 输出会包含新规则的句柄,例如 `# handle 10`

3.4 删除规则

规则只能通过其唯一的 handle 删除。

1
2
3
4
# 1. 查找要删除规则的句柄
nft --handle list ruleset
# 2. 使用句柄删除
nft delete rule inet my_table my_filter_chain handle 8

3.5 查看规则

支持不同粒度的查看。

1
2
3
4
5
6
# 查看所有规则
nft list ruleset
# 查看特定表
nft list table inet my_table
# 查看特定链
nft list chain inet my_table my_filter_chain

四、高级特性:集合与字典

4.1 集合(Sets)

集合用于匹配多个条件(如多个IP、端口),分为匿名集合和命名集合。

匿名集合(适用于固定条件):

1
2
3
4
# 允许来自两个特定IP的流量
nft add rule inet my_table my_filter_chain ip saddr { 10.10.10.123, 10.10.10.231 } accept
# 简化多个端口的规则
nft add rule inet my_table my_filter_chain tcp dport { http, nfs, ssh } accept

命名集合(可动态修改):

  1. 创建集合(指定元素类型):
    1
    nft add set inet my_table my_set '{ type ipv4_addr; }'
  2. 在规则中引用集合(使用 @ 符号):
    1
    nft add rule inet my_table my_filter_chain ip saddr @my_set drop
  3. 向集合添加/删除元素
    1
    2
    nft add element inet my_table my_set { 10.10.10.22, 10.10.10.33 }
    nft delete element inet my_table my_set { 10.10.10.33 }

支持区间的集合(需添加 interval 标志):

1
2
3
4
nft add set inet my_table my_range_set '{ type ipv4_addr; flags interval; }'
nft add element inet my_table my_range_set { 10.20.20.0/24 }
# 或直接使用区间
# nft add element inet my_table my_range_set { 10.20.20.0-10.20.20.255 }

级联类型集合(组合匹配):

1
2
3
4
5
6
# 创建可同时匹配IP、协议、端口的集合
nft add set inet my_table my_concat_set '{ type ipv4_addr . inet_proto . inet_service; }'
# 添加元素
nft add element inet my_table my_concat_set { 10.30.30.30 . tcp . telnet }
# 在规则中引用,需指明对应关系
nft add rule inet my_table my_filter_chain ip saddr . meta l4proto . tcp dport @my_concat_set accept

4.2 字典(Maps)

字典提供高效的键值映射,用于根据条件跳转到不同链或执行不同动作,避免链式跳转的性能开销。

创建字典并用于规则跳转:

1
2
3
4
5
# 1. 创建目标链
nft add chain inet my_table my_tcp_chain
nft add chain inet my_table my_udp_chain
# 2. 使用匿名字典根据协议跳转
nft add rule inet my_table my_filter_chain meta l4proto vmap { tcp : jump my_tcp_chain, udp : jump my_udp_chain }

命名字典:

1
2
3
4
5
6
# 1. 创建字典(键类型: 值类型)
nft add map inet my_table my_vmap '{ type ipv4_addr : verdict; }'
# 2. 添加映射关系
nft add element inet my_table my_vmap { 192.168.0.10 : drop, 192.168.0.11 : accept }
# 3. 在规则中引用字典
nft add rule inet my_table my_filter_chain ip saddr vmap @my_vmap

五、架构设计与命名空间

nftables 中,每个表都是一个独立的命名空间。这意味着不同表中的链、集合可以同名,互不干扰。

1
2
3
4
nft add table inet table_one
nft add chain inet table_one my_chain
nft add table inet table_two
nft add chain inet table_two my_chain # 同名,但允许

重要提示:数据包必须通过所有表中相关链的允许,才能被放行。链的执行顺序由 priority 决定,值越小优先级越高。

六、持久化配置

命令行配置是临时的。要使规则永久生效,需进行备份和恢复。

备份当前规则集:

1
nft list ruleset > /root/nftables.conf

从文件恢复规则集:

1
nft -f /root/nftables.conf

在 CentOS 8/RHEL 8 中:

  • 系统服务 nftables.service 默认从 /etc/nftables.conf 加载规则。
  • 该文件通常会 include 其他配置文件,如 /etc/sysconfig/nftables.conf(默认被注释)。

七、总结

nftables 作为 iptables 的现代替代品,通过更清晰的数据结构、原生集合/字典支持、独立的命名空间等特性,提供了更强大、灵活且高效的防火墙配置体验。

学习路径建议:

  1. 理解 表 -> 链 -> 规则 的核心层级。
  2. 掌握基础的 增(add/insert)、删(delete)、查(list) 操作。
  3. 熟练使用 集合 来简化多条件规则。
  4. 在复杂策略中应用 字典 进行高效跳转。
  5. 最后通过 备份/恢复 实现配置持久化。

问题描述

需要使用MSBuild将Tools文件夹及其所有子文件夹和文件递归复制到应用程序的输出目录(如Debug文件夹)中。

目录结构示例:

1
2
3
4
5
{ProjectName}
├── Source
└── Tools
└── Viewer
└── {约5个子目录}

解决方案

方案一:使用Copy任务(推荐)

通过RecursiveDir元数据保持目录结构:

1
2
3
4
5
6
7
8
<Target Name="AfterBuild">
<ItemGroup>
<Viewer Include="..\$(ApplicationDirectory)\Tools\viewer\**\*.*" />
</ItemGroup>
<Copy SourceFiles="@(Viewer)"
DestinationFolder="$(TargetDir)\%(RecursiveDir)"
SkipUnchangedFiles="true" />
</Target>

关键点

  • 使用%(RecursiveDir)保留源目录结构
  • SkipUnchangedFiles="true"避免重复复制
  • 建议在AfterBuild阶段执行(确保构建完成)

方案二:完整项目配置示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">
<PropertyGroup>
<YourDestinationDirectory>..\SomeDestinationDirectory</YourDestinationDirectory>
<YourSourceDirectory>..\SomeSourceDirectory</YourSourceDirectory>
</PropertyGroup>

<Target Name="BeforeBuild">
<CreateItem Include="$(YourSourceDirectory)\**\*.*">
<Output TaskParameter="Include" ItemName="YourFilesToCopy" />
</CreateItem>

<Copy SourceFiles="@(YourFilesToCopy)"
DestinationFiles="@(YourFilesToCopy->'$(YourDestinationDirectory)\%(RecursiveDir)%(Filename)%(Extension)')" />
</Target>
</Project>

方案三:使用Exec调用xcopy(简单场景)

1
2
3
4
<Target Name="CopyToDeployFolder" DependsOnTargets="CompileWebSite">
<Exec Command="xcopy.exe $(OutputDirectory) $(DeploymentDirectory) /e"
WorkingDirectory="C:\Windows\" />
</Target>

注意:此方法依赖系统命令,跨平台兼容性差


常见错误排查

1. 路径问题

  • 确保$(ApplicationDirectory)$(OutputPath)已正确定义
  • 验证源路径是否存在:..\$(ApplicationDirectory)\Tools\viewer\

2. 目标路径格式

错误写法:

1
DestinationFolder="@(Viewer->'$(OutputPath)\\Tools')"

正确写法应使用RecursiveDir

1
DestinationFolder="$(TargetDir)\%(RecursiveDir)"

3. 文件匹配模式

  • 使用\**\*.*递归匹配所有文件
  • 确保包含隐藏文件(如需要):\**\*

最佳实践

  1. 使用标准属性

    • $(TargetDir) 替代 $(OutputPath)
    • $(ProjectDir) 获取项目根目录
  2. 增量构建优化

    1
    2
    3
    <Copy SourceFiles="@(Files)" 
    DestinationFiles="@(Files->'$(Dest)\%(RecursiveDir)%(Filename)%(Extension)')"
    SkipUnchangedFiles="true" />
  3. 错误处理

    1
    <Copy ... ContinueOnError="WarnAndContinue" />

官方参考

Linux常用命令

Linux的命令确实非常多,然而熟悉Linux的人从来不会因为Linux的命令太多而烦恼。因为我们仅仅只需要掌握常用命令,就完全可以驾驭Linux。

接下来,让我们一起来看看都有那些常用的Linux命令吧!

一、文件目录操作

1. ls命令

ls命令不仅可以查看Linux文件夹包含的文件,而且可以查看文件权限(包括目录、文件夹、文件权限)和目录信息等。

命令格式

1
ls [选项] [目录]

常用参数

  • -l:列出长数据串,包含文件的属性与权限数据等
  • -a:列出全部的文件,连同隐藏文件(开头为.的文件)一起列出来(常用)
  • -d:仅列出目录本身,而不是列出目录的文件数据
  • -h:将文件容量以较易读的方式(GB,kB等)列出来
  • -R:连同子目录的内容一起列出(递归列出),等于该目录下的所有文件都会显示出来

使用实例

  1. 列出home目录下的所有文件和目录的详细资料

    1
    2
    ls -a -l /home
    ls -al /home
  2. 列出当前目录下所有以”d”开头的文件目录详情内容

    1
    ls -l d*

2. cd命令

最基本的命令语句,其他命令语句要进行操作,都是建立在使用cd命令上的。用于切换当前目录至指定目录。

命令格式

1
cd [目录名]

操作案例

  1. 从当前目录进入系统根目录

    1
    cd /
  2. 跳转到home/Code目录

    1
    cd /home/Code

3. pwd命令

查看”当前工作目录”的完整路径。

命令格式

1
pwd [选项]

常用参数

  • -P:显示实际物理路径,而非使用连接(link)路径
  • -L:当目录为连接路径时,显示连接路径

操作案例

  1. 显示当前所在路径
    1
    pwd

4. mkdir命令

用来创建指定名称的目录,要求创建目录的用户在当前目录中具有写权限,并且指定的目录名不能是当前目录中已有的目录。

命令格式

1
mkdir [选项] 目录

常用参数

  • -m, --mode=模式:设定权限<模式>(类似chmod),而不是rwxrwxrwx减umask
  • -p, --parents:可以是一个路径名称。此时若路径中的某些目录尚不存在,加上此选项后,系统将自动建立好那些尚不存在的目录,即一次可以建立多个目录
  • -v, --verbose:每次创建新目录都显示信息
  • --help:显示此帮助信息并退出
  • --version:输出版本信息并退出

使用实例

  1. 创建一个空目录

    1
    mkdir test
  2. 递归创建多个目录

    1
    mkdir -p test/test1
  3. 创建权限为777的目录

    1
    mkdir -m 777 test2
  4. 创建目录都显示信息

    1
    mkdir -v test4

5. rm命令

删除一个目录中的一个或多个文件或目录,如果没有使用-r选项,则rm不会删除目录。如果使用rm来删除文件,通常仍可以将该文件恢复原状。

命令格式

1
rm [选项] 文件

常用参数

  • -f, --force:忽略不存在的文件,从不给出提示
  • -i, --interactive:进行交互式删除
  • -r, -R, --recursive:指示rm将参数中列出的全部目录和子目录均递归地删除
  • -v, --verbose:详细显示进行的步骤
  • --help:显示此帮助信息并退出
  • --version:输出版本信息并退出

使用实例

  1. 删除文件test.txt,系统会提示是否删除

    1
    rm test.txt
  2. 强制删除test.txt,系统不再提示

    1
    rm -f test.txt
  3. 将test子目录及目录中所有档案删除

    1
    rm -r test

6. rmdir命令

该命令从一个目录中删除一个或多个子目录项,删除某目录时也必须具有对父目录的写权限。

命令格式

1
rmdir [选项] 目录

常用参数

  • -p:递归删除目录dirname,当子目录删除后其父目录为空时,也一同被删除。如果整个路径被删除或者由于某种原因保留部分路径,则系统在标准输出上显示相应的信息
  • -v, --verbose:显示指令执行过程

使用实例

  1. 删除空目录test1,非空目录无法删除

    1
    rmdir test1
  2. 当子目录被删除后使它也成为空目录的话,则顺便一并删除

    1
    rmdir -p test2  # test目录下仅有test2

7. mv命令

可以用来移动文件或者将文件改名(move (rename) files)。当第二个参数类型是文件时,mv命令完成文件重命名。当第二个参数是已存在的目录名称时,源文件或目录参数可以有多个,mv命令将各参数指定的源文件均移至目标目录中。

命令格式

1
mv [选项] 源文件或目录 目标文件或目录

常用参数

  • -b:若需覆盖文件,则覆盖前先行备份
  • -f:force强制的意思,如果目标文件已经存在,不会询问而直接覆盖
  • -i:若目标文件(destination)已经存在时,就会询问是否覆盖
  • -u:若目标文件已经存在,且source比较新,才会更新(update)
  • -t--target-directory=DIRECTORY move all SOURCE arguments into DIRECTORY,即指定mv的目标目录,该选项适用于移动多个源文件到一个目录的情况,此时目标目录在前,源文件在后

使用实例

  1. 将test1.txt重命名为test2.txt

    1
    mv test1.txt test2.txt
  2. 移动文件test1.txt到目录test2

    1
    mv test1.txt test2
  3. 将文件test1.txt、test2.txt、test3.txt移动到目录test3

    1
    mv test1.txt test2.txt test3.txt test3

8. cp命令

将源文件复制至目标文件,或将多个源文件复制至目标目录。

命令格式

1
2
3
cp [选项] 源文件 目录

cp [选项] -t 目录 源文件

常用参数

  • -t --target-directory:指定目标目录
  • -i --interactive:覆盖前询问(使前面的-n选项失效)
  • -n --no-clobber:不要覆盖已存在的文件(使前面的-i选项失效)
  • -f --force:强行复制文件或目录,不论目的文件或目录是否已经存在
  • -u --update:使用这项参数之后,只会在源文件的修改时间较目的文件更新时,或是对应的目的文件并不存在,才复制文件

使用实例

  1. 复制文件test1.txt到test1目录

    1
    cp test1.txt test1  # 若文件存在,会提示是否覆盖。若不存在直接完成复制
  2. 复制test1整个目录到test2

    1
    cp -a test1 test2

9. touch命令

touch命令参数可更改文档或目录的日期时间,包括存取时间和更改时间。

命令格式

1
touch [选项] 文件

常用参数

  • -a--time=atime--time=access--time=use:只更改存取时间
  • -c--no-create:不建立任何文档
  • -d:使用指定的日期时间,而非现在的时间
  • -f:此参数将忽略不予处理,仅负责解决BSD版本touch指令的兼容性问题
  • -m--time=mtime--time=modify:只更改变动时间
  • -r:把指定文档或目录的日期时间,统统设成和参考文档或目录的日期时间相同
  • -t:使用指定的日期时间,而非现在的时间

使用实例

  1. 创建不存在的文件test.txt

    1
    touch test.txt
  2. 更新test.txt的时间戳和test1.txt时间戳相同

    1
    touch -r test.txt test1.txt

10. cat命令

用来显示文件内容,或者将几个文件连接起来显示,或者从标准输入读取内容并显示,它常与重定向符号配合使用。

命令格式

1
cat [选项] [文件]

常用参数

  • -A, --show-all:等价于-vET
  • -b, --number-nonblank:对非空输出行编号
  • -e:等价于-vE
  • -E, --show-ends:在每行结束处显示$
  • -n, --number:对输出的所有行编号,由1开始对所有输出的行数编号
  • -s, --squeeze-blank:有连续两行以上的空白行,就代换为一行的空白行
  • -t:与-vT等价
  • -T, --show-tabs:将跳格字符显示为^I
  • -u:(被忽略)
  • -v, --show-nonprinting:使用^M-引用,除了LFD和TAB之外

使用实例

  1. 把test.log的文件内容加上行号后输入test1.log这个文件里

    1
    cat -n test.log > test1.log
  2. 将test.log的文件内容反向显示

    1
    tac test.log

11. nl命令

输出的文件内容自动的加上行号!其默认的结果与cat -n有点不太一样,nl可以将行号做比较多的显示设计,包括位数与是否自动补齐0等等的功能。

命令格式

1
nl [选项] [文件]

常用参数

  • -b:指定行号指定的方式,主要有两种:
    • -b a:表示不论是否为空行,也同样列出行号(类似cat -n
    • -b t:如果有空行,空的那一行不要列出行号(默认值)
  • -n:列出行号表示的方法,主要有三种:
    • -n ln:行号在萤幕的最左方显示
    • -n rn:行号在自己栏位的最右方显示,且不加0
    • -n rz:行号在自己栏位的最右方显示,且加0
  • -w:行号栏位的占用的位数

使用实例

  1. 用nl列出test.log的内容

    1
    nl test.log
  2. 用nl列出test.log的内容,空本行也加上行号

    1
    nl -b a test.log

12. more命令

more命令和cat的功能一样都是查看文件里的内容,但有所不同的是more可以按页来查看文件的内容,还支持直接跳转行等功能。

命令格式

1
more [-dlfpcsu] [-num] [+/pattern] [+linenum] [file...]

常用参数

  • +n:从第n行开始显示
  • -n:定义屏幕大小为n行
  • +/pattern:在每个档案显示前搜寻该字串(pattern),然后从该字串前两行之后开始显示
  • -c:从顶部清屏,然后显示
  • -d:提示”Press space to continue,’q’ to quit(按空格键继续,按q键退出)”,禁用响铃功能
  • -l:忽略Ctrl+l(换页)字符
  • -p:通过清除窗口而不是滚屏来对文件进行换页,与-c选项相似
  • -s:把连续的多个空行显示为一行
  • -u:把文件内容中的下画线去掉

操作指令

  • Enter:向下n行,需要定义。默认为1行
  • Ctrl+F:向下滚动一屏
  • 空格键:向下滚动一屏
  • Ctrl+B:返回上一屏
  • =:输出当前行的行号
  • :f:输出文件名和当前行的行号
  • V:调用vi编辑器
  • !命令:调用Shell,并执行命令
  • q:退出more

使用实例

  1. 显示文件test.log第3行起内容

    1
    more +3 test.log
  2. 从文件test.log查找第一个出现”day3”字符串的行,并从该处前2行开始显示输出

    1
    more +/day3 test.log
  3. 设置每屏显示行数

    1
    more -5 test.log

13. less命令

less与more类似,但使用less可以随意浏览文件,而more仅能向前移动,却不能向后移动,而且less在查看之前不会加载整个文件。

命令格式

1
less [参数] 文件

常用参数

  • -b <缓冲区大小>:设置缓冲区的大小
  • -e:当文件显示结束后,自动离开
  • -f:强迫打开特殊文件,例如外围设备代号、目录和二进制文件
  • -g:只标志最后搜索的关键词
  • -i:忽略搜索时的大小写
  • -m:显示类似more命令的百分比
  • -N:显示每行的行号
  • -o <文件名>:将less输出的内容在指定文件中保存起来
  • -Q:不使用警告音
  • -s:显示连续空行为一行
  • -S:行过长时间将超出部分舍弃
  • -x <数字>:将”tab”键显示为规定的数字空格

操作命令

  • /字符串:向下搜索”字符串”的功能
  • ?字符串:向上搜索”字符串”的功能
  • n:重复前一个搜索(与/?有关)
  • N:反向重复前一个搜索(与/?有关)
  • b:向后翻一页
  • d:向后翻半页
  • h:显示帮助界面
  • Q:退出less命令
  • u:向前滚动半页
  • y:向前滚动一行
  • 空格键:滚动一行
  • 回车键:滚动一页
  • [pagedown]:向下翻动一页
  • [pageup]:向上翻动一页

使用实例

  1. 查看文件test.log
    1
    less test.log

14. head命令

head用来显示档案的开头至标准输出中,默认head命令打印其相应文件的开头10行。

命令格式

1
head [参数] [文件]

常用参数

  • -q:隐藏文件名
  • -v:显示文件名
  • -c<字节>:显示字节数
  • -n<行数>:显示的行数

使用实例

  1. 显示文件test.log的前5行

    1
    head -n 5 test.log
  2. 显示文件test.log前20个字节

    1
    head -c 20 test.log

15. tail命令

显示指定文件末尾内容,不指定文件时,作为输入信息进行处理。常用查看日志文件。

命令格式

1
tail [必要参数] [选择参数] [文件]

常用参数

  • -f:循环读取
  • -q:不显示处理信息
  • -v:显示详细的处理信息
  • -c<数目>:显示的字节数
  • -n<行数>:显示行数
  • --pid=PID:与-f合用,表示在进程ID,PID死掉之后结束
  • -q, --quiet, --silent:从不输出给出文件名的首部
  • -s, --sleep-interval=S:与-f合用,表示在每次反复的间隔休眠S秒

使用实例

  1. 显示文件test.log最后5行内容

    1
    tail -n 5 test.log
  2. 循环查看文件内容

    1
    tail -f test.log

二、文件查找

16. which命令

which指令会在PATH变量指定的路径中,搜索某个系统命令的位置,并且返回第一个搜索结果。

命令格式

1
which 可执行文件名称

常用参数

  • -n:指定文件名长度,指定的长度必须大于或等于所有文件中最长的文件名
  • -p:与-n参数相同,但此处的包括了文件的路径
  • -w:指定输出时栏位的宽度
  • -V:显示版本信息

使用实例

  1. 查找文件、显示命令路径

    1
    which pwd
  2. 用which去找出which

    1
    which which

17. whereis命令

whereis命令是定位可执行文件、源代码文件、帮助文件在文件系统中的位置。

命令格式

1
whereis [-bmsu] [BMS 目录名 -f] 文件名

常用参数

  • -b:定位可执行文件
  • -m:定位帮助文件
  • -s:定位源代码文件
  • -u:搜索默认路径下除可执行文件、源代码文件、帮助文件以外的其它文件
  • -B:指定搜索可执行文件的路径
  • -M:指定搜索帮助文件的路径
  • -S:指定搜索源代码文件的路径

使用实例

  1. 将和svn文件相关的文件都查找出来

    1
    whereis svn
  2. 只将二进制文件查找出来

    1
    whereis -b svn

18. locate命令

可以很快速的搜寻档案系统内是否有指定的档案。

命令格式

1
locate [选择参数] [样式]

常用参数

  • -e:将排除在寻找的范围之外
  • -1:如果是1,则启动安全模式。在安全模式下,使用者不会看到权限无法看到的档案。这会始速度减慢,因为locate必须至实际的档案系统中取得档案的权限资料
  • -f:将特定的档案系统排除在外,例如我们没有到理要把proc档案系统中的档案放在资料库中
  • -q:安静模式,不会显示任何错误讯息
  • -n:至多显示n个输出
  • -r:使用正规运算式做寻找的条件
  • -o:指定资料库存的名称
  • -d:指定资料库的路径

使用实例

  1. 查找和pwd相关的所有文件

    1
    locate pwd
  2. 搜索etc目录下,所有以m开头的文件

    1
    locate /etc/m

19. find命令

主要作用是沿着文件层次结构向下遍历,匹配符合条件的文件,并执行相应的操作。

命令格式

1
find [选项] [搜索路径] [表达式]

常用参数

  • -print:find命令将匹配的文件输出到标准输出
  • -exec:find命令对匹配的文件执行该参数所给出的shell命令
  • -name:按照文件名查找文件
  • -type:查找某一类型的文件

使用实例

  1. 打印当前目录文件目录列表

    1
    find . -print
  2. 打印当前目录下所有不以.txt结尾的文件名

    1
    find . ! -name "*.txt"
  3. 打印当前目录下所有权限为777的php文件

    1
    find . -type f -name "*.php" -perm 777
  4. 找到当前目录下所有php文件,并显示其详细信息

    1
    find . -name "*.php" -exec ls -l {} \;
  5. 查找当前目录下所有c代码文件,统计总行数

    1
    find . -type f -name "*.c" | xargs wc -l

xargs命令说明:xargs命令可以从标准输入接收输入,并把输入转换为一个特定的参数列表。
命令格式:command | xargs [选项] [command]
xargs命令应该紧跟在管道操作符之后,因为它以标准输入作为主要的源数据流。
常用参数:

  • -n:指定每行最大的参数数量
  • -d:指定分隔符

三、文件打包上传和下载

20. tar命令

用来压缩和解压文件。tar本身不具有压缩功能。它是调用压缩功能实现的。

命令格式

1
tar [必要参数] [选择参数] [文件]

常用参数

必要参数:

  • -A:新增压缩文件到已存在的压缩
  • -B:设置区块大小
  • -c:建立新的压缩文件
  • -d:记录文件的差别
  • -r:添加文件到已经压缩的文件
  • -u:添加改变了和现有的文件到已经存在的压缩文件
  • -x:从压缩的文件中提取文件
  • -t:显示压缩文件的内容
  • -z:支持gzip解压文件
  • -j:支持bzip2解压文件
  • -Z:支持compress解压文件
  • -v:显示操作过程
  • -l:文件系统边界设置
  • -k:保留原有文件不覆盖
  • -m:保留文件不被覆盖
  • -W:确认压缩文件的正确性

可选参数:

  • -b:设置区块数目
  • -C:切换到指定目录
  • -f:指定压缩文件
  • --help:显示帮助信息
  • --version:显示版本信息

使用实例

  1. 将文件全部打包成tar包

    1
    2
    3
    tar -cvf test.tar test.log    # 仅打包,不压缩!
    tar -zcvf test.tar.gz test.log # 打包后,以gzip压缩
    tar -jcvf test.tar.bz2 test.log # 打包后,以bzip2压缩
  2. 将tar包解压缩

    1
    tar -zxvf test.tar.gz

21. gzip命令

使用广泛的压缩程序,文件经它压缩过后,其名称后面会多出”.gz”的扩展名。

命令格式

1
gzip [参数] [文件或者目录]

常用参数

  • -a--ascii:使用ASCII文字模式
  • -c--stdout--to-stdout:把压缩后的文件输出到标准输出设备,不去更动原始文件
  • -d--decompress--uncompress:解开压缩文件
  • -f--force:强行压缩文件。不理会文件名称或硬连接是否存在以及该文件是否为符号连接
  • -h--help:在线帮助

使用实例

  1. 把test1目录下的每个文件压缩成.gz文件
    1
    gzip *

四、文件权限设置

22. chmod命令

用于改变Linux系统文件或目录的访问权限。

命令格式

1
chmod [-cfvR] [--help] [--version] mode file

常用参数

必要参数:

  • -c:当发生改变时,报告处理信息
  • -f:错误信息不输出
  • -R:处理指定目录以及其子目录下的所有文件
  • -v:运行时显示详细处理信息

选择参数:

  • --reference=<目录或者文件>:设置成具有指定目录或者文件具有相同的权限
  • --version:显示版本信息
  • <权限范围>+<权限设置>:使权限范围内的目录或者文件具有指定的权限
  • <权限范围>-<权限设置>:删除权限范围的目录或者文件的指定权限
  • <权限范围>=<权限设置>:设置权限范围内的目录或者文件的权限为指定的值

权限范围:

  • u:目录或者文件的当前的用户
  • g:目录或者文件的当前的群组
  • o:除了目录或者文件的当前用户或群组之外的用户或者群组
  • a:所有的用户及群组

权限代号:

  • r:读权限,用数字4表示
  • w:写权限,用数字2表示
  • x:执行权限,用数字1表示
  • -:删除权限,用数字0表示

使用实例

  1. 增加文件所有用户组可执行权限

    1
    chmod a+x test.log
  2. 删除所有用户的可执行权限

    1
    chmod a-x test.log

23. chgrp命令

可采用群组名称或群组识别码的方式改变文件或目录的所属群组。

命令格式

1
chgrp [选项] [组] [文件]

常用参数

必要参数:

  • -c:当发生改变时输出调试信息
  • -f:不显示错误信息
  • -R:处理指定目录以及其子目录下的所有文件
  • -v:运行时显示详细的处理信息
  • --dereference:作用于符号链接的指向,而不是符号链接本身
  • --no-dereference:作用于符号链接本身

选择参数:

  • --reference=<文件或者目录>
  • --help:显示帮助信息
  • --version:显示版本信息

使用实例

  1. 改变文件的群组属性
    1
    chgrp -v bin test.log

24. chown命令

通过chown改变文件的拥有者和群组。

命令格式

1
chown [选项] [所有者][:[组]] 文件

常用参数

必要参数:

  • -c:显示更改的部分的信息
  • -f:忽略错误信息
  • -h:修复符号链接
  • -R:处理指定目录以及其子目录下的所有文件
  • -v:显示详细的处理信息
  • --deference:作用于符号链接的指向,而不是符号链接本身

选择参数:

  • --reference=<目录或文件>:把指定的目录/文件作为参考,把操作的文件/目录设置成参考文件/目录相同拥有者和群组
  • --from=<当前用户:当前群组>:只有当前用户和群组跟指定的用户和群组相同时才进行改变
  • --help:显示帮助信息
  • --version:显示版本信息

使用实例

  1. 改变拥有者和群组
    1
    chown mail:mail test.log

五、磁盘管理

25. df命令

显示磁盘空间使用情况。

命令格式

1
df [选项] [文件]

常用参数

  • -a:全部文件系统列表
  • -h:方便阅读方式显示
  • -i:显示inode信息
  • -k:区块为1024字节
  • -l:只显示本地磁盘
  • -T:文件系统类型

使用实例

  1. 显示磁盘使用情况

    1
    df -l
  2. 以易读方式列出所有文件系统及其类型

    1
    df -haT

26. du命令

显示每个文件和目录的磁盘使用空间。

命令格式

1
du [选项] [文件]

常用参数

  • -a:显示目录中个别文件的大小
  • -b:显示目录或文件大小时,以byte为单位
  • -c:除了显示个别目录或文件的大小外,同时也显示所有目录或文件的总和
  • -k:以KB(1024bytes)为单位输出
  • -m:以MB为单位输出
  • -s:仅显示总计,只列出最后加总的值
  • -h:以K,M,G为单位,提高信息的可读性
  • -x:以一开始处理时的文件系统为准,若遇上其它不同的文件系统目录则略过
  • -L:显示选项中所指定符号链接的源文件大小
  • -S:显示个别目录的大小时,并不含其子目录的大小
  • -X<文件>:在<文件>指定目录或文件
  • --exclude=<目录或文件>:略过指定的目录或文件
  • -D:显示指定符号链接的源文件大小
  • -H:与-h参数相同,但是K,M,G是以1000为换算单位
  • -l:重复计算硬件链接的文件

使用实例

  1. 以易读方式显示文件夹内及子文件夹大小

    1
    du -h test/
  2. 显示几个文件或目录各自占用磁盘空间的大小,并统计总和

    1
    du -ch test1 test2

27. ln命令

为某一个文件在另外一个位置建立一个同步的链接。

命令格式

1
ln [参数] [源文件或目录] [目标文件或目录]

常用参数

  • -b:删除,覆盖以前建立的链接
  • -d:允许超级用户制作目录的硬链接
  • -f:强制执行
  • -i:交互模式,文件存在则提示用户是否覆盖
  • -n:把符号链接视为一般目录
  • -s:软链接(符号链接)
  • -v:显示详细的处理过程

使用实例

  1. 给文件创建软链接

    1
    ln -s /usr/local/mysql/bin/mysql /usr/bin
  2. 给文件创建硬链接

    1
    ln /usr/local/mysql/bin/mysql /usr/bin

六、系统管理

28. ps命令

用来列出系统中当前运行的那些进程。

命令格式

1
ps [参数]

常用参数

  • -a:显示所有进程
  • -u:用户以及其他详细信息
  • -x:显示没有控制终端的进程

使用实例

  1. 显示所有进程信息

    1
    ps -A
  2. 显示指定用户信息

    1
    ps -u root

29. kill命令

发送指定的信号到相应进程。

命令格式

1
kill [参数] [进程号]

常用参数

  • -l:信号,若不加信号的编号参数,则使用-l参数会列出全部的信号名称
  • -a:当处理当前进程时,不限制命令名和进程号的对应关系
  • -p:指定kill命令只打印相关进程的进程号,而不发送任何信号
  • -s:指定发送信号
  • -u:指定用户

使用实例

  1. 先使用ps查找进程pro1,然后用kill杀掉
    1
    kill -9 $(ps -ef | grep pro1)

30. top命令

显示当前系统正在执行的进程的相关信息,包括进程ID、内存占用率、CPU占用率等。

命令格式

1
top [参数]

常用参数

  • -b:批处理
  • -c:显示完整的治命令
  • -I:忽略失效过程
  • -s:保密模式
  • -S:累积模式
  • -i<时间>:设置间隔时间
  • -u<用户名>:指定用户名
  • -p<进程号>:指定进程
  • -n<次数>:循环显示的次数

使用实例

  1. 显示进程信息

    1
    top
  2. 显示完整命令

    1
    top -c

31. free命令

显示系统内存使用情况,包括物理内存、交互区内存(swap)和内核缓冲区内存。

命令格式

1
free [参数]

常用参数

  • -b:以Byte为单位显示内存使用情况
  • -k:以KB为单位显示内存使用情况
  • -m:以MB为单位显示内存使用情况
  • -g:以GB为单位显示内存使用情况
  • -o:不显示缓冲区调节列
  • -s<间隔秒数>:持续观察内存使用状况
  • -t:显示内存总和列
  • -V:显示版本信息

使用实例

  1. 显示内存使用情况
    1
    free -m

32. vmstat命令

显示虚拟内存状态。

命令格式

1
vmstat [参数]

常用参数

  • -a:显示活动内页
  • -f:显示启动后创建的进程总数
  • -m:显示slab信息
  • -n:头信息仅显示一次
  • -s:以表格方式显示事件计数器和内存状态
  • -d:报告磁盘状态
  • -p:显示指定的硬盘分区状态
  • -S:输出信息的单位

使用实例

  1. 显示活动和非活动内存
    1
    vmstat -a 2 5

33. iostat命令

被用于监视系统输入输出设备和CPU的使用情况。

命令格式

1
iostat [参数] [时间] [次数]

常用参数

  • -c:仅显示CPU使用情况
  • -d:仅显示设备利用率
  • -k:显示状态以千字节每秒为单位,而不使用块每秒
  • -m:显示状态以兆字节每秒为单位
  • -p:仅显示块设备和所有被使用的其他分区的状态
  • -t:显示每个报告产生时的时间
  • -V:显示版号并退出
  • -x:显示扩展状态

使用实例

  1. 显示所有设备负载情况
    1
    iostat

34. watch命令

可以将命令的输出结果输出到标准输出设备,多用于周期性执行命令/定时执行命令。

命令格式

1
watch [参数] [命令]

常用参数

  • -n:或--interval:watch缺省每2秒运行一下程序,可以用-n--interval来指定间隔的时间
  • -d:或--differences:用-d--differences选项watch会高亮显示变化的区域。-d=cumulative选项会把变动过的地方(不管最近的那次有没有变动)都高亮显示出来
  • -t:或-no-title:会关闭watch命令在顶部的时间间隔,命令,当前时间的输出
  • -h:或--help:查看帮助文档

使用实例

  1. 每隔一秒高亮显示网络链接数的变化情况

    1
    watch -n 1 -d netstat -ant
  2. 每隔一秒高亮显示http链接数的变化情况

    1
    watch -n 1 -d 'pstree|grep http'

35. at命令

在指定时间执行一个任务,只能执行一次。

命令格式

1
at [参数] [时间]

常用参数

  • -m:当指定的任务被完成之后,将给用户发送邮件,即使没有标准输出
  • -I:atq的别名
  • -d:atrm的别名
  • -v:显示任务将被执行的时间
  • -c:打印任务的内容到标准输出
  • -V:显示版本信息
  • -q<队列>:使用指定的队列
  • -f<文件>:从指定文件读入任务而不是从标准输入读入
  • -t<时间参数>:以时间参数的形式提交要运行的任务

使用实例

  1. 三天后的下午5点执行/bin/ls

    1
    2
    at 5pm + 3 days
    /bin/ls
  2. 明天17点钟,输出时间到指定文件内

    1
    2
    at 17:20 tomorrow
    date > /root/2016.log

36. crontab命令

被用来提交和管理用户的需要周期性执行的任务。

命令格式

1
crontab [选项] [文件]

常用参数

  • -e:编辑该用户的计时器设置
  • -l:列出该用户的计时器设置
  • -r:删除该用户的计时器设置
  • -u<用户名称>:指定要设定计时器的用户名称

使用实例

  1. 每1分钟执行一次command

    1
    * * * * * command
  2. 每小时的第3和第15分钟执行

    1
    3,15 * * * * command
  3. 在上午8点到11点的第3和第15分钟执行

    1
    3,15 8-11 * * * command

37. scp命令

用于Linux之间复制文件和目录。

命令格式

1
scp [参数] [原路径] [目标路径]

常用参数

  • -1:强制scp命令使用协议ssh1
  • -2:强制scp命令使用协议ssh2
  • -4:强制scp命令只使用IPv4寻址
  • -6:强制scp命令只使用IPv6寻址
  • -B:使用批处理模式(传输过程中不询问传输口令或短语)
  • -C:允许压缩(将-C标志传递给ssh,从而打开压缩功能)
  • -p:保留原文件的修改时间,访问时间和访问权限
  • -q:不显示传输进度条
  • -r:递归复制整个目录
  • -v:详细方式显示输出。scp和ssh(1)会显示出整个过程的调试信息。这些信息用于调试连接,验证和配置问题
  • -c cipher:以cipher将数据传输进行加密,这个选项将直接传递给ssh
  • -F ssh_config:指定一个替代的ssh配置文件,此参数直接传递给ssh
  • -i identity_file:从指定文件中读取传输时使用的密钥文件,此参数直接传递给ssh
  • -l limit:限定用户所能使用的带宽,以Kbit/s为单位
  • -o ssh_option:如果习惯于使用ssh_config(5)中的参数传递方式
  • -P port:注意是大写的P,port是指定数据传输用到的端口号
  • -S program:指定加密传输时所使用的程序。此程序必须能够理解ssh(1)的选项

使用实例

  1. 从本地复制到远程

    1
    2
    3
    4
    scp local_file remote_username@remote_ip:remote_folder
    scp local_file remote_username@remote_ip:remote_file
    scp local_file remote_ip:remote_folder
    scp local_file remote_ip:remote_file
  2. 从远程复制到本地

    1
    2
    scp root@www.test.com:/home/root/others/music /home/space/music/1.mp3
    scp -r www.test.com:/home/root/others/ /home/space/music/

38. wget命令

用来从指定的URL下载文件。

命令格式

1
wget [参数] [URL地址]

常用参数

  • -b:后台下载模式
  • -P:下载到指定目录
  • -t:最大尝试次数
  • -c:断点续传
  • -p:下载页面内所有资源,包括图片、视频等
  • -r:递归下载

使用实例

  1. 使用wget下载单个文件

    1
    wget http://www.test.com/testfile.zip
  2. 下载并以不同的文件名保存

    1
    wget -O test.zip http://www.test.com/download?file=testfile.zip

七、网络管理

39. ifconfig命令

被用于配置和显示Linux内核中网络接口的网络参数。

命令格式

1
ifconfig [参数]

常用参数

  • add<地址>:设置网络设备IPv6的IP地址
  • del<地址>:删除网络设备IPv6的IP地址
  • down:关闭指定的网络设备
  • up:启动指定的网络设备
  • IP地址:指定网络设备的IP地址

使用实例

  1. 显示网络设备信息

    1
    ifconfig
  2. 启动关闭指定网卡

    1
    2
    ifconfig eth0 down
    ifconfig eth0 up

40. ping命令

用于测试主机之间网络的连通性。

命令格式

1
ping [参数] [主机名或IP地址]

常用参数

  • -d:使用Socket的SO_DEBUG功能
  • -c<完成次数>:设置完成要求回应的次数
  • -f:极限检测
  • -i<间隔秒数>:指定收发信息的间隔时间
  • -I<网络界面>:使用指定的网络界面送出数据包
  • -l<前置载入>:设置在送出要求信息之前,先行发出的数据包
  • -n:只输出数值
  • -p<范本样式>:设置填满数据包的范本样式
  • -q:不显示指令执行过程,开头和结尾的相关信息除外
  • -r:忽略普通的Routing Table,直接将数据包送到远端主机上
  • -R:记录路由过程
  • -s<数据包大小>:设置数据包的大小
  • -t<存活数值>:设置存活数值TTL的大小
  • -v:详细显示指令的执行过程

使用实例

  1. 检测是否与主机连通
    1
    ping www.google.com

41. netstat命令

用于显示网络状态。

命令格式

1
netstat [参数]

常用参数

  • -a--all:显示所有连线中的Socket
  • -A<网络类型>--<网络类型>:列出该网络类型连线中的相关地址
  • -c--continuous:持续列出网络状态
  • -C--cache:显示路由器配置的快取信息
  • -e--extend:显示网络其他相关信息
  • -F--fib:显示FIB
  • -g--groups:显示多重广播功能群组组员名单
  • -h--help:在线帮助
  • -i--interfaces:显示网络界面信息表单
  • -l--listening:显示监控中的服务器的Socket
  • -M--masquerade:显示伪装的网络连线
  • -n--numeric:直接使用IP地址,而不通过域名服务器
  • -N--netlink--symbolic:显示网络硬件外围设备的符号连接名称
  • -o--timers:显示计时器
  • -p--programs:显示正在使用Socket的程序识别码和程序名称
  • -r--route:显示Routing Table
  • -s--statistice:显示网络工作信息统计表
  • -t--tcp:显示TCP传输协议的连线状况
  • -u--udp:显示UDP传输协议的连线状况
  • -v--verbose:显示指令执行过程
  • -V--version:显示版本信息
  • -w--raw:显示RAW传输协议的连线状况
  • -x--unix:此参数的效果和指定”-A unix”参数相同
  • --ip--inet:此参数的效果和指定”-A inet”参数相同

使用实例

  1. 列出所有端口

    1
    netstat -a
  2. 显示TCP端口

    1
    netstat -at

42. telnet命令

用于登录远程主机,对远程主机进行管理。

命令格式

1
telnet [参数] [主机]

常用参数

  • -8:允许使用8位字符资料,包括输入与输出
  • -a:尝试自动登入远端系统
  • -b<主机别名>:使用别名指定远端主机名称
  • -c:不读取用户专属目录里的.telnetrc文件
  • -d:启动排错模式
  • -e<脱离字符>:设置脱离字符
  • -E:滤除脱离字符
  • -f:此参数的效果和指定”-F”参数相同
  • -F:使用Kerberos V5认证时,加上此参数可把本地主机的认证数据上传到远端主机
  • -k<域名>:使用Kerberos认证时,加上此参数让远端主机采用指定的领域名,而非该主机的域名
  • -K:不自动登入远端主机
  • -l<用户名称>:指定要登入远端主机的用户名称
  • -L:允许输出8位字符资料
  • -n<记录文件>:指定文件记录相关信息
  • -r:使用类似rlogin指令的用户界面
  • -S<服务类型>:设置telnet连线所需的IP TOS信息
  • -x:假设主机有支持数据加密的功能,就使用它
  • -X<认证形态>:关闭指定的认证形态

使用实例

  1. 远程服务器无法访问
    1
    telnet 192.168.0.5 8080

43. ssh命令

用于远程登录上Linux主机。

命令格式

1
ssh [参数] [远程主机]

常用参数

  • -1:强制使用ssh协议版本1
  • -2:强制使用ssh协议版本2
  • -4:强制使用IPv4地址
  • -6:强制使用IPv6地址
  • -A:开启认证代理连接转发功能
  • -a:关闭认证代理连接转发功能
  • -b<IP地址>:使用本机指定的地址作为对位连接的源IP地址
  • -c<请求认证>:选择所请求的认证方式。用逗号分隔列表
  • -C:请求压缩所有数据
  • -F<配置文件>:指定ssh指令的配置文件
  • -f:后台执行ssh指令
  • -g:允许远程主机连接主机的转发端口
  • -i<身份文件>:指定身份文件(即私钥文件)
  • -l<登录名>:指定连接远程服务器的登录用户名
  • -N:不执行远程指令
  • -o<选项>:指定配置选项
  • -p<端口>:指定远程服务器上的端口
  • -q:静默模式,所有的警告和诊断信息被禁止输出
  • -s:请求远程系统上的一个子系统调用。子系统是SSH2协议的一个特性,有助于将SSH用作其他应用程序的安全传输。子系统由远程命令指定
  • -T:禁止分配伪终端
  • -t:强制分配伪终端。可以在远程机器上执行任何全屏幕(screen-based)程序,所以非常有用,例如菜单服务。重复使用-t选项可以强制分配终端,即使没有本地终端
  • -v:详细模式。使ssh打印关于进度的调试信息。这对于调试连接、认证和配置问题很有帮助。多个-v选项会增加详细程度。最大为3
  • -V:显示版本信息
  • -x:关闭X11转发
  • -X:开启X11转发
  • -y:开启信任X11转发

使用实例

  1. 登录远程服务器
    1
    ssh 192.168.0.11

八、用户管理

44. useradd命令

用于建立用户帐号。

命令格式

1
useradd [选项] 用户名

常用参数

  • -c<备注>:加上备注文字。备注文字会保存在passwd的备注栏位中
  • -d<登入目录>:指定用户登入时的启始目录
  • -D:变更预设值
  • -e<有效期限>:指定帐号的有效期限
  • -f<缓冲天数>:指定在密码过期后多少天即关闭该帐号
  • -g<群组>:指定用户所属的群组
  • -G<群组>:指定用户所属的附加群组
  • -m:自动建立用户的登入目录
  • -M:不要自动建立用户的登入目录
  • -n:取消建立以用户名称为名的群组
  • -r:建立系统帐号
  • -s<shell>:指定用户登入后所使用的shell
  • -u<uid>:指定用户id

使用实例

  1. 新建用户加入组
    1
    useradd -g root testuser

45. userdel命令

用于删除用户帐号。

命令格式

1
userdel [选项] 用户名

常用参数

  • -f:强制删除用户,即使用户当前已登录
  • -r:删除用户的同时,删除与用户相关的所有文件

使用实例

  1. 删除用户账号
    1
    userdel testuser

46. passwd命令

用于设置用户的认证信息,包括用户密码、密码过期时间等。

命令格式

1
passwd [选项] [用户名]

常用参数

  • -d:删除密码,仅有系统管理者才能使用
  • -f:强制执行
  • -k:设置只有在密码过期失效后,方能更新
  • -l:锁住密码
  • -s:列出密码的相关信息,仅有系统管理者才能使用
  • -u:解开已上锁的帐号

使用实例

  1. 修改用户密码
    1
    passwd testuser

47. su命令

用于切换当前用户身份到其他用户身份。

命令格式

1
su [选项] [用户名]

常用参数

  • -c<指令>--command=<指令>:执行完指定的指令后,即恢复原来的身份
  • -f--fast:适用于csh与tsch,使shell不用去读取启动文件
  • -l--login:改变身份时,也同时变更工作目录,以及HOME,SHELL,USER,LOGNAME。此外,也会变更PATH变量
  • -m-p--preserve-environment:变更身份时,不要变更环境变量
  • -s<shell>--shell=<shell>:指定要执行的shell
  • --help:显示帮助
  • --version:显示版本信息

使用实例

  1. 变更帐号为root并在执行ls指令后退出变回原使用者
    1
    su -c ls root

九、其他常用命令

48. clear命令

用于清除当前终端屏幕。

命令格式

1
clear

使用实例

  1. 清屏
    1
    clear

49. history命令

用于显示历史命令。

命令格式

1
history [选项] [参数]

常用参数

  • -c:清空当前历史命令
  • -a:将历史命令缓冲区中命令写入历史命令文件中
  • -r:将历史命令文件中的命令读入当前历史命令缓冲区
  • -w:将当前历史命令缓冲区命令写入历史命令文件中

使用实例

  1. 显示所有历史命令

    1
    history
  2. 执行历史记录中第n条命令

    1
    !n

50. alias命令

用于设置指令的别名。

命令格式

1
alias [别名]=[指令名称]

使用实例

  1. 查看系统当前所有别名

    1
    alias
  2. 设置别名

    1
    alias l='ls -l'

51. unalias命令

用于删除别名。

命令格式

1
unalias [别名]

使用实例

  1. 删除别名
    1
    unalias l

52. date命令

显示或设定系统的日期与时间。

命令格式

1
date [选项] [格式]

常用参数

  • -d<字符串>:显示字符串所指的日期与时间。字符串前后必须加上双引号
  • -s<字符串>:根据字符串来设置日期与时间。字符串前后必须加上双引号
  • -u:显示GMT
  • --help:在线帮助
  • --version:显示版本信息

使用实例

  1. 显示当前时间

    1
    date
  2. 设置系统时间

    1
    date -s "2024-01-01 12:00:00"

53. cal命令

用于显示当前日历,或者指定日期的日历。

命令格式

1
cal [选项] [[[日] 月] 年]

常用参数

  • -1:显示一个月的日历
  • -3:显示前一个月、当前月、下一个月的日历
  • -s:星期天作为一周的第一天
  • -m:星期一作为一周的第一天
  • -j:显示在当年中的第几天(一年日期按天算,从1月1日算起,默认显示当前月在一年中的天数)
  • -y:显示当前年份的日历

使用实例

  1. 显示当前月份日历

    1
    cal
  2. 显示指定年份的日历

    1
    cal 2024

54. echo命令

用于在shell中打印shell变量的值,或者直接输出指定的字符串。

命令格式

1
echo [选项] [字符串]

常用参数

  • -e:激活转义字符
  • -n:不换行输出

使用实例

  1. 输出字符串

    1
    echo "Hello World"
  2. 输出变量值

    1
    echo $PATH

55. grep命令

强大的文本搜索工具,能使用正则表达式搜索文本,并把匹配的行打印出来。

命令格式

1
grep [选项] 模式 [文件]

常用参数

  • -i:忽略大小写
  • -v:反向选择,即显示不包含匹配文本的所有行
  • -n:显示匹配行及行号
  • -c:计算匹配的行数
  • -r:递归搜索
  • -l:只显示文件名
  • -w:匹配整个单词

使用实例

  1. 在文件中搜索指定字符串

    1
    grep "error" test.log
  2. 递归搜索目录

    1
    grep -r "function" /home/user/code/

56. sed命令

流编辑器,用于对文本进行过滤和转换。

命令格式

1
sed [选项] '命令' 文件

常用参数

  • -n:只显示处理后的结果
  • -e:直接在命令行模式上进行sed动作编辑
  • -i:直接修改文件内容
  • -r:使用扩展正则表达式

常用命令

  • s:替换
  • d:删除
  • p:打印
  • a:追加
  • i:插入

使用实例

  1. 替换文件中的字符串

    1
    sed 's/old/new/g' file.txt
  2. 删除文件中的空行

    1
    sed '/^$/d' file.txt

57. awk命令

强大的文本分析工具,用于处理文本文件。

命令格式

1
awk [选项] '模式 {动作}' 文件

常用参数

  • -F:指定分隔符
  • -v:定义变量
  • -f:从脚本文件中读取awk命令

使用实例

  1. 打印文件的第一列

    1
    awk '{print $1}' file.txt
  2. 使用冒号作为分隔符

    1
    awk -F: '{print $1}' /etc/passwd

58. sort命令

用于对文本文件的行进行排序。

命令格式

1
sort [选项] [文件]

常用参数

  • -n:依照数值的大小排序
  • -r:以相反的顺序来排序
  • -k:指定排序的列
  • -u:去除重复行
  • -t:指定分隔符

使用实例

  1. 对文件进行排序

    1
    sort file.txt
  2. 按数值排序

    1
    sort -n file.txt

59. uniq命令

用于报告或忽略文件中的重复行。

命令格式

1
uniq [选项] [输入文件] [输出文件]

常用参数

  • -c:在每行旁边显示该行重复出现的次数
  • -d:仅显示重复出现的行
  • -u:仅显示出现一次的行
  • -i:忽略大小写

使用实例

  1. 显示重复行

    1
    uniq -d file.txt
  2. 统计每行出现次数

    1
    uniq -c file.txt

60. wc命令

用于计算文件的字节数、字数、行数。

命令格式

1
wc [选项] [文件]

常用参数

  • -c:显示字节数
  • -l:显示行数
  • -w:显示字数
  • -m:显示字符数

使用实例

  1. 统计文件行数

    1
    wc -l file.txt
  2. 统计多个文件

    1
    wc file1.txt file2.txt

十、总结

Linux命令虽然繁多,但掌握这些常用命令已经足够应对日常的系统管理和开发工作。建议:

  1. 分类记忆:将命令按功能分类,便于记忆和使用
  2. 实践为主:多在实际工作中使用这些命令
  3. 善用帮助:使用man命令查看命令的详细说明
  4. 组合使用:学会将多个命令通过管道组合使用
  5. 建立别名:为常用命令设置别名,提高工作效率

记住,熟练使用Linux命令的关键在于实践。随着使用经验的积累,这些命令会逐渐成为你的第二本能。

wpa_supplicant是一个连接、配置WIFI的工具,它主要包含wpa_supplicantwpa_cli两个程序。通常情况下,可以通过wpa_cli来进行WIFI的配置与连接,如果有特殊的需要,可以编写应用程序直接调用wpa_supplicant的接口直接开发。

启动wpa_supplicant应用

1
$ wpa_supplicant -D nl80211 -i wlan0 -c /etc/wpa_supplicant.conf -B

/etc/wpa_supplicant.conf文件里,添加下面代码:

1
ctrl_interface=/var/run/wpa_supplicant update_config=1

启动wpa_cli应用

1
2
3
$ wpa_cli -i wlan0 scan # 搜索附近wifi网络 
$ wpa_cli -i wlan0 scan_result # 打印搜索wifi网络结果
$ wpa_cli -i wlan0 add_network # 添加一个网络连接

如果要连接加密方式是[WPA-PSK-CCMP+TKIP][WPA2-PSK-CCMP+TKIP][ESS] (wpa加密),wifi名称是namewifi密码是:psk

1
2
3
$ wpa_cli -i wlan0 set_network 0 ssid '"name"' 
$ wpa_cli -i wlan0 set_network 0 psk '"psk"'
$ wpa_cli -i wlan0 enable_network 0

如果要连接加密方式是[WEP][ESS] (wep加密),wifi名称是namewifi密码是psk

1
2
3
4
$ wpa_cli -i wlan0 set_network 0 ssid '"name"' 
$ wpa_cli -i wlan0 set_network 0 key_mgmt NONE
$ wpa_cli -i wlan0 set_network 0 wep_key0 '"psk"'
$ wpa_cli -i wlan0 enable_network 0

如果要连接加密方式是[ESS] (无加密),wifi名称是name

1
2
3
$ wpa_cli -i wlan0 set_network 0 ssid '"name"' 
$ wpa_cli -i wlan0 set_network 0 key_mgmt NONE
$ wpa_cli -i wlan0 enable_network 0

分配ip/netmask/gateway/dns

1
$ udhcpc -i wlan0 -s /etc/udhcpc.script -q

执行完毕,就可以连接网络了。

保存连接

1
$ wpa_cli -i wlan0 save_config

断开连接

1
$ wpa_cli -i wlan0 disable_network 0

连接已有的连接

1
2
3
$ wpa_cli -i wlan0 list_network #列举所有保存的连接 
$ wpa_cli -i wlan0 select_network 0 #连接第1个保存的连接
$ wpa_cli -i wlan0 enable_network 0 #使能第1个保存的连接

断开wifi

1
2
3
$ ifconfig wlan0 down 
$ killall udhcpc
$ killall wpa_supplicant

wpa_wifi_tool使用方法

wpa_wifi_tool是基于wpa_supplicantwpa_cli的一个用于快速设置wifi的工具,方便调试时连接wifi使用。使用方法:1、运行wpa_wifi_tool;2、输入help进行命令查看;3、s进行SSID扫描;4、c[n]进行wifi连接,连接时若为新的SSID则需输入密码,若为已保存的SSID则可以使用保存过的密码或者重新输入密码;5、e退出工具。

0%