local skynet = require "skynet" require "skynet.manager" local socket = require "skynet.socket" local websocket = require "http.websocket" local socketdriver = require "skynet.socketdriver" local jmutil = require "jmutil" local WATCHDOG = nil local mapFd = {} -- fd -> connection : { fd , client, agent , ip, mode } local clientCount = 0 local MAX_CLIENT = 1024 -- max client local IS_NODELAY = false -- fd解绑agent local function l_unband_agent(c) if c.agent then c.agent = nil c.client = nil end end local function l_close_fd(fd) local c = mapFd[fd] if c then l_unband_agent(c) mapFd[fd] = nil clientCount = clientCount - 1 end end local handler = {} -- 接受链接 function handler.connect(fd) local addr = websocket.addrinfo(fd) skynet.error(string.format("ws connect fd[%s] from[%s] ", tostring(fd), tostring(addr))) if clientCount >= MAX_CLIENT then -- 超出最大承载玩家数 socketdriver.close(fd) skynet.error(string.format("超出最大承载玩家数 MAX_CLIENT[%s]", tostring(MAX_CLIENT))) return end if IS_NODELAY then socketdriver.nodelay(fd) end clientCount = clientCount + 1 local c = { fd = fd, ip = addr } mapFd[fd] = c skynet.send(WATCHDOG, "lua", "socket", "open", fd, addr) end function handler.handshake(fd, header, url) end -- 接收消息 function handler.message(fd, msg) -- skynet.error("message ws ping from: " .. tostring(fd), msg.."\n") if #msg == 0 then return end -- msg is string local data, sz = jmutil.str2lightuserdata(msg) -- recv a package, forward it local c = mapFd[fd] local agent = c and c.agent if agent then skynet.redirect(agent, c.client, "client", fd, data, sz) else -- 首次必须鉴权 skynet.send(WATCHDOG, "lua", "socket", "data", fd, data, sz) end end function handler.ping(fd) skynet.error("ws ping from: " .. tostring(fd) .. "\n") end function handler.pong(fd) skynet.error("ws pong from: " .. tostring(fd)) end function handler.close(fd, code, reason) skynet.error("ws close from: " .. tostring(fd), code, reason) l_close_fd(fd) skynet.send(WATCHDOG, "lua", "socket", "close", fd) end function handler.error(fd) skynet.error("ws error from: " .. tostring(fd)) l_close_fd(fd) skynet.send(WATCHDOG, "lua", "socket", "error", fd) end function handler.warning(fd, size) skynet.send(WATCHDOG, "lua", "socket", "warning", fd, size) end local root = {} -- 开始监听链接 function root.open(source, conf) WATCHDOG = conf.watchdog or source if conf.maxclient then MAX_CLIENT = conf.maxclient end IS_NODELAY = conf.nodelay -- 打开监听链接端口 local address = "0.0.0.0" local port = assert(conf.wsPort) local protocol = conf.protocol or "ws" local fd = socket.listen(address, port) skynet.error( string.format("Listen websocket fd[%s] port[%s] protocol[%s]", tostring(fd), tostring(port), tostring(protocol)) ) -- 开始接受链接 socket.start( fd, function(fd, addr) -- skynet.error(string.format("accept client socket_fd: %s addr:%s", fd, addr)) websocket.accept(fd, handler, protocol, addr) end ) end -- fd已经绑定agent function root.fd_band_agent(source, fd, client, address) if not mapFd[fd] then return false end local c = assert(mapFd[fd]) l_unband_agent(c) c.client = client or 0 c.agent = address or source return true end -- 向fd回应消息 function root.send_to_client(source, fd, msg) if msg == nil then return end websocket.write(fd, msg, "binary") end -- 断开链接 function root.kick(source, fd) websocket.close(fd) end -- 注册接收skynet协议列表 skynet.register_protocol { name = "client", id = skynet.PTYPE_CLIENT } skynet.start( function() skynet.dispatch( "lua", function(session, source, cmd, ...) local f = root[cmd] if not f then skynet.error("wsGate can't dispatch cmd " .. (cmd or nil)) return end if session == 0 then f(source, ...) else skynet.ret(skynet.pack(f(source, ...))) end end ) skynet.register(".ws_gate") end )