|
| 1 | +--[[ |
| 2 | + SocketTcp lua客户端 |
| 3 | + @author zrong (zengrong.net) |
| 4 | + Creation: 2013-11-12 |
| 5 | + 修改自: http://cn.quick-x.com/?topic=quickkydsocketfzl |
| 6 | + 因为发现luasocket receive(number)方式的一个奇惨问题 所以收数据改成了按行读取 |
| 7 | +]] |
| 8 | +local SOCKET_TICK_TIME = 0.1 --SOCKET接收信息轮询时间 |
| 9 | +local SOCKET_RECONNECT_TIME = 5 --socket重连偿试时间时隔 |
| 10 | +local SOCKET_CONNECT_FAIL_TIMEOUT = 3 --多长时间没有连接成功视为失败 |
| 11 | + |
| 12 | +local STATUS_CLOSED = "closed" |
| 13 | +local STATUS_NOT_CONNECTED = "Socket is not connected" |
| 14 | +local STATUS_TIMEOUT = "timeout" |
| 15 | + |
| 16 | +local scheduler = require("framework.scheduler") |
| 17 | +local socket = require "socket" |
| 18 | + |
| 19 | +local SocketTcp = class("SocketTcp") |
| 20 | + |
| 21 | +SocketTcp.EVENT_DATA = "SOCKET_TCP_DATA" |
| 22 | +SocketTcp.EVENT_CLOSE = "SOCKET_TCP_CLOSE" |
| 23 | +SocketTcp.EVENT_CLOSED = "SOCKET_TCP_CLOSED" |
| 24 | +SocketTcp.EVENT_CONNECTED = "SOCKET_TCP_CONNECTED" |
| 25 | +SocketTcp.EVENT_CONNECT_FAILURE = "SOCKET_TCP_CONNECT_FAILURE" |
| 26 | + |
| 27 | +SocketTcp._VERSION = socket._VERSION |
| 28 | +SocketTcp._DEBUG = socket._DEBUG |
| 29 | + |
| 30 | +require("framework.api.EventProtocol").extend(SocketTcp) |
| 31 | + |
| 32 | +function SocketTcp:ctor(__host, __port, __retryConnectWhenFailure) |
| 33 | + self.host = __host |
| 34 | + self.port = __port |
| 35 | + self.tickScheduler = nil -- 消息接收定时器 |
| 36 | + self.reconnectScheduler = nil -- 重连定时器 |
| 37 | + self.connectTimeTickScheduler = nil -- 检测连接超时定时器 |
| 38 | + self.lastHeartbeatTime = os.time() |
| 39 | + self.name = 'SocketTcp' |
| 40 | + self.tcp = nil |
| 41 | + self.isRetryConnect = __retryConnectWhenFailure |
| 42 | + self.isConnected = false |
| 43 | +end |
| 44 | + |
| 45 | +function SocketTcp:setName( name ) |
| 46 | + self.name = name |
| 47 | +end |
| 48 | + |
| 49 | +function SocketTcp:getTime() |
| 50 | + return socket.gettime() |
| 51 | +end |
| 52 | + |
| 53 | +function SocketTcp:connect(__host, __port, __retryConnectWhenFailure) |
| 54 | + if __host then self.host = __host end |
| 55 | + if __port then self.port = __port end |
| 56 | + if __retryConnectWhenFailure ~= nil then self.isRetryConnect = __retryConnectWhenFailure end |
| 57 | + assert(self.host or self.port, "Host and port are necessary!") |
| 58 | + --echoInfo("%s.connect(%s, %d)", self.name, self.host, self.port) |
| 59 | + self.tcp = socket.tcp() |
| 60 | + self.tcp:settimeout(0) |
| 61 | + |
| 62 | + self.tcp:connect(self.host, self.port) |
| 63 | + -- 检测连接是否成功 |
| 64 | + -- SOCKET_CONNECT_FAIL_TIMEOUT 后如果未连接视为连接失败 |
| 65 | + local __connectTimeTick = function () |
| 66 | + --echoInfo("%s.connectTimeTick", self.name) |
| 67 | + if self.isConnected then return end |
| 68 | + self.waitConnect = self.waitConnect or 0 |
| 69 | + self.waitConnect = self.waitConnect + SOCKET_TICK_TIME |
| 70 | + if self.waitConnect >= SOCKET_CONNECT_FAIL_TIMEOUT then |
| 71 | + self.waitConnect = nil |
| 72 | + self:close() |
| 73 | + self:_connectFailure() |
| 74 | + end |
| 75 | + -- 每 SOCKET_TICK_TIME 发送一个值到服务器,发送成功说明服务器连接成功 |
| 76 | + -- 不能采用此种方式,因为服务器会缓存这个1,加到下一次发送的协议的前面,导致下一次发送正常协议就会不返回 |
| 77 | + -- local __succ, __status = self.tcp:send(1) |
| 78 | + -- 改为采用接收包体 |
| 79 | + local __body, __status, __partial = self.tcp:receive("*l") |
| 80 | + --print("receive:", __body, __status, string.len(__partial)) |
| 81 | + if __status == STATUS_TIMEOUT then |
| 82 | + self:_onConnected() |
| 83 | + end |
| 84 | + end |
| 85 | + self.connectTimeTickScheduler = scheduler.scheduleGlobal(__connectTimeTick, SOCKET_TICK_TIME) |
| 86 | +end |
| 87 | + |
| 88 | +-- 直接调用socket的原始发送功能 |
| 89 | +function SocketTcp:send(__data) |
| 90 | + assert(self.isConnected, self.name .. " is not connected.") |
| 91 | + self.tcp:send(__data) |
| 92 | +end |
| 93 | + |
| 94 | +function SocketTcp:close( ... ) |
| 95 | + --echoInfo("%s.close", self.name) |
| 96 | + self.tcp:close(); |
| 97 | + if self.connectTimeTickScheduler then scheduler.unscheduleGlobal(self.connectTimeTickScheduler) end |
| 98 | + if self.tickScheduler then scheduler.unscheduleGlobal(self.tickScheduler) end |
| 99 | + if self.connectTimeTickScheduler then scheduler.unscheduleGlobal(self.connectTimeTickScheduler) end |
| 100 | + self:dispatchEvent({name=SocketTcp.EVENT_CLOSE}) |
| 101 | +end |
| 102 | + |
| 103 | +--用户主动退出 |
| 104 | +function SocketTcp:disconnect() |
| 105 | + self:_disconnect() |
| 106 | + self.isRetryConnect = false --主动性断开不重连 |
| 107 | +end |
| 108 | + |
| 109 | +-------------------- |
| 110 | +-- private |
| 111 | +-------------------- |
| 112 | + |
| 113 | +function SocketTcp:_disconnect() |
| 114 | + self.isConnected = false |
| 115 | + self.tcp:shutdown() |
| 116 | + self:dispatchEvent({name=SocketTcp.EVENT_CLOSED}) |
| 117 | +end |
| 118 | + |
| 119 | +function SocketTcp:_onDisconnect() |
| 120 | + --echoInfo("%s._onDisConnect", self.name); |
| 121 | + self.isConnected = false |
| 122 | + self:dispatchEvent({name=SocketTcp.EVENT_CLOSED}) |
| 123 | + self:_reconnect(); |
| 124 | +end |
| 125 | + |
| 126 | +-- 成功建立连接,取消超时计时器 |
| 127 | +function SocketTcp:_onConnected() |
| 128 | + --echoInfo("%s._onConnectd", self.name) |
| 129 | + self.isConnected = true |
| 130 | + self:dispatchEvent({name=SocketTcp.EVENT_CONNECTED}) |
| 131 | + if self.connectTimeTickScheduler then scheduler.unscheduleGlobal(self.connectTimeTickScheduler) end |
| 132 | + |
| 133 | + local __tick = function() |
| 134 | + while true do |
| 135 | + local __body, __status, __partial = self.tcp:receive("*l")--读取包体 |
| 136 | + --print("body:", __body, "__status:", __status, "__partial:", __partial) |
| 137 | + if __status == STATUS_CLOSED or __status == STATUS_NOT_CONNECTED then --如果读取失败 则跳出 |
| 138 | + self:close() |
| 139 | + if self.isConnected then |
| 140 | + self:_onDisconnect() |
| 141 | + else |
| 142 | + self:_connectFailure() |
| 143 | + end |
| 144 | + return |
| 145 | + end |
| 146 | + if string.len(__partial) == 0 then return end |
| 147 | + self:dispatchEvent({name=SocketTcp.EVENT_DATA, partial=__partial, body=__body}) |
| 148 | + end |
| 149 | + end |
| 150 | + |
| 151 | + --开始读取TCP数据 |
| 152 | + self.tickScheduler = scheduler.scheduleGlobal(__tick, SOCKET_TICK_TIME) |
| 153 | +end |
| 154 | + |
| 155 | +-- 连接失败 |
| 156 | +function SocketTcp:_connectFailure(status) |
| 157 | + --echoInfo("%s._connectFailure", self.name); |
| 158 | + self:dispatchEvent({name=SocketTcp.EVENT_CONNECT_FAILURE}) |
| 159 | + self:_reconnect(); |
| 160 | +end |
| 161 | + |
| 162 | +-- 重连 |
| 163 | +-- 非主动性断开 SOCKET_RECONNECT_TIME 秒后重连 |
| 164 | +--主动性断开不重连 |
| 165 | +function SocketTcp:_reconnect() |
| 166 | + if not self.isRetryConnect then return end -- 不允许重连 |
| 167 | + --echoInfo("%s._reconnect", self.name) |
| 168 | + if self.reconnectScheduler then scheduler.unscheduleGlobal(self.reconnectScheduler) end |
| 169 | + local __doReConnect = function () |
| 170 | + self:connect() |
| 171 | + end |
| 172 | + self.reconnectScheduler = scheduler.performWithDelayGlobal(__doReConnect, SOCKET_RECONNECT_TIME) |
| 173 | +end |
| 174 | + |
| 175 | +return SocketTcp |
0 commit comments