一、前言

最近,我在搞服务端的skynet框架,看看以后自己做些作品(skynet框架服务端+Unity客户端)。今天呢,我就先把skynet环境搞一下,讲讲流程,也方便想学习的同学,话不多说,我们开始吧~

二、关于Skynet

skynet是一个轻量级的网络游戏框架,也可用于许多其他领域。

建议大家看下云风的《[Skynet设计综述][Skynet 4]》,这里我不过多赘述,主要讲讲操作流程~

三、Ubuntu虚拟机

skynet需要运行在linuxmacos系统中,这里作为演示,我使用Ubuntu虚拟机。下面我讲下Ubuntu虚拟机的安装过程。

1、Ubuntu系统镜像下载

首先我们需要先下载Ubuntu系统的iso文件,下面这些地址都可以下载,大家选择一个即可:

网易开源镜像:[http://mirrors.163.com/ubuntu-releases/][http_mirrors.163.com_ubuntu-releases]
Ubuntu官方:[http://releases.ubuntu.com/][http_releases.ubuntu.com]
Ubuntu中国官网:[https://ubuntu.com/download/alternative-downloads][https_ubuntu.com_download_alternative-downloads]
中科开源镜像:[http://mirrors.ustc.edu.cn/ubuntu-releases/][http_mirrors.ustc.edu.cn_ubuntu-releases]
阿里开源镜像:[http://mirrors.aliyun.com/ubuntu-releases/][http_mirrors.aliyun.com_ubuntu-releases]
浙江大学开源镜像:[http://mirrors.zju.edu.cn/ubuntu-releases/][http_mirrors.zju.edu.cn_ubuntu-releases]

我以Ubuntu 16.04.7版本为例,地址:[http://mirrors.163.com/ubuntu-releases/16.04.7/][http_mirrors.163.com_ubuntu-releases_16.04.7]

iso文件下载到本地,

2、VirtualBox虚拟机软件

有了iso文件,需要将其安装到虚拟机中,而虚拟机需要运行在虚拟机软件上,所以,我们还需要先安装一个虚拟机软件。
虚拟机软件大家常用的是VMWare,这里我强烈推荐另一款虚拟机软件:VirtualBox,它轻量、开源免费,对于个人学习使用完全足够,五星推荐~

关于VirtualBox:
VirtualBox是一款开源虚拟机软件。VirtualBox是由德国Innotek公司开发,由Sun Microsystems公司出品的软件,使用Qt编写,在 SunOracle收购后正式更名成 Oracle VM VirtualBox
VirtualBox号称是最强的免费虚拟机软件,它不仅具有丰富的特色,而且性能也很优异!它简单易用,可虚拟的系统包括Windows(从Windows 3.1Windows 10Windows Server 2012,所有的Windows系统都支持)、Mac OS XLinuxOpenBSDSolarisIBM OS2甚至Android等操作系统!使用者可以在VirtualBox上安装并且运行上述的这些操作系统!

2.1、VirtualBox下载

VirtualBox我们可以从官网下载到,地址:[https://www.virtualbox.org/][https_www.virtualbox.org]

选择windows版本,点击下载,

下载完毕,

2.2、VirtualBox安装

双击安装包运行安装,过程没有什么特别的,这里不赘述~
安装成功后打开VirtualBox,界面如下:

2.3、创建虚拟机

点击菜单控制/新建

填写虚拟机名称,设置虚拟机保存路径,如下,我设置为E:\ubuntu16

设置内存大小,建议分配2G内存,

创建虚拟硬盘,

建议分配10G的虚拟硬盘空间,

虚拟机创建完成,如下

3、载入Ubuntu iso镜像

点击启动虚拟机,会提示选择启动盘,点击下面的小按钮,

点击注册,

选择我们刚刚下载的iso系统镜像文件,打开,可以看到列表中出现了我们的镜像,选中它,

点击启动,即可进入系统安装。

4、Ubuntu系统安装过程

点击Install Ubuntu

点击Continue

点击Install Now

此时会弹个提示框,点击Continue

时区填写China Time,然后点击Continue

语言默认English,点击Continue

接着输入账号密码,后面进入系统的时候要用到,这里提示我的密码弱(Weak password),由于只是自己学习使用,密码弱也没什么关系,点击Continue

接着就是耐心等待它安装,

完成后,会提示需要重启,点击Restart Now

进入下面这个界面时,按一下回车键,

输入刚刚设置的密码,

顺利进入系统,

四、安装必要的工具

1、安装git

我们需要通过git来下载skynet,所以必须先安装git,我们先打开终端,如下:

在终端输入下面的命令:(注意按回车后需要输入一次密码,并且密码不会显示出来,不要怀疑你的键盘)

sudo apt-get install git

如下:
安装完毕后,输入下面的命令检查下git是否安装成功了,

git --version

如果输出了版本号,则说明git已经安装成功了,

2、安装autoconf

编译skynet需要用到autoconf,在终端输入下面的命令来安装autoconf

sudo apt-get install autoconf

如下:

安装完毕后,输入autoconf --version按回车,如果能输出版本号,则说明安装成功了,

3、安装gcc

编译skynet需要用到gcc,因为Ubuntu默认安装了gcc,所以我们这里就不用重复安装了,可以在终端中输入gcc --version,如果能输出版本号,则说明已经安装了gcc

如果提示没有gcc,则执行sudo apt-get install gcc进行安装即可。

五、下载Skynet源码

在终端中执行下面的命令,

git clone https://gitee.com/mirrors/skynet.git

如下:

下载完毕后,我们打开文件夹浏览器,可以在Home目录中看到多了一个skynet文件夹,

进入skynet文件夹,就可以看到框架源码啦,

六、编译Skynet源码

在终端中进入skynet目录,

cd skynet

如下:

然后执行下面的命令:

make linux

此处你可能会报错,如下:

这是因为过程中去gitub下载jemalloc失败了,可以多试几次,编译成功后显示的输出内容如下:

我们可以在skynet目录中看到生成了一个可执行文件:skynet,如下:

七、运行Skynet案例

在终端中进入skynet目录后,执行下面的命令,

./skynet example/config

如下,可以看到服务启动成功了,

接下来,我们开启一个新的终端,

运行一个客户端来测试一下,先cd skynet进入目录,
然后执行如下命令:

./3rd/lua/lua example/client.lua

如下,每隔5秒就会给服务端发送一个heartbeat心跳包,

我们可以输入hello,服务器会回应一个world,如下:

八、写个Demo

想要自己写个Demo,得先知道skynet是如何工作的。

1、配置文件

运行skynet时,需要制定一个配置文件,例:

./skynet example/config

我们先看看这个config文件里面是啥,进入examples目录,打开config文件,

config文件内容如下:

include "config.path"-- preload = "./examples/preload.lua"  -- run preload.lua before every lua service run
thread = 8
logger = nil
logpath = "."
harbor = 1
address = "127.0.0.1:2526"
master = "127.0.0.1:2013"
start = "main"   -- main script
bootstrap = "snlua bootstrap"    -- The service for bootstrap
standalone = "0.0.0.0:2013"
-- snax_interface_g = "snax_g"
cpath = root.."cservice/?.so"
-- daemon = "./skynet.pid"

第一行引用了config.path文件,我们打开config.path文件,

内容如下:

root = "./"
luaservice = root.."service/?.lua;"..root.."test/?.lua;"..root.."examples/?.lua;"..root.."test/?/init.lua"
lualoader = root .. "lualib/loader.lua"
lua_path = root.."lualib/?.lua;"..root.."lualib/?/init.lua"
lua_cpath = root .. "luaclib/?.so"
snax = root.."examples/?.lua;"..root.."test/?.lua"

现在,我们把两个文件合在一起看,如下:

root = "./"
luaservice = root.."service/?.lua;"..root.."test/?.lua;"..root.."examples/?.lua;"..root.."test/?/init.lua"
lualoader = root .. "lualib/loader.lua"
lua_path = root.."lualib/?.lua;"..root.."lualib/?/init.lua"
lua_cpath = root .. "luaclib/?.so"
snax = root.."examples/?.lua;"..root.."test/?.lua"-- preload = "./examples/preload.lua" -- run preload.lua before every lua service run
thread = 8
logger = nil
logpath = "."
harbor = 1
address = "127.0.0.1:2526"
master = "127.0.0.1:2013"
start = "main"   -- main script
bootstrap = "snlua bootstrap"    -- The service for bootstrap
standalone = "0.0.0.0:2013"
-- snax_interface_g = "snax_g"
cpath = root.."cservice/?.so"
-- daemon = "./skynet.pid"

我这里对几个重要的参数做一下说明:

参数 描述
luaservice 服务脚本路径,包括skynet框架自带的一些服务和自己写的服务
lualoader lua脚本加载器,指定skynetloader.lua
lua_path 程序加载lua脚本时,会搜索这个lua_path配置的路径
lua_cpath C语言编写的程序库(.so文件)的路径
thread 启用的工作线程数量,一般配置为CPU核心数
harbor 主从节点模式。skynet初期提供了master/slave集群模式,后来提供了更适用的cluster集群模式,建议使用cluster模式,配0
start 主服务入口
cpath C语言编写的服务模块的路径

画个图,强化记忆:

2、规范目录结构

上面介绍了配置文件,现在我们可以自己写一个配置文件啦,不过,实际项目中,一般会先规范一下目录结构,我们把根目录重命名为game,新建一些子文件夹:

文件夹 说明
etc 存放配置文件
luaclib 存放一些C模块(.so文件)
lualib 存放lua模块
service 存放各服务的lua代码
skynet 存放skynet框架(存放我们刚刚下载的skynet框架源码)

最终如下:

3、自己写个配置文件

我们在etc目录中新建一个config.node1配置,如下:

因为我们把skynet框架源码丢在了skynet子目录中,所以我们配置路径的时候需要加多一层skynet,最终config.node1配置如下:

thread = 8
cpath = "./skynet/cservice/?.so"
bootstrap = "snlua bootstrap"start = "main"
harbor = 0lualoader = "./skynet/lualib/loader.lua"luaservice = "./service/?.lua;" .. "./service/?/init.lua;" .. "./skynet/service/?.lua;"lua_path = "./etc/?.lua;" .. "./lualib/?.lua;" .. "./skynet/lualib/?.lua;" .. "./skynet/lualib/?/init.lua;"lua_cpath = "./luaclib/?.so;" .. "./skynet/luaclib/?.so"

4、主服务

上面我们配置的主服务是main

start = "main"

它会去配置的luaservice路径中查找一个main.lua脚本,

luaservice = "./service/?.lua;" .. "./service/?/init.lua;" .. "./skynet/service/?.lua;"

框架会去启动这个main服务,我们现在还没有这个main.lua脚本,现在我们就来写这个main.lua脚本吧~

进入service目录,创建main.lua脚本,代码如下:

local skynet = require "skynet"
skynet.start(function()skynet.error("[start main] hello world")-- TODO 启动其他服务skynet.exit()
end)

上面我们用到了skynet的三个API,如下:

有同学会问了,明明说三个API,怎么列了四个,那个newservice(name, ...)没看到呀!
因为main是主服务,它是由框架来启动的,所以是框架帮我们调用了newservice,如果我们想在主服务中启动其他服务,就要自己调用newservice了。

现在我们测试一下,打开终端,进入game目录,然后执行命令:

./skynet/skynet etc/config.node1

运行效果如下,可以看到main.lua脚本被执行了,输出了[start main] hello world

5、写个打工服务

服务脚本统一放在service目录中,以服务名为文件夹名字创建子目录,打工服务我们取名为worker,所以,我们在service文件夹中新建一个worker目录,

进入worker目录,新建一个init.lua脚本,

init.lua脚本需要实现服务的逻辑,

init.lua代码如下,

-- service/worker/init.lua脚本local skynet = require "skynet"-- 消息响应函数表
local CMD = {}
-- 服务名
local worker_name = ""
-- 服务id
local worker_id = ""
-- 工钱
local money = 0
-- 是否在工作
local isworking = false-- 每帧调用,一帧的时间是0.2秒
local function update(frame)if isworking thenmoney = money + 1skynet.error(worker_name .. tostring(worker_id) .. ", money: " .. tostring(money))end
end-- 定时器,每隔0.2秒调用一次update函数
local function timer()local stime = skynet.now()local frame = 0while true doframe = frame + 1local isok, err = pcall(update, frame)if not isok thenskynet.error(err)endlocal etime = skynet.now()-- 保证0.2秒local waittime = frame * 20 - (etime - stime)if waittime <= 0 thenwaittime = 2endskynet.sleep(waittime)end
end-- 初始化
local function init(name, id)worker_name = nameworker_id = id
end-- 开始工作
function CMD.start_work(source)isworking = true
end-- 停止工作
function CMD.stop_work(source)isworking = false
end-- 调用初始化函数,...是不定参数,会从skynet.newservice的第二个参数开始透传过来
init(...)skynet.start(function ()-- 消息分发skynet.dispatch("lua", function (session, source, cmd, ...)-- 从CMD这个表中查找是否有定义响应函数,如果有,则触发响应函数local func = CMD[cmd]if func thenfunc(source, ...)endend)-- 启动定时器skynet.fork(timer)
end)

注:这里对代码说明一下,timer定时器函数中,waittime代表每次循环等待的时间,由于程序有可能会卡住,我们很难保证 “每隔0.2秒调用一次update” 是精确的,update函数本身执行也需要时间,所以等待时间是0.2减去执行时间,执行时间就是etime - stime

6、在主服务中启动打工服务

我们回到主服务main.lua脚本中,添加一句skynet.newservice调用,如下:

-- main.lua脚本local skynet = require "skynet"skynet.start(function ()skynet.error("[start main] hello world")-- 启动打工服务,其中第二个参数和第三个参数会透传给service/worker/init.lua脚本local worker1 = skynet.newservice("worker", "worker", 1)skynet.exit()
end)

现在我们测试一下,在game目录中执行命令

./skynet/skynet etc/config.node1

运行效果如下,可以看到启动了一个worker服务,

有同学可能会问了,我们调用skynet.newservice时第一个参数是worker,框架怎么知道会去执行service/worker/init.lua脚本呢?
还记得我们的config.node1配置吗,里面的luaservice我们配置了"./service/?/init.lua;",如下:

-- config.node1配置luaservice = "./service/?.lua;" .. "./service/?/init.lua;" .. "./skynet/service/?.lua;"

其中,?符号会匹配服务名,也就是说,当我们调用skynet.newservice("worker")时,框架先去检查./service/worker.lua脚本是否存在,发现不存在,于是接着检查./service/worker/init.lua脚本,发现存在,于是执行./service/worker/init.lua脚本作为worker服务,当然,如果找不到,它就会去检查./skynet/service/worker.lua是否存在了。

另外,newservice的函数原型是newservice(name, ...),我们调用skynet.newservice时可以透传一些参数给服务,比如我们上面的

-- main.lua脚本local worker1 = skynet.newservice("worker", "worker", 1)

第二个参数和第三个参数就会透传给init.lua脚本,我们在init.lua脚本中可以取出来缓存起来,如下:

-- service/worker/init.lua脚本-- 服务名
local worker_name = ""
-- 服务id
local worker_id = ""local function init(name, id)worker_name = nameworker_id = id
endinit(...)

7、在主服务中给打工服务发消息

打工服务中我们定义了两个消息:start_workstop_work,现在我们在主服务中给打工服务发送消息,添加skynet.send调用,如下:

local skynet = require "skynet"skynet.start(function ()skynet.error("[start main] hello world")-- 启动打工服务,其中第二个参数和第三个参数会透传给service/worker/init.lua脚本local worker1 = skynet.newservice("worker", "worker", 1)-- 开始工作skynet.send(worker1, "lua", "start_work")-- 主服务休息2秒,注意,这里是主服务休息2秒,并不会卡住worker服务skynet.sleep(200)-- 停止工作skynet.send(worker1, "lua", "stop_work")skynet.exit()
end)

我们再次执行命令

./skynet/skynet etc/config.node1

运行效果如下,可以看到打工服务开始工作了,2秒赚了10块钱~

8、封装服务类

假设我们现在要再写一个买猫粮的服务,这个时候,可以按照上面的打工服务写一个服务。事实上,每个服务都有一些通用的变量和方法,我们可以封装一个service类,方便复用减少代码量。
我们在lualib目录中新建一个service.lua脚本,

service.lua代码如下,

local skynet = require "skynet"
local cluster = require "skynet.cluster"-- 封装服务类
local M = {-- 服务名name = "",-- 服务idid = 0,-- 退出exit = nil,-- 初始化init = nil,-- 消息响应函数表resp = {},
}-- 输出堆栈
local function tracback(err)skynet.error(tostring(err))skynet.error(debug.traceback())
end-- 消息分发
local dispatch = function (session, address, cmd, ...)-- 从resp表中查找是否存在消息的响应函数local func = M.resp[cmd]if not func thenskynet.ret()returnend-- 调用响应函数local ret = table.pack(xpcall(func, tracback, address, ...))local isok = ret[1]if not isok thenskynet.ret()returnendskynet.retpack(table.unpack(ret, 2))
end-- 初始化
local function init()skynet.error(M.name .. " " .. M.id .. " init")skynet.dispatch("lua", dispatch)if M.init thenM.init()end
end-- 启动服务
function M.start(name, id, ...)M.name = nameM.id = tonumber(id)skynet.start(init)
endreturn M

9、重写打工服务

有了service类,我们写服务的时候,只需要按下面这个模板写就可以了,

local skynet = require "skynet"
local s = require "service"s.init = function()-- 初始化
ends.resp.协议1 = function(source, ...)-- TODO
ends.resp.协议2 = function(source, ...)-- TODO
ends.start(...)

现在,我们重新写一下打工服务,改造后代码如下,我们后面新写服务也按照这个格式来写,统一,方便维护,

local skynet = require "skynet"
local s = require "service"s.money = 0
s.isworking = falses.update = function(frame)if s.isworking thens.money = s.money + 1skynet.error(s.name .. tostring(s.id) .. ", money: " .. tostring(s.money))end
ends.init = function ()skynet.fork(s.timer)
ends.timer = function()local stime = skynet.now()local frame = 0while true doframe = frame + 1local isok, err = pcall(s.update, frame)if not isok thenskynet.error(err)endlocal etime = skynet.now()local waittime = frame * 20 - (etime - stime)if waittime <= 0 thenwaittime = 2endskynet.sleep(waittime)end
ends.resp.start_work = function(source)s.isworking = true
ends.resp.stop_work = function(source)s.isworking = false
ends.start(...)

10、买猫粮服务

打工挣钱,有了钱我们就可以买猫粮啦,我们来写一个买猫粮的服务脚本。
我们在service文件夹中新建一个buy文件夹,如下,

进入buy目录,然后创建一个init.lua脚本,

代码如下:

-- service/buy/init.lua脚本local skynet = require "skynet"
local s = require "service"s.cat_food_price = 5
s.cat_food_cnt = 0s.resp.buy = function (source)-- 先扣费local left_money = skynet.call("worker1", "lua", "change_money", -s.cat_food_price)if left_money >= 0 thens.cat_food_cnt = s.cat_food_cnt + 1skynet.error("buy cat food ok, current cnt: " .. tostring(s.cat_food_cnt))return trueend-- 购买失败,把钱加回去skynet.error("buy failed, money not enough")skynet.call("worker1", "lua", "change_money", s.cat_food_price)return false
ends.start(...)

然后我们给打工服务加多一个消息,

-- service/worker/init.lua脚本s.resp.change_money = function(source, delta)s.money = s.money + deltareturn s.money
end

最后,在主服务中加上买猫粮服务的启动和消息发送,如下:

local skynet = require "skynet"skynet.start(function ()skynet.error("[start main] hello world")-- 启动打工服务local worker1 = skynet.newservice("worker", "worker", 1)-- 启动买猫粮服务local buy1 = skynet.newservice("buy", "buy", 1)-- 开始打工skynet.send(worker1, "lua", "start_work")skynet.sleep(200)-- 结束打工skynet.send(worker1, "lua", "stop_work")-- 买猫粮skynet.send(buy1, "lua", "buy")-- 退出主服务skynet.exit()
end)

好啦,现在我们测试一下,运行效果如下:

可以看到,最后买猫粮成功啦~

九、补充

1、网络模块

写服务端,肯定需要涉及到网络模块,需要用到skynet.socket,案例:

-- main.lualocal skynet = require "skynet"
local socket = require "skynet.socket"local function on_connect(fd, addr)socket.start(fd)while true dolocal readdata = socket.read(fd)if readdata then-- TODO 处理消息-- 回应客户端,把readdata返回给客户端-- socket.write(fd, "server get data: " .. readdata)else-- 连接断开了socket.close(fd)endend
endskynet.start(function()local listenfd = socket.listen("0.0.0.0", 8888)socket.start(listenfd, on_connect)
end)

如果要写一个多人聊天功能的话,只需要把消息广播出去即可,例:

-- main.lualocal skynet = require "skynet"
local socket = require "skynet.socket"local clients = {}local function on_connect(fd, addr)clients[fd] = {}socket.start(fd)while true dolocal readdata = socket.read(fd)if readdata then-- 广播for client_fd, _ in pairs(clients) dosocket.write(client_fd, readdata)end else-- 连接断开了socket.close(fd)clients[fd] = nilendend
endskynet.start(function()local listenfd = socket.listen("0.0.0.0", 8888)socket.start(listenfd, on_connect)
end)

2、节点集群

我上面写的打工赚钱买猫粮,都在同一个节点中,

实际项目中,可能会开启多个节点,两个服务如果在同一个节点中,则通过skynet.sendskynet.call来传递消息,

注:send是发送消息,不会阻塞调用方;call是阻塞调用。

如果在不同的节点中,则需要使用cluster.sendcluster.call

注:send是发送消息,不会阻塞调用方;call是阻塞调用。

对此,我们可以优化一下上文中的service.lua,封装sendcall方法,

-- lualib/service.lualocal cluster = require "skynet.cluster"...function M.call(node, srv, ...)local mynode = skynet.getenv("node")if node == mynode then    return skynet.call(srv, "lua", ...)elsereturn cluster.call(node, srv, ...)end
endfunction M.send(node, srv, ...)local mynode = skynet.getenv("node")if node == mynode then   return skynet.send(srv, "lua", ...)elsereturn cluster.send(node, srv, ...)end
end

我们在etc/config.node1的末尾加多一行

node = "node1"

拷贝一份,重命名为config.node2,把末尾一行改为

node = "node2"

然后,我们改下main.lua脚本,让它在node1节点开启打工服务,在node2节点开启买猫粮节点,最终main.lua脚本如下:

local skynet = require "skynet"
local cluster = require "skynet.cluster"
require "skynet.manager"skynet.start(function ()skynet.error("[start main] hello world")-- 集群配置cluster.reload({node1 = "127.0.0.1:7001",node2 = "127.0.0.1:7002",})local mynode = skynet.getenv("node")if "node1" == mynode then-- 启动集群节点cluster.open("node1")-- node1节点,开启打工服务local worker1 = skynet.newservice("worker", "worker", 1)skynet.name("worker1", worker1)skynet.send(worker1, "lua", "start_work")skynet.sleep(200)skynet.send(worker1, "lua", "stop_work")elseif "node2" == mynode then-- 启动集群节点cluster.open("node2")-- node2节点,开启买猫粮服务local buy1 = skynet.newservice("buy", "buy", 1)-- 请求买猫粮,买三次skynet.send(buy1, "lua", "buy")skynet.send(buy1, "lua", "buy")skynet.send(buy1, "lua", "buy")endskynet.exit()
end)

最后,我们改下buy/init.lua脚本,把skynet.call改成s.call,如下:

local skynet = require "skynet"
local s = require "service"s.cat_food_price = 5
s.cat_food_cnt = 0s.resp.buy = function (source)local left_money = s.call("node1", "worker1", "change_money", -s.cat_food_price)if left_money >= 0 thens.cat_food_cnt = s.cat_food_cnt + 1skynet.error("buy cat food ok, current cnt: " .. tostring(s.cat_food_cnt))return trueendskynet.error("buy cat food failed, money not enough")s.call("node1", "worker1", "change_money", s.cat_food_price)return false
ends.start(...)

好了,现在我们先开启节点1,在终端执行命令

./skynet/skynet etc/config.node1

可以看到节点1开启了打工服务,赚了10块钱


现在我们开启节点2,在终端执行命令

./skynet/skynet etc/config.node2

因为猫粮价格是5块钱一包,所以只能买两次,执行结果如下,可以看到第三次买猫粮失败,因为钱不够了,

3、数据库模块

3.1、安装MySQL

在终端执行命令

sudo apt-get install mysql-server

执行过程中会弹出框让你输入MySQLroot账号的密码,如下,需要输入两次,

安装完毕后,执行mysql --version,如果输出版本号,则说明安装成功了,

3.2、启动MySQL

在终端执行命令

service mysql start

此时会弹出一个框,注意此处输入Ubuntu的开机密码,而不是MySQLroot账号密码哦,点击Authenticate

启动成功后,我们可以通过下面的命令看是否有mysql的进程,

ps -axj |grep mysql

可以看到有mysql进程,说明MySQL服务已经成功启动了,

3.3、关闭MySQL

在终端执行命令

service mysql stop

此时会弹出一个框,此处输入Ubuntu的开机密码,点击Authenticate

执行完毕后,同理,我们可以通过下面的命令查看是否已经没有mysql进程了,

ps -axj |grep mysql
3.4、登录MySQL

在终端执行命令

mysql -h127.0.0.1 -uroot -p你的密码

如下,登录成功,

现在我们可以愉快地使用mysql啦,执行show databases;,查看所有的数据库

3.5、在skynet中操作数据库

我们先在终端手动创建一个数据库,

create database test_db;

如下,

选择test_db

接着再创建一个表,

create table users (id int not null auto_increment,name varchar(30) not null,primary key (id));

如下:

好了,现在我们在skynet中来操作数据库吧,例:

local skynet = require "skynet"
local mysql = require "skynet.db.mysql"skynet.start(function ()skynet.error("[start main] hello world")local db = mysql.connect({host = "127.0.0.1",port = 3306,database = "test_db",user = "root",password = "123456",max_packet_size = 1024 * 1024,on_connect = nil})-- 插入数据local res = db:query("insert into users(name) values (\'linxinfa\')")-- 查询数据res = db:query('select * from users')for i, v in pairs(res) doprint(i, " " .. v.id .. " " .. v.name)endskynet.exit()
end)

我们执行两次,结果如下,可以看到,数据正常写入到数据库中了,成功~

手把手教你从零跑一个Skynet相关推荐

  1. 【游戏开发实战】手把手教你从零跑一个Skynet,详细教程,含案例讲解(服务端 | Skynet | Ubuntu)

    文章目录 一.前言 二.关于Skynet 三.Ubuntu虚拟机 1.Ubuntu系统镜像下载 2.VirtualBox虚拟机软件 2.1.VirtualBox下载 2.2.VirtualBox安装 ...

  2. 手把手教你从零写一个日志框架

    点击上方 "编程技术圈"关注, 星标或置顶一起成长 后台回复"大礼包"有惊喜礼包! 每日英文 Sometimes you have to accept the ...

  3. 手把手教你用C#写一个刷屏软件

    手把手教你用C#写一个刷屏轰炸软件 成品展示 环境准备 新建项目 程序思路 程序部分 完整代码 成品展示 环境准备 VS2019 新建项目 打开界面绘制 打开工具箱开始放置按钮标签以及文本框 最后设计 ...

  4. python界面设计-手把手教你用Python设计一个简单的命令行界面

    原标题:手把手教你用Python设计一个简单的命令行界面 对 Python 程序来说,完备的命令行界面可以提升团队的工作效率,减少调用时可能碰到的困扰.今天,我们就来教大家如何设计功能完整的 Pyth ...

  5. IP门禁:手把手教你用PHP实现一个IP防火墙

    最近我遇到一个需求,我的一台服务器总是遭到端口扫描和恶意登录攻击,对此可以怎么办呢?似乎除了内网隔离.增强密码认证.证书登录.设置防火墙iptables,网上找不到什么别的方案,对了,还用堡垒机的方案 ...

  6. 超详细——手把手教你用threejs实现一个酷炫的模型发光扫描效果(三)

    上一篇文章 voidjay,公众号:web前端可视化超详细--手把手教你用threejs实现一个酷炫的模型发光扫描效果(二) 上一篇文章已完成基本效果的实现,本文则完成整个项目的灵魂:发光效果以及模型 ...

  7. 手把手教你使用nodejs编写一个【使用远程仓库模板,快速创建项目模块】的cli(命令行)

    目录 实现步骤 初始化cli项目 项目目录 创建交互式命令 拉取远程仓库代码,读取仓库中的模板 拉取远程仓库代码 ora 终端 loading 读取仓库中的模板 将选择的模板复制写入目标项目 Comm ...

  8. 如何用python开发游戏_手把手教你用Python完成一个控制台小游戏-阿里云开发者社区...

    很多人想学Python程序设计或者已经了解过一点Python程序设计基础,却没办法开发出一个项目. 今天,通过演示一个简单的控制台小游戏制作,手把手教你如何用Python编写一个游戏程序,即便你是个新 ...

  9. 手把手教你用ESP32 制作一个游戏机,小白可上手

    MAKER: JuanF92/译:趣无尽 相逢已是初识 MicroByte 是一款微型主机,能够运行 NES.GameBoy.GameBoy Color.Game Gear 和 Sega Master ...

最新文章

  1. SAP EWM Table Overview [转]
  2. python script文件夹在哪_Python获取当前脚本文件夹(Script)的绝对路径方法代码
  3. java indexof效率_Java indexOf函数比Rabin-Karp更有效吗?文字搜寻效率
  4. python 保存图片代码_最简单的selenium+Python自动右键保存图片
  5. ubuntu的mysql教程 pdf_Ubuntu上的MySQL字符集设置技巧
  6. vivo V15 Pro渲染图曝光 弹出式摄像头后置三摄
  7. js 滚动条自动滚动到最底部
  8. vc编译 matlab,精通VC与MATLAB联合编程——编译器的使用
  9. 天津事业编计算机岗位综合知识,天津事业编综合知识考什么
  10. GreenPlum数据库调研及架构介绍
  11. 酷派android升级失败,酷派大神X7刷机失败变砖开不了机 救砖教程
  12. XRD测试常见问题及解答(一)
  13. 用Excel进行数据分析练习(一)
  14. openssl编程-DH
  15. loadrunner 12 --录制手机app脚本
  16. PROE CREO 各类3D模型图档大全-8.2G
  17. pytest接口自动化测试框架 | 用python代码测试接口
  18. 将excel表格数据转换为xml文本数据
  19. 零样本分割系列论文(2)Open-Vocabulary Instance Segmentation via Robust Cross-Modal Pseudo-Labeling
  20. 实战 webpack 4 配置解析四

热门文章

  1. 赛灵思 Xilinx UG908 - Vivado Design Suite 用户指南:编程和调试(中文版) (v2020.2)
  2. CTF-攻防世界web新手入门篇
  3. 《python语言程序设计》第1章第7题def功能求pi π 设计思路先分
  4. 统计学习方法第三章 k近邻法
  5. ffmpeg文档37-视频滤镜
  6. 稳健经营,龙光集团成为“中国地产上市公司财务稳健奖”获得者之一
  7. C# winForm 定时拷贝覆盖文件小工具
  8. (工作笔记)jmockit的使用-mock私有变量、静态变量、代码块
  9. 人脸验证与识别——从模型训练到项目部署
  10. 人脸识别之人脸验证(五)--NormFace