Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --[[
- Most of these functions can be called at any time. Send queues what you send until a connection is made.
- On* functions can be overriden
- Only "tcp" and "udp" is supported. Default is tcp.
- There isn't really any difference when it comes to what events and functions to use in this wrapper.
- by default the socket has a 3 second timeout
- the timeout count is started/restarted whenever the mesage is "timeout" and stopped otherwise
- luasocket.debug = true
- will print debug messages about sending and receiving data
- very useful for (duh) debugging!
- -- client
- CLIENT = luasocket.Client("udp" or "tcp") -- this defaults to tcp
- CLIENT:GetTimeoutDuration()
- CLIENT:IsTimingOut()
- CLIENT:IsSending()
- CLIENT:IsConnected()
- CLIENT:SetTimeout(seconds or nil)
- CLIENT:GetTimeout()
- CLIENT:OnReceive(str)
- -- return false to prevent the socket from being removed
- -- if the timeout duration has exceeded the max timeout duration
- CLIENT:OnTimeout(count)
- CLIENT:OnSend(str, bytes)
- CLIENT:OnError(msg)
- CLIENT:OnClose()
- CLIENT:Connect(ip, port)
- --
- -- server
- SERVER = luasocket.Server("udp" or "tcp") -- this defaults to tcp
- SERVER:Host(ip, port)
- -- returning false here will close and remove the client
- -- returning true will call SetKeepAlive true on the client
- SERVER:OnClientConnected(client, ip, port)
- SERVER:OnReceive(str, client)
- SERVER:OnClientClosed(client)
- SERVER:OnClientError(client, msg)
- SERVER:GetClients()
- --
- -- shared
- SHARED:Send(str, instant)
- SHARED:GetIP()
- SHARED:GetPort()
- SHARED:IsValid()
- SHARED:Remove()
- -- These have Get ones as well but they only return something if Set was called. This uses setoption.
- SHARED:SetReuseAddress(val)
- SHARED:SetNoDelay(val)
- SHARED:SetLinger(val)
- SHARED:SetKeepAlive(val)
- --
- ]]
- if luasocket then luasocket.Panic() end
- luasocket = {}
- local check = check or function() end
- luasocket.ltn12 = require("mime")
- luasocket.socket = require("socket")
- luasocket.mime = require("mime")
- luasocket.url = require("socket.url")
- luasocket.http = require("socket.http")
- luasocket.tp = require("socket.tp")
- luasocket.smtp = require("socket.smtp")
- luasocket.ftp = require("socket.ftp")
- function luasocket.DebugPrint(...)
- if luasocket.debug then
- MsgN(string.format(...))
- end
- end
- function luasocket.HeaderToTable(header)
- local tbl = {}
- for line in header:gmatch("(.-)\n") do
- local key, value = line:match("(.+):%s+(.+)\13")
- if key and value then
- tbl[key] = value
- end
- end
- return tbl
- end
- function luasocket.TableToHeader(tbl)
- local str = ""
- for key, value in pairs(tbl) do
- str = str .. tostring(key) .. ": " .. tostring(value) .. "\n"
- end
- return str
- end
- function luasocket.Get(url, callback)
- check(url, "string")
- check(callback, "function", "nil", "false")
- url = url:gsub("http://", "")
- callback = callback or PrintTable
- local host, get = url:match("(.-)/(.+)")
- if not get then
- host = url:gsub("/", "")
- get = ""
- end
- local socket = luasocket.Client("tcp")
- socket:Connect(host, 80)
- socket:Send(("GET /%s HTTP/1.1\r\n"):format(get))
- socket:Send(("Host: %s\r\n"):format(host))
- socket:Send("User-Agent: gmod\r\n")
- socket:Send("\r\n")
- socket:SetTimeoutLength(5)
- function socket:OnReceive(str)
- local header, content = str:match("(.-\10\13)(.+)")
- local ok, err = xpcall(callback, print, {content = content, header = luasocket.HeaderToTable(header), status = status})
- if err then
- print(err)
- end
- self:Remove()
- end
- end
- function luasocket.SendUDPData(ip, port, str)
- if not str and type(port) == "string" then
- str = port
- port = tonumber(ip:match(".-:(.+)"))
- end
- local sck = luasocket.socket.udp()
- local ok, msg = sck:sendto(str, ip, port)
- sck:close()
- if ok then
- luasocket.DebugPrint("SendUDPData sent data to %s:%i (%s)", ip, port, str)
- else
- luasocket.DebugPrint("SendUDPData failed %q", msg)
- end
- return ok, msg
- end
- local receive_types = {all = "*a", lines = "*l"}
- do -- tcp socket meta
- local sockets = {}
- function luasocket.GetSockets()
- return sockets
- end
- function luasocket.Panic()
- for key, sock in pairs(sockets) do
- if sock:IsValid() then
- sock:DebugPrintf("removed from luasocket.Panic()")
- sock:Remove()
- else
- table.remove(sockets, key)
- end
- end
- end
- timer.Create("socket_think", 0, 0, function()
- for key, sock in pairs(sockets) do
- if sock.remove_me then
- setmetatable(sock, getmetatable(NULL))
- end
- if sock:IsValid() then
- local ok, err = pcall(sock.Think, sock)
- if not ok then
- ErrorNoHalt(err)
- end
- else
- table.remove(sockets, key)
- end
- end
- end)
- local function assert(res, err)
- if not res then
- error(res, 3)
- end
- return res
- end
- local function new_socket(override, META, typ)
- typ = typ or "tcp"
- typ = typ:lower()
- if typ == "udp" or typ == "tcp" then
- local self = {}
- self.socket = override or assert(luasocket.socket[typ]())
- self.socket:settimeout(0)
- self.socket_type = typ
- local obj = setmetatable(self, META)
- obj:Initialize()
- table.insert(sockets, obj)
- return obj
- end
- end
- local function remove_socket(self)
- self.socket:close()
- self.remove_me = true
- end
- local options =
- {
- KeepAlive = "keepalive",
- Linger = "linger",
- ReuseAddress = "ReuseAddr",
- NoDelay = "tcp-nodelay",
- }
- local function add_options(tbl)
- for func_name, key in pairs(options) do
- tbl["Set" .. func_name] = function(self, val)
- self.socket:setoption(key, val)
- self:DebugPrintf("option[%q] = %s", key, tostring(val))
- self[func_name] = val
- end
- tbl["Get" .. func_name] = function(self, val)
- return self[func_name]
- end
- end
- end
- do -- client
- local CLIENT = {}
- CLIENT.__index = CLIENT
- add_options(CLIENT)
- function CLIENT:Initialize()
- self.Buffer = {}
- end
- function CLIENT:__tostring()
- return string.format("client_%s[%s][%s]", self.socket_type, self:GetIP() or "none", self:GetPort() or "0")
- end
- function CLIENT:DebugPrintf(fmt, ...)
- luasocket.DebugPrint("%s - " .. fmt, self, ...)
- end
- function CLIENT:Connect(ip, port)
- check(ip, "string")
- check(port, "number")
- self.socket:settimeout(0) -- is this needed?
- if self.socket_type == "tcp" then
- self.socket:connect(ip, port)
- else
- self.socket:setpeername(ip, port)
- end
- self.connecting = true
- end
- function CLIENT:Send(str, instant)
- if self.socket_type == "tcp" then
- if instant then
- local bytes, b, c, d = self.socket:send(str)
- if bytes then
- self:DebugPrintf("sucessfully sent %q", str)
- self:OnSend(str, bytes, b,c,d)
- end
- else
- table.insert(self.Buffer, str)
- end
- else
- self.socket:send(str)
- self:DebugPrintf("sent %q", str)
- end
- end
- CLIENT.ReceiveMode = "all"
- function CLIENT:SetReceiveMode(type)
- self.ReceiveMode = type
- end
- function CLIENT:GetReceiveMode()
- return self.ReceiveMode
- end
- function CLIENT:Think()
- local sock = self.socket
- sock:settimeout(0)
- if self.connecting then
- local res, msg = sock:getpeername()
- if res then
- self:DebugPrintf("connected to %s:%s", res, msg)
- -- ip, port = res, msg
- self.connected = true
- self.connecting = nil
- self:OnConnect(res, msg)
- self:Timeout(false)
- elseif msg == "timeout" or msg == "getpeername failed" then
- self:Timeout(true)
- else
- self:DebugPrintf("errored: %s", msg)
- self:OnError(msg)
- self.connecting = nil
- end
- end
- if self.socket_type == "tcp" and self.connected then
- while true do
- local data = self.Buffer[1]
- if data then
- local bytes, b, c, d = sock:send(data)
- if bytes then
- self:DebugPrintf("sucessfully sent %q", data)
- self:OnSend(data, bytes, b,c,d)
- table.remove(self.Buffer, 1)
- end
- else
- break
- end
- end
- end
- local data, err, partial = sock:receive(receive_types[self.ReceiveMode] or self.ReceiveMode)
- if not data and partial ~= "" then
- data = partial
- end
- --self:DebugPrintf("receive: %s, %s, %s, %i", data or "", err or "", partial or "", self.Timeouts)
- if data then
- self:DebugPrintf("received %q", data)
- self:OnReceive(data)
- self:Timeout(false)
- elseif err == "timeout" then
- self:Timeout(true)
- elseif err == "closed" then
- self:DebugPrintf("wants to close", client)
- self:Remove()
- else
- self:DebugPrintf("errored: %s", err)
- self:OnError(err)
- end
- end
- do -- timeout
- CLIENT.TimeoutLength = 3
- function CLIENT:Timeout(bool)
- if not self.TimeoutLength then return end
- if not bool then
- self.TimeoutStart = nil
- return
- end
- local time = os.clock()
- if not self.TimeoutStart then
- self.TimeoutStart = time + self.TimeoutLength
- end
- local seconds = time - self.TimeoutStart
- if self:OnTimeout(seconds) ~= false then
- if seconds > self.TimeoutLength then
- self:DebugPrintf("timed out")
- self:Remove()
- end
- end
- end
- function CLIENT:GetTimeoutDuration()
- if not self.TimeoutStart then return 0 end
- local t = os.clock()
- return t - self.TimeoutStart
- end
- function CLIENT:IsTimingOut()
- return
- end
- function CLIENT:SetTimeout(seconds)
- self.TimeoutLength = seconds
- end
- function CLIENT:GetTimeout()
- return self.TimeoutLength or math.huge
- end
- end
- function CLIENT:Remove()
- self:OnClose()
- remove_socket(self)
- end
- function CLIENT:IsConnected()
- return self.connected == true
- end
- function CLIENT:IsSending()
- return #self.Buffer > 0
- end
- function CLIENT:GetIP()
- local ip, port = self.socket:getpeername()
- return ip
- end
- function CLIENT:GetPort()
- local ip, port = self.socket:getpeername()
- return ip and port or nil
- end
- function CLIENT:IsValid()
- return true
- end
- function CLIENT:OnTimeout(count) end
- function CLIENT:OnConnect(ip, port) end
- function CLIENT:OnReceive(data) end
- function CLIENT:OnError(msg) end
- function CLIENT:OnSend(data, bytes, b,c,d) end
- function CLIENT:OnClose()
- remove_socket(self)
- end
- function luasocket.Client(typ)
- return new_socket(nil, CLIENT, typ)
- end
- luasocket.ClientMeta = CLIENT
- end
- do -- server
- local SERVER = {}
- SERVER.__index = SERVER
- add_options(SERVER)
- function SERVER:Initialize()
- self.Clients = {}
- end
- function SERVER:__tostring()
- return string.format("server_%s[%s][%s]", self.socket_type, self:GetIP() or "nil", self:GetPort() or "nil")
- end
- function SERVER:DebugPrintf(fmt, ...)
- luasocket.DebugPrint("%s - " .. fmt, self, ...)
- end
- function SERVER:GetClients()
- local copy = {}
- for key, client in pairs(self.Clients) do
- if client.IsValid and client:IsValid() then
- table.insert(copy, client)
- else
- table.remove(self.Clients, key)
- end
- end
- return copy
- end
- function SERVER:Host(ip, port)
- ip = ip or "*"
- port = port or 0
- self.socket:settimeout(0)
- if self.socket_type == "tcp" then
- self.socket:setoption("reuseaddr", true)
- self.socket:bind(ip, port)
- self.socket:listen()
- self.ready = true
- elseif self.socket_type == "udp" then
- self.socket:setsockname(ip, port)
- self.ready = true
- end
- self.socket:settimeout(0)
- end
- SERVER.Bind = SERVER.Host
- function SERVER:Send(data, ip, port)
- check(ip, "string")
- if self.socket_type == "tcp" then
- for key, client in pairs(self:GetClients()) do
- if client:GetIP() == ip and (not port or (port == client:GetPort())) then
- client:Send(data)
- break
- end
- end
- elseif self.socket_type == "udp" then
- check(port, "number")
- self.socket:sendto(data, ip, port)
- end
- end
- local DUMMY = {}
- DUMMY.__tostring = function(s)
- return string.format("client_%s[%s][%s]", "udp", s.ip or "nil", s.port or "nil")
- end
- DUMMY.GetIp = function(s) return s.ip end
- DUMMY.GetPort = function(s) return s.port end
- DUMMY.IsValid = function() return true end
- DUMMY.Close = function() return end
- DUMMY.Remove = function() return end
- local function create_dummy_client(ip, port)
- return setmetatable({ip = ip, port = port}, DUMMY)
- end
- function SERVER:Think()
- if not self.ready then return end
- if self.socket_type == "udp" then
- local data, ip, port = self.socket:receivefrom()
- if ip == "timeout" then return end
- if not data then
- self:DebugPrintf("errored: %s", tostring(ip))
- else
- self:OnReceive(data, create_dummy_client(ip, port))
- self:DebugPrintf("received %s from %s:%s", tostring(data), ip, port)
- end
- elseif self.socket_type == "tcp" then
- local sock = self.socket
- sock:settimeout(0)
- local ls_client, err = sock:accept()
- if ls_client then
- self:DebugPrintf("%s connected", client)
- local client = new_socket(ls_client, luasocket.ClientMeta, "tcp")
- client.connected = true
- table.insert(self.Clients, client)
- local b = self:OnClientConnected(client, client:GetIP(), client:GetPort())
- if b == true then
- client:SetKeepAlive(true)
- elseif b == false then
- client:Remove()
- end
- end
- for _, client in pairs(self:GetClients()) do
- local data, err, partial = client.socket:receive(receive_types[self.ReceiveMode] or self.ReceiveMode)
- -- client:DebugPrintf("data = %s, error = %s, partial = %s", tostring(data), tostring(err), tostring(partial))
- if data then
- self:DebugPrintf("received %s from %s", data, client)
- self:OnReceive(data, client)
- client:OnSend(data)
- elseif err == "closed" then
- self:DebugPrintf("%s wants to close", client)
- if self:OnClientClosed(client) ~= false then
- client:Remove()
- end
- elseif err == "timeout" then
- --client:Timeout() -- hmm
- else
- self:DebugPrintf("%s errored: ", client, err)
- self:OnClientError(client, err)
- client:OnError(err)
- end
- end
- end
- end
- function SERVER:Remove()
- remove_socket(self)
- end
- function SERVER:IsValid()
- return true
- end
- function SERVER:GetIP()
- local ip, port = self.socket:getsockname()
- return ip
- end
- function SERVER:GetPort()
- local ip, port = self.socket:getsockname()
- return ip and port or nil
- end
- function SERVER:OnClientConnected(client, ip, port) end
- function SERVER:OnClientClosed(client) end
- function SERVER:OnReceive(data, client) end
- function SERVER:OnClientError(client, err) end
- function luasocket.Server(typ)
- return new_socket(nil, SERVER, typ)
- end
- luasocket.ServerMeta = SERVER
- end
- end
- function luasocket.DumpDocs()
- local function getargs(func)
- local tbl = func:getparams()
- table.remove(tbl, 1)
- return tbl
- end
- local tbl = {}
- for key, val in pairs(luasocket.ClientMeta) do
- if type(val) == "function" then
- table.insert(tbl, {key = key, str = "CLIENT:" .. key .. "(" .. table.concat(getargs(val), ", ") .. ")\n"})
- end
- end
- for key, val in pairs(luasocket.ServerMeta) do
- if type(val) == "function" then
- table.insert(tbl, {key = key, str = "SERVER:" .. key .. "(" .. table.concat(getargs(val), ", ") .. ")\n"})
- end
- end
- for k1,v1 in pairs(tbl) do
- for k2,v2 in pairs(tbl) do
- if v1.key == v2.key and k1 ~= k2 then
- v1.str = v1.str:gsub("(.-):", "SHARED:")
- print(v1.str)
- tbl[k2] = nil
- break
- end
- end
- end
- table.sort(tbl, function(a, b) return a and b and a.str > b.str end)
- local str = ""
- for k,v in pairs(tbl) do
- str = str .. v.str
- end
- return str
- end
- concommand.Add("luasocket_restart", function() include("luasocket.lua") end)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement