文章篇幅较长,代码部分建议横屏查看,或在PC端打开本文链接。文末依然为爱学习的你准备了专属福利~

TCP和UDP除了在Lua代码声明时有一些不同,其他地方完全一样,所以下面的代码将以TCP长连接的数据收发作为示例,如果需要UDP连接,只需要改声明对象时的三个字母即可。

阅读本文需要具有的技能:

看过该系列前几篇文章或明白前几篇文章内容的
熟悉Lua语法,尤其是数组部分
可以明白字符串、字节码之间的区别
可以自己实践操作
对TCP/UDP通讯有基本的了解

各位想看MQTT解释的,请等待下一篇文章,不过也可以顺便看看这一篇,二者都差不多的。

1

Socket(TCP/UDP)

TCP和UDP除了在Lua代码声明时有一些不同,其他地方完全一样,所以下面的代码将以TCP长连接的数据收发作为示例,如果需要UDP连接,只需要改声明对象时的三个字母即可。

先定义一个假装能用来测试的TCP协议(需求)

  • 客户端每10秒发送一条字符串heart beat

  • 客户端接收到back开头的数据要回复相同的数据

  • 客户端收到bin要回复二进制数组0x11 0x22 0x33

  • 客户端收到time要回复当前时间的时间戳字符串

示例时序如下:

2

代码详解

2.1 官方demo提供的示例代码

在github的Luat_2G_RDA_8955/script_LuaTask/demo/socket/longConnection目录或luatools的LuaTools 1.x.x\script\script_LuaTask\demo\socket\longConnection目录找到

如果你能看懂官方例程,那么可以直接去使用,不需要再看本文了。

2.2  socket连接代码的拆解分析

这一部分会将官方demo的代码拆开来,只保留基础部分,放到一个文件中来解释。

2.2.1建立文件

首先先新建两个文件,用于测试这个工程。

main.lua

PROJECT = "SOCKET-TEST"

VERSION = "1.0.0"

--根据固件判断模块类型

moduleType = string.find(rtos.get_version(),"8955") and 2 or 4

require "log"

LOG_LEVEL = log.LOGLEVEL_TRACE

require "sys"

--每1分钟查询一次GSM信号强度,每1分钟查询一次基站信息

require "net"

net.startQueryAll(60000, 60000)

--加载硬件看门狗功能模块

--如果用的是720 4g模块,请注释掉这两行

require "wdt"

wdt.setup(pio.P0_30, pio.P0_31)

--加载网络指示灯功能模块

require "netLed"

netLed.setup(true,moduleType == 2 and pio.P1_1 or pio.P2_0,moduleType == 2 and nil or pio.P2_1)

require"longlink"

--启动系统框架

sys.init(0, 0)

sys.run()

longlink.lua

module(...,package.seeall)

require"socket"

--下面代码一会儿写

2.2.2找一个测试用的服务器

2G模块Socket测试和Wi-Fi有着本质的区别:没法使用内网来调试,必须要使用一个公网服务器来调试。

为了解决这个问题,Luat官方提供了一个TCP测试实验室网站服务:http://tcplab.openluat.com/这个工具有一个坏处,就是三分钟没有客户端连接的话就会被强行关闭服务。我们可以在本地用一个TCP调试工具提前连上,就不会被强制关闭服务了。

为了针对这种情况,我临时写了一个工具:https://github.com/chenxuuu/tcplab.openluat.com编译好的文件可以点击下载。

打开后可以直接获取从服务器分配的ip、端口,还能接收客户端数据、主动发送数据:

记住自己获取到的ip和端口,在下面的代码中会被使用到。

2.2.3建立socket线程

一般来说,socket连接都是异步运行的,何时应该发送数据,何时应该接收数据,这些逻辑应该让socket收发的进程自己进行控制。

所以我们在longlink.lua中添加一个新的线程(看不懂的回去看前几篇文章),文件改成如下(注意要自己改东西!):

longlink.lua

module(...,package.seeall)

require"socket"

--测试用的服务器信息,上一部分获取到的那个

local testip,testport = "",""

--启动socket客户端任务

