花了两三天时间来学习luasocket,调试通过,运行正常,于是发文分享。
说明:文中的代码其实都是伪代码,只表明了思路,我并没有把调试通过的代码直接放上来。
准备工作
如何加载
luasocket官方文档http://w3.impa.br/~diego/software/luasocket/introduction.html,说的是使用require('socket');
不过经过测试,其实在cocos2d-x中,应该使用require('socket.core');才可以。
实际使用时我用的是local socket = require('socket.core');
版本
socket._VERSION取得的值是“LuaSocket 3.0-rc1”,这比luasocket官方网站的版本(2.0.2)还要新。不过一圈实验下来,官方文档所描述的接口也都可用,没有体会到哪里有变化。
正题
1. 首先是创建socket,这使用socket.tcp()即可。
local clientSocket = socket.tcp();
clientSocket:settimeout(0);
由于手机是作为游戏客户端,所以socket主要就是connect、send、receive这三个函数,外加select。至于bind、accept,这些是用在服务端的,我并没有花心思去体会。
if clientSockend:connect(address, port) == 1 then
cclog('socket connected');
else
isConnecting = true;
end
if isConnecting and socket.gettime() - connectTime > 10 then
-- 连接超时,关闭socket并清理所有变量。
-- 我还没有发现如何通过luasocket的接口来判断连接超时,所以只好用了这个笨办法
return;
end
local arr = {clientSocket};
local r, s, e = socket.select(arr, isConnecting and arr or nil, 0);
if r and #r >= 1 and r[1] == clientSocket then
local recvData, recvError, recvParticialData = clientSocket:receive(99999999);
if recvError == 'closed' then
-- socket已经断开
return;
end
-- 如果是大块数据,可能被拆分成多段,需要多次调用clientSocket:receive才能完成接收。
-- 因此可以把接收到数据先放到recvBuffer缓存起来。
if recvData then
recvBuffer = recvBuffer .. recvData;
elseif recvParticialData then
recvBuffer = recvBuffer .. recvParticialData;
end
-- 处理缓存起来的数据。
processRecvBuffer();
end
if s and #s >= 1 and s[1] == clientSocket then
if isConnecting then
-- 执行到此,说明连接已经成功了
isConnecting = false;
isConnected = true;
end
-- 可以调用send
processSendBuffer();
end
if isConnected then
procsesSendBuffer();
end
while true do
-- 看消息头是否完整。目前我设计消息头有四个字段msgSize, msgType, msgSerial, msgCheck,都是USHORT类型。
-- 这里string.unpack其实是另一个库lpack提供的。cocos2d-x没有自带这个lpack库,不过它只有一个.c文件,很容易就加入到工程中。
local nextReceiveIndex, msgSize, msgType, msgSerial, msgCheck = string.unpack(receiveBuffer, '=HHHH', receiveIndex);
if nextReceiveIndex - receiveIndex < 8 then
break;
end
-- 根据消息头,看消息体是否完整
if msgSize + receiveIndex > #receiveBuffer + 1 then
break;
end
-- 消息体完整。处理这个消息。
local reader = netmsgReadersMap[msgType];
local handler = netmsgHandlersMap[msgType];
if reader and handler then
local netmsg = reader(receiveBuffer, receiveIndex);
if netmsg then
handler(netmsg);
end
end
-- receiveIndex向后移动msgSize个字节。
-- 如果已经处理完所有消息,则清空整个缓冲。
receiveIndex = receiveIndex + msgSize;
if receiveIndex > #receiveBuffer then
receiveBuffer = '';
receiveIndex = 1;
break;
end
end
local num, err, num2 = clientSocket:send(sendBuffer, sendIndex);
if num then
-- 所有数据发送完毕
sendBuffer = '';
sendIndex = 1;
else
-- 只有部分数据发送完毕
sendIndex = num2 + 1;
end