sys.taskInit(

function()

   while true do

--该区域的代码会永久循环运行(除非出现语法错误)

end

end)

2.2.4进行socket连接

一般来说,我们会在模块成功获取基站分配的ip后,才会进行网络的连接操作,所以我们需要使用socket.isReady()函数来判断是否连接网络,然后再进行网络操作。

在成功获取ip后,我们才能新建一个tcp对象,对其进行联网操作,socket客户端线程代码改为如下:

--启动socket客户端任务

sys.taskInit(

function()

   while true do

       --是否获取到了分配的ip(是否连上网)

       if socket.isReady() then

           --新建一个socket对象,如果是udp只需要把tcp改成udp即可

           local socketClient = socket.tcp()

--尝试连接指定服务器

           if socketClient:connect(testip,testport) then

--连接成功

               log.info("longlink.socketClient","connect success")

else

               log.info("longlink.socketClient","connect fail")

--连接失败

end

else

--没连上网,原地等待一秒,一秒后会循环回去重试

           sys.wait(1000)

end

end

end)

2.2.5对连接失败的处理

上述代码只是一个简单的连接服务器的代码,并且连上之后没有进行任何的其他操作。

为了增加代码的稳健性,我们可以利用sys.waitUntil()函数,设置五分钟内没有获取到ip就开启飞行模式几秒,再关闭,让模块重新去获取GPRS连接:

--启动socket客户端任务

sys.taskInit(

function()

   while true do

       --是否获取到了分配的ip(是否连上网)

       if socket.isReady() then

           --新建一个socket对象,如果是udp只需要把tcp改成udp即可

           local socketClient = socket.tcp()

--尝试连接指定服务器

           if socketClient:connect(testip,testport) then

--连接成功

               log.info("longlink.socketClient","connect success")

else

               log.info("longlink.socketClient","connect fail")

--连接失败

end

else

--没连上网

           --等待网络环境准备就绪,超时时间是5分钟

           sys.waitUntil("IP_READY_IND",300000)

--等完了还没连上?

           if not socket.isReady() then

               --进入飞行模式,20秒之后,退出飞行模式

               net.switchFly(true)

               sys.wait(20000)

               net.switchFly(false)

end

end

end

end)

同样,我们也可以给socketClient:connect(testip,testport)的连接加上错误次数的判断,连接错误超过五次,强制断开socket连接,等待五秒后重试:

--启动socket客户端任务

sys.taskInit(

function()

   local retryConnectCnt = 0   --失败次数统计

   while true do

       --是否获取到了分配的ip(是否连上网)

       if socket.isReady() then

           --新建一个socket对象,如果是udp只需要把tcp改成udp即可

           local socketClient = socket.tcp()

--尝试连接指定服务器

           if socketClient:connect(testip,testport) then

--连接成功

               log.info("longlink.socketClient","connect success")

               retryConnectCnt = 0 --失败次数清零

else

               log.info("longlink.socketClient","connect fail")

--连接失败

               retryConnectCnt = retryConnectCnt+1 --失败次数加一

end

           socketClient:close()    --断开socket连接

           if retryConnectCnt>=5 then  --失败次数大于五次了

               link.shut()         --强制断开TCP/UDP连接

               retryConnectCnt=0   --失败次数清零

end

           sys.wait(5000)

else

           retryConnectCnt = 0     --没连上网,失败次数清零

--没连上网

           --等待网络环境准备就绪,超时时间是5分钟

           sys.waitUntil("IP_READY_IND",300000)

--等完了还没连上?

           if not socket.isReady() then

               --进入飞行模式,20秒之后,退出飞行模式

               net.switchFly(true)

               sys.wait(20000)

               net.switchFly(false)

end

end

end

end)

2.2.6添加发送/接收处理函数

到了这一步,整个的socket线程只剩下循环处理接收和发送的数据这一部分与demo不同了,我们直接把这两句话加到socket线程的代码中吧:

--启动socket客户端任务

sys.taskInit(

function()

   local retryConnectCnt = 0   --失败次数统计

   while true do

       --是否获取到了分配的ip(是否连上网)

       if socket.isReady() then

           --新建一个socket对象,如果是udp只需要把tcp改成udp即可

           local socketClient = socket.tcp()

--尝试连接指定服务器

           if socketClient:connect(testip,testport) then

--连接成功

               log.info("longlink.socketClient","connect success")

               retryConnectCnt = 0 --失败次数清零

--循环处理接收和发送的数据

               while true do

                   if not inMsgProcess(socketClient) then  --接收消息处理函数

                       log.error("longlink.inMsgProcess error")

break

end

                   if not outMsgprocess(socketClient) then --发送消息处理函数

                       log.error("longlink.outMsgprocess error")

break

end

end

else

               log.info("longlink.socketClient","connect fail")

--连接失败

               retryConnectCnt = retryConnectCnt+1 --失败次数加一

end

           socketClient:close()    --断开socket连接

           if retryConnectCnt>=5 then  --失败次数大于五次了

               link.shut()         --强制断开TCP/UDP连接

               retryConnectCnt=0   --失败次数清零

end

           sys.wait(5000)

else

           retryConnectCnt = 0     --没连上网,失败次数清零

--没连上网

           --等待网络环境准备就绪,超时时间是5分钟

           sys.waitUntil("IP_READY_IND",300000)

--等完了还没连上?

           if not socket.isReady() then

               --进入飞行模式,20秒之后,退出飞行模式

               net.switchFly(true)

               sys.wait(20000)

               net.switchFly(false)

end

end

end

end)

可以看到,在接收和发送函数不返回false的情况下,接收和发送循环会一直进行下去;只有当两个函数之一返回false时,才会触发break导致退出该接收和发送循环。

inMsgProcess(socketClient)函数

这段的代码相对来说比较简单,我们可以直接使用socketClient:recv(毫秒数)来接收我们的TCP消息。
我们在合适的地方,新建一个inMsgProcess(socketClient)函数:

function inMsgProcess(socketClient)

   local result,data

   while true do

       result,data = socketClient:recv(2000)

--接收数据

       if result then  --接收成功

           log.info("longlink.inMsgProcess",data)

           --处理data数据,现在还没代码,空着

       else    --接收失败

break

end

end

   --返回结果,处理成功返回true,处理出错返回false

   return result or data=="timeout"

end

这段代码就是循环获取socket消息,如果没获取到,socketClient:recv(2000)就会返回false,"timeout";如果获取到了,就会返回true,获取到的数据字符串;如果返回了false,不为"timeout",则表示数据处理出错,说明socket连接有了什么问题

细心的读者可能看出来了,如果接收函数一直在2秒内有接收到数据,那么这段函数会永远无限循环下去,没办法到达outMsgprocess(socketClient)函数进行发送数据的操作,所以我们先去讲outMsgprocess(socketClient)函数的实现过程,再回来改进inMsgProcess(socketClient)函数

outMsgprocess(socketClient)函数

由于发送函数在socket线程中是一个循环的小部分,所以我们要建立一个消息发送的队列:有要发送的发数据时,将数据放到这个队列中;等运行到outMsgprocess(socketClient)函数时,将队列中的数据一个一个发出去

首先我们要建一个放这种队列的数组,在合适位置声明一下这个数组:

--数据发送的消息队列

local msgQuene = {}

接着我们构造一个可以往数组里插入数据的函数,table.insert()可以向数组添加数据,所以我们新建一个insertMsg函数:

local function insertMsg(data)

   table.insert(msgQuene,data)

end

还记得上面说过的消息接收函数函数会永远无限循环下去的问题吗?我们在合适的地方新建一个判断发送消息队列是否为空的函数:

function waitForSend()

   return #msgQuene > 0

end

在数组有数据时,这个函数会返回true,我们可以将inMsgProcess(socketClient)接收到数据后的代码添加一行判断发送队列是否有数据的代码,当检测到发送队列有数据时,就立即退出接收函数,转而去进行发送动作,接收函数最终改为了这样:

function inMsgProcess(socketClient)

   local result,data

   while true do

       result,data = socketClient:recv(2000)

--接收到数据

       if result then  --接收成功

           log.info("longlink.inMsgProcess",data)

           --处理data数据,现在还没代码,空着

           --如果msgQuene中有等待发送的数据,则立即退出本循环

           if waitForSend() then return true end

       else    --接收失败

break

end

end

   --返回结果,处理成功返回true,处理出错返回false

   return result or data=="timeout"

end

最后我们终于可以开始写消息发送函数了,整体的函数就是检查队列是否为空,不为空的话就发一条消息并将其从队列中删除,然后重复这一操作,函数代码如下:

function outMsgprocess(socketClient)

--队列中有消息

   while #msgQuene>0 do

--获取消息,并从队列中删除

       local outMsg = table.remove(msgQuene,1)

--发送这条消息,并获取发送结果

       local result = socketClient:send(outMsg)

       --发送失败的话立刻返回nil(等同于false)

       if not result then return end

end

   return true

end

2.2.7完成基本的socket线程

经过上述的更改,最终,longlink.lua已经实现了连接服务器并自动处理错误的功能,并且预留了消息接收以及向发送队列添加数据的接口,文件的所有代码如下:

longlink.lua

module(...,package.seeall)

require"socket"

--测试用的服务器信息

local testip,testport = "180.97.81.180","50798"

--数据发送的消息队列

local msgQuene = {}

local function insertMsg(data)

   table.insert(msgQuene,data)

end

function waitForSend()

   return #msgQuene > 0

end

function outMsgprocess(socketClient)

--队列中有消息

   while #msgQuene>0 do

--获取消息,并从队列中删除

       local outMsg = table.remove(msgQuene,1)

--发送这条消息,并获取发送结果

       local result = socketClient:send(outMsg)

       --发送失败的话立刻返回nil(等同于false)

       if not result then return end

end

   return true

end

function inMsgProcess(socketClient)

   local result,data

   while true do

       result,data = socketClient:recv(2000)

--接收到数据

       if result then  --接收成功

           log.info("longlink.inMsgProcess",data)

           --处理data数据,现在还没代码,空着

           --如果msgQuene中有等待发送的数据,则立即退出本循环

           if waitForSend() then return true end

       else    --接收失败

break

end

end

   --返回结果,处理成功返回true,处理出错返回false

   return result or data=="timeout"

end

--启动socket客户端任务

sys.taskInit(

function()

   local retryConnectCnt = 0   --失败次数统计

   while true do

       --是否获取到了分配的ip(是否连上网)

       if socket.isReady() then

           --新建一个socket对象,如果是udp只需要把tcp改成udp即可

           local socketClient = socket.tcp()

--尝试连接指定服务器

           if socketClient:connect(testip,testport) then

--连接成功

               log.info("longlink.socketClient","connect success")

               retryConnectCnt = 0 --失败次数清零

--循环处理接收和发送的数据

               while true do

                   if not inMsgProcess(socketClient) then  --接收消息处理函数

                       log.error("longlink.inMsgProcess error")

break

end

                   if not outMsgprocess(socketClient) then --发送消息处理函数

                       log.error("longlink.outMsgprocess error")

break

end

end

else

               log.info("longlink.socketClient","connect fail")

--连接失败

               retryConnectCnt = retryConnectCnt+1 --失败次数加一

end

           socketClient:close()    --断开socket连接

           if retryConnectCnt>=5 then  --失败次数大于五次了

               link.shut()         --强制断开TCP/UDP连接

               retryConnectCnt=0   --失败次数清零

end

           sys.wait(5000)

else

           retryConnectCnt = 0     --没连上网,失败次数清零

--没连上网

           --等待网络环境准备就绪,超时时间是5分钟

           sys.waitUntil("IP_READY_IND",300000)

--等完了还没连上?

           if not socket.isReady() then

               --进入飞行模式,20秒之后,退出飞行模式

               net.switchFly(true)

               sys.wait(20000)

               net.switchFly(false)

end

end

end

end)

烧录到模块中,可以得到正常的连接结果:

2.3 实现协议需求

2.3.1心跳包需求

这个需求极其简单,只需要建立一个新的线程,在联网后往消息队列中添加数据即可,代码如下:

--启动心跳包任务

sys.taskInit(

function()

   while true do

       if socket.isReady() then    --连上网再开始运行

           insertMsg("heart beat") --队列里塞个消息

           sys.wait(10000)         --等待10秒

       else    --没连上网别忘了延时!不然会陷入while true死循环,导致模块无法运行其他代码

           sys.wait(1000)          --等待1秒

end

end

end)

2.3.2收到back开头的数据要回复相同的数据

这一条功能十分简单,只需要在inMsgProcess()函数中,写了--处理data数据,现在还没代码,空着这段注释的地方添加相应代码即可,代码如下:

--处理data数据

if data:sub(1,4) == "back" then --收到back开头的数据要回复相同的数据

   insertMsg(data)

end

2.3.3客户端收到bin要回复二进制数组0x11 0x22 0x33

这个功能和上面差不多,返回的东西不一样而已,拼接字节码我们可以用pack,也可以直接用fromHex,下面两种方法都示范一下:

pack方式:

if data == "bin" then --收到bin要回复二进制数组0x11 0x22 0x33

   insertMsg(pack.pack(">bbb",0x11,0x22,0x33))

end

fromHex方式:

if data == "bin" then --收到bin要回复二进制数组0x11 0x22 0x33

   insertMsg(string.fromHex("112233"))

end

2.3.4收到time要回复当前时间的时间戳字符串

进行这个操作,要在开机的时候先同步一下时间,我们可以使用demo中的同步ntp来实现。在main.lua中的启动系统框架上方添加两行同步代码即可:

require"ntp"

ntp.timeSync()

接着和上面一样,在消息接收处返回需要的数据即可:

if data == "time" then --收到time要回复当前时间的时间戳字符串

   insertMsg(tostring(os.time()))

end

2.4完整代码

经过上面的删删改改,功能以及基本实现了,整个文件的代码如下:

longlink.lua

module(...,package.seeall)

require"socket"

--测试用的服务器信息

local testip,testport = "180.97.81.180","50798"

--数据发送的消息队列

local msgQuene = {}

local function insertMsg(data)

   table.insert(msgQuene,data)

end

function waitForSend()

   return #msgQuene > 0

end

function outMsgprocess(socketClient)

--队列中有消息

   while #msgQuene>0 do

--获取消息,并从队列中删除

       local outMsg = table.remove(msgQuene,1)

--发送这条消息,并获取发送结果

       local result = socketClient:send(outMsg)

       --发送失败的话立刻返回nil(等同于false)

       if not result then return end

end

   return true

end

function inMsgProcess(socketClient)

   local result,data

   while true do

       result,data = socketClient:recv(2000)

--接收到数据

       if result then  --接收成功

           log.info("longlink.inMsgProcess",data)

           --处理data数据

           if data:sub(1,4) == "back" then --收到back开头的数据要回复相同的数据

               insertMsg(data)

           elseif data == "bin" then --收到bin要回复二进制数组0x11 0x22 0x33

               insertMsg(string.fromHex("112233"))

           elseif data == "time" then --收到time要回复当前时间的时间戳字符串

               insertMsg(tostring(os.time()))

end

           --如果msgQuene中有等待发送的数据,则立即退出本循环

           if waitForSend() then return true end

       else    --接收失败

break

end

end

   --返回结果,处理成功返回true,处理出错返回false

   return result or data=="timeout"

end

--启动socket客户端任务

sys.taskInit(

function()

   local retryConnectCnt = 0   --失败次数统计

   while true do

       --是否获取到了分配的ip(是否连上网)

       if socket.isReady() then

           --新建一个socket对象,如果是udp只需要把tcp改成udp即可

           local socketClient = socket.tcp()

--尝试连接指定服务器

           if socketClient:connect(testip,testport) then

--连接成功

               log.info("longlink.socketClient","connect success")

               retryConnectCnt = 0 --失败次数清零

--循环处理接收和发送的数据

               while true do

                   if not inMsgProcess(socketClient) then  --接收消息处理函数

                       log.error("longlink.inMsgProcess error")

break

end

                   if not outMsgprocess(socketClient) then --发送消息处理函数

                       log.error("longlink.outMsgprocess error")

break

end

end

else

               log.info("longlink.socketClient","connect fail")

--连接失败

               retryConnectCnt = retryConnectCnt+1 --失败次数加一

end

           socketClient:close()    --断开socket连接

           if retryConnectCnt>=5 then  --失败次数大于五次了

               link.shut()         --强制断开TCP/UDP连接

               retryConnectCnt=0   --失败次数清零

end

           sys.wait(5000)

else

           retryConnectCnt = 0     --没连上网,失败次数清零

--没连上网

           --等待网络环境准备就绪,超时时间是5分钟

           sys.waitUntil("IP_READY_IND",300000)

--等完了还没连上?

           if not socket.isReady() then

               --进入飞行模式,20秒之后,退出飞行模式

               net.switchFly(true)

               sys.wait(20000)

               net.switchFly(false)

end

end

end

end)

--启动心跳包任务

sys.taskInit(

function()

   while true do

       if socket.isReady() then    --连上网再开始运行

           insertMsg("heart beat") --队列里塞个消息

           sys.wait(10000)         --等待10秒

       else    --没连上网别忘了延时!不然会陷入while true死循环,导致模块无法运行其他代码

           sys.wait(1000)          --等待1秒

end

end

end)

main.lua

PROJECT = "SOCKET-TEST"

VERSION = "1.0.0"

require "log"

LOG_LEVEL = log.LOGLEVEL_TRACE

require "sys"

--每1分钟查询一次GSM信号强度,每1分钟查询一次基站信息

require "net"

net.startQueryAll(60000, 60000)

--加载硬件看门狗功能模块

require "wdt"

wdt.setup(pio.P0_30, pio.P0_31)

--加载网络指示灯功能模块

require "netLed"

netLed.setup(true,pio.P1_1)

require"longlink"

require"ntp"

ntp.timeSync()

--启动系统框架

sys.init(0, 0)

sys.run()

3

验证功能

把最终代码烧录进去,按需求测试即可:

很明显,功能符合预期。

今天的内容就讲到这里,如有错误或疑问请在文后留言,感谢大家的关注!

更多福利,敬请期待

Luat相关教程文章:

Luat系列官方教程1:下载调试工具LuaTools的使用指南

Luat系列官方教程2:控制LED小灯

Luat系列官方教程3:Luat程序的基本时序

Luat系列官方教程4:学会使用并看懂Luatools的trace信息

Luat小企业系列原创文章:

小企业怎么寻找生意的蓝海?

小企业2B新产品怎样定价?

小企业的2B业务怎么获客(一)?

合宙Luat将陆续推出小企业系列原创作品,敬请关注!

▼Luat热文推荐▼

>小企业怎么寻找生意的蓝海?

>NB-IoT,未来的物联网脊梁,还是扶不起的阿斗?

>2019年最值得工作和落户的几个城市,有你的家乡吗?

>为什么说『 物联网通信模块业务』没有前途?

>4G模块价格进入2位数时代,合宙Air720模块正式量产发货

>NB向左,GPRS向右,谁会率先突破百亿连接数?

>GPRS模块为什么会低至十几元?我所经历的物联网模块国产化过程

socket 获取回传信息_Luat系列官方教程5:Socket代码详解相关推荐

  1. socket 获取回传信息_基于netty框架的socket长连接负载均衡解决方案 oswl

    前言 物联网如今是一个大的趋势,但是概念还比较新颖.大家对这一块的技术积累也比较匮乏,借此前段时间摩拜单车出现了大规模瘫痪的现象.我们今天来讨论一下物联网项目的开发方式. 关于tcp/ip 相关的知识 ...

  2. socket 获取回传信息_java中使用网络通信(Socket)来传输对象

    服务端:package Work1; /** * 通过Socket通讯来传输对象序列 * 通过服务端接受到的流来恢复对象并通过toString显示出来对象的信息 */ import java.io.* ...

  3. 如何创建一个百分百懂你的产品推荐系统 | 深度教程(附代码详解)

    (图片由AI科技大本营付费下载自视觉中国) 来源 | 读芯术(ID:AI_Discovery) 你也许每天都会逛一逛电子商务网站,或者从博客.新闻和媒体出版物上阅读大量文章.浏览这些东西的时候,最令读 ...

  4. 微信小程序使用百度api获取天气信息 —— 微信小程序教程系列(16)

    之前已经介绍过,如何使用百度地图api来获取地理位置信息 微信小程序的百度地图获取地理位置 -- 微信小程序教程系列(15) 下面介绍使用百度api来获取天气信息. 1> 第一步:先到百度开放平 ...

  5. 大疆 DJI mobile SDK系列详细教程——运行实例代码(跑通大疆官方提供Mobile SDK里的sample code)

    大疆 DJI mobile SDK系列详细教程--运行实例代码(跑通大疆官方提供Mobile SDK里的sample code) 文章目录 一.官方文献与资源地址 二.操作步骤 提示:昨天在尝试跑通大 ...

  6. python爬虫获取服务器信息,通过python自动化获取服务器信息,并写入到excel(示例代码)...

    简介这篇文章主要介绍了通过python自动化获取服务器信息,并写入到excel(示例代码)以及相关的经验技巧,文章约943字,浏览量170,点赞数4,值得参考! 博主目前在电信外包工作,比较坑,因为涉 ...

  7. JVM调优系列--Java命令选项(参数)--大全/详解/常用

    原文网址:JVM调优系列--Java命令选项(参数)--大全/详解/常用_IT利刃出鞘的博客-CSDN博客 简介 说明        本文介绍Java的java命令用法,包括:常用用法.选项大全. J ...

  8. Nmap扫描教程之基础扫描详解

    Nmap扫描教程之基础扫描详解 Nmap扫描基础扫描 当用户对Nmap工具了解后,即可使用该工具实施扫描.通过上一章的介绍,用户可知Nmap工具可以分别对主机.端口.版本.操作系统等实施扫描.但是,在 ...

  9. python split函数 空格_最易懂的Python新手教程:从基础语法到代码详解

    导读:本文立足基础,讲解Python和PyCharm的安装,及Python最简单的语法基础和爬虫技术中所需的Python语法. 作者:罗攀 蒋仟 如需转载请联系华章科技 本文涉及的主要知识点如下: P ...

最新文章

  1. 如何让文字溢出自动变成省略号
  2. MySQL 多表查询、连接查询(内连接、外连接)
  3. Android获取屏幕实际高度跟显示高度,判断Android设备是否拥有虚拟功能键
  4. 阿里云centos云服务器 - 网站搭建教程
  5. java的隐藏函数_java – 隐藏子级数据成员的父成员函数
  6. Ansible(五)远程创建用户并对密码进行加密设置
  7. mysql - GTID主从复制
  8. Linux---阻塞与非阻塞、同步与异步的区别
  9. 设置,获取和删除Cookies
  10. 《Ray Tracing in One Weekend》——Chapter 0: Overview
  11. Python进阶(十)多进程multiprocessing和subprocess模块
  12. springmvc自定义类型转换
  13. L13-页眉页脚设计加水印
  14. 气传导蓝牙耳机优缺点有哪些?气传导耳机科普及推荐
  15. 微星GT77HX-13VI2023原厂Windows11重建F3一键恢复msirestore功能
  16. Freebase再研究
  17. 学计算机买什么电脑性价比高,学生用什么笔记本电脑好 性价比高的学生笔记本电脑...
  18. html文档网页制作,使用HTML制作网页.doc
  19. 【瑞萨RA_FSP】SCL UART 串口通信
  20. 基于STM32和超声波模块的超声波测距,使用OLED显示距离和温度(附详细源代码)

热门文章

  1. apache+mod_wsgi+django的环境配置
  2. 张小龙公布微信小程序进展 可直接从桌面进入
  3. 传统与敏捷开发的真正区别
  4. 排错之网络映射缓存凭证记录导致备份计划任务失败
  5. css细节(实习第1天)
  6. 互联网 DBA 需要做那些事(转)
  7. 使用Word宏替换Header、Footer等中的文本
  8. .NET定时任务执行管理器开源组件–FluentScheduler
  9. JS(去掉前后空格或去掉所有空格)的用法
  10. C#关于MSMQ通过HTTP远程发送专有队列消息的问题