--[[ Name: client.wx.lua Purpose: Client for wxSocket (ported wx samples) Author: Andre Arpin Modified by: Created: 27-10-2010 Copyright: (c) 2010 Andre Arpin Licence: wxWindows licence --]] -- Load the wxLua module, does nothing if running from wxLua, wxLuaFreeze, or wxLuaEdit package.cpath = package.cpath..";./?.dll;./?.so;../lib/?.so;../lib/vc_dll/?.dll;../lib/bcc_dll/?.dll;../lib/mingw_dll/?.dll;" require("wx") local version = '0.2' local port = 3000 local CLIENT_OPEN = 100 local CLIENT_TEST1 = 101 local CLIENT_TEST2 = 102 local CLIENT_TEST3 = 103 local CLIENT_CLOSE = 104 local CLIENT_TESTURL = 105 local CLIENT_DGRAM = 106 local SOCKET_ID = 107 local TESTURL_ID = 108 local NEWURL_ID = 109 function iff(cond, a, b) if cond then return a else return b end end local m_sock = wx.wxSocketClient() local m_busy = false; --> File menu local editorApp = wx.wxGetApp() editorApp.VendorName = "WXLUA" editorApp.AppName = "wxSocket demo: Client" local menuBar = wx.wxMenuBar() fileMenu = wx.wxMenu { {wx.wxID_ABOUT, "&About\tF1", "About Client" }, {}, {wx.wxID_EXIT, "E&xit\tAlt-X", "Exit Program" } } protocolsMenu = wx.wxMenu { { TESTURL_ID, "Test URL", "Read a file from a URL uses wxURL" }, { NEWURL_ID, "Alternate Test URL", "Read a file from a URL uses wxFileSystem" }, } socketClientMenu= wx.wxMenu{ {CLIENT_OPEN, "&Open session", "Connect to client"}; {}, {CLIENT_TEST1, "Test &1", "Test basic functionality"}; {CLIENT_TEST2, "Test &2", "Test ReadMsg and WriteMsg"}; {CLIENT_TEST3, "Test &3", "Test large data transfer"}; {}, {CLIENT_CLOSE, "&Close session", "Close connection"}; } DatagramSocketMenu = wx.wxMenu{ {CLIENT_DGRAM, "Send Datagram", "Test UDP sockets"} } menuBar:Append(fileMenu, "&File") menuBar:Append(socketClientMenu, "&SocketClient") menuBar:Append(DatagramSocketMenu, "&DatagramSocket") menuBar:Append(protocolsMenu, "&Protocols") local frame = wx.wxFrame(wx.NULL, wx.wxID_ANY, editorApp:GetAppName() .. ' by ' .. editorApp:GetVendorName()) frame:SetMenuBar(menuBar) local statusBar = frame:CreateStatusBar(1) local m_text = wx.wxTextCtrl(frame, wx.wxID_ANY, "Welcome to wxSocket demo: Client\n", wx.wxDefaultPosition, wx.wxDefaultSize, wx.wxTE_MULTILINE + wx.wxTE_READONLY) frame:Show(true) function display(m) m_text:AppendText(tostring(m)..'\n') end frame:Connect(SOCKET_ID, wx.wxEVT_SOCKET, function(event) local s local sock = event.Socket local socketEvent = event.SocketEvent if socketEvent == wx.wxSOCKET_INPUT then s = "wxSOCKET_INPUT" elseif socketEvent == wx.wxSOCKET_LOST then s = "wxSOCKET_LOST" elseif socketEvent == wx.wxSOCKET_CONNECTION then s = "wxSOCKET_CONNECTION" else s = "Unexpected event !" end display ("Socket event: " .. s) end) frame:Connect( wx.wxID_EXIT, wx.wxEVT_COMMAND_MENU_SELECTED, function (event) frame:Close() end) frame:Connect(CLIENT_CLOSE, wx.wxEVT_UPDATE_UI, function (event) event:Enable(m_sock:IsConnected()) end) frame:Connect( CLIENT_CLOSE, wx.wxEVT_COMMAND_MENU_SELECTED, function (event) m_sock:Close() end) frame:Connect( CLIENT_DGRAM, wx.wxEVT_COMMAND_MENU_SELECTED, function (event) wx.wxMessageBox("Sorry not implemented!") end) frame:Connect(wx.wxID_ABOUT, wx.wxEVT_COMMAND_MENU_SELECTED, function (event) wx.wxMessageBox('Client sample.\n\n'.. wxlua.wxLUA_VERSION_STRING.." built with "..wx.wxVERSION_STRING, "About wxLua", wx.wxOK + wx.wxICON_INFORMATION, frame) end ) frame:Connect(CLIENT_OPEN, wx.wxEVT_UPDATE_UI, function (event) event:Enable(not m_sock:IsConnected() and not m_busy) end) frame:Connect(CLIENT_OPEN, wx.wxEVT_COMMAND_MENU_SELECTED, function (event) local addr = wx.wxIPV4address() -- Ask user for server address local hostname = wx.wxGetTextFromUser("Enter the address of the wxSocket demo server:", "Connect ...", "localhost"); addr:Hostname(hostname) addr:Service(port) display("\nTrying to connect (timeout = 10 sec) ..."); m_sock:Connect(addr, false) m_sock:WaitOnConnect(10) if m_sock:IsConnected() then display("Succeeded ! Connection established\n"); else m_sock:Close(); display("Failed ! Unable to connect") wx.wxMessageBox("Can't connect to the specified host", "Alert !"); end UpdateStatusBar(); end) frame:Connect(CLIENT_TEST1, wx.wxEVT_UPDATE_UI, function (event) event:Enable(m_sock:IsConnected() and not m_busy) end) frame:Connect(CLIENT_TEST1, wx.wxEVT_COMMAND_MENU_SELECTED, function (event) -- Disable socket menu entries (exception: Close Session) m_busy = true; UpdateStatusBar(); display("\n=== Test 1 begins ===\n") -- Tell the server which test we are running local c = string.char( 11 * 16 + 14) m_sock:Write(c, 1); -- Send some data and read it back. We know the size of the -- buffer, so we can specify the exact number of bytes to be -- sent or received and use the wxSOCKET_WAITALL flag. Also, -- we have disabled menu entries which could interfere with -- the test, so we can safely avoid the wxSOCKET_BLOCK flag. -- -- First we send a byte with the length of the string, then -- we send the string itself (do NOT try to send any integral -- value larger than a byte "as is" across the network, or -- you might be in trouble! Ever heard about big and little -- endian computers?) m_sock:SetFlags(wx.wxSOCKET_WAITALL) local buf1 = "Test string (less than 256 chars!)" local len = string.char(#buf1) display("Sending a test buffer to the server ...") m_sock:Write(len, 1); m_sock:Write(buf1, #buf1); display(iff(m_sock:Error() , "failed !" , "done\n")) display("Receiving the buffer back from server ...") buf2 = m_sock:Read(#buf1) display(iff(m_sock:Error() , "failed !" , "done\n")) display("Comparing the two buffers ...") if buf1 ~= buf2 then display("failed!\n") display("Test 1 failed !\n") else display("done\n") display("Test 1 passed !\n") end display("=== Test 1 ends ===\n") m_busy = false; UpdateStatusBar(); end) frame:Connect(CLIENT_TEST2, wx.wxEVT_UPDATE_UI, function (event) event:Enable(m_sock:IsConnected() and not m_busy) end) frame:Connect(CLIENT_TEST2, wx.wxEVT_COMMAND_MENU_SELECTED, function (event) -- Disable socket menu entries (exception: Close Session) m_busy = true; UpdateStatusBar(); display("\n=== Test 2 begins ===") -- Tell the server which test we are running local c = string.char( 12 * 16 + 14) m_sock:Write(c, 1); -- Here we use ReadMsg and WriteMsg to send messages with -- a header with size information. Also, the reception is -- event triggered, so we test input events as well. -- -- We need to set no flags here (ReadMsg and WriteMsg are -- not affected by flags) m_sock:SetFlags(wx.wxSOCKET_WAITALL); local msg1 = wx.wxGetTextFromUser( "Enter an arbitrary string to send to the server:", "Test 2 ...", "Yes I like wxWidgets!") display("Sending the string with WriteMsg ...") m_sock:WriteMsg(msg1, #msg1); display(iff(m_sock:Error(), "failed !", "done")) display("Waiting for an event (timeout = 2 sec)\n") -- Wait until data available (will also return if the connection is lost) m_sock:WaitForRead(2); if m_sock:IsData() then display("Reading the string back with ReadMsg ...") local msg2 = m_sock:ReadMsg(#msg1) display(iff(m_sock:Error(), "failed !", "done")) display("Comparing the two buffers ...") if msg1 ~= msg2 then display("failed!") display("Test 2 failed !") display(msg1) display(msg2) else display("done") display("Test 2 passed !") end else display("Timeout ! Test 2 failed.") end display("=== Test 2 ends ===\n") m_busy = false; UpdateStatusBar(); end) frame:Connect(CLIENT_TEST3, wx.wxEVT_UPDATE_UI, function (event) event:Enable(m_sock:IsConnected() and not m_busy) end) frame:Connect(CLIENT_TEST3, wx.wxEVT_COMMAND_MENU_SELECTED, function (event) -- Disable socket menu entries (exception: Close Session) m_busy = true; UpdateStatusBar(); display("\n=== Test 3 begins ===") -- Tell the server which test we are running local c = string.char( 13 * 16 + 14) m_sock:Write(c, 1); -- This test also is similar to the first one but it sends a -- large buffer so that wxSocket is actually forced to split -- it into pieces and take care of sending everything before -- returning. m_sock:SetFlags(wx.wxSOCKET_WAITALL); -- Note that len is in kbytes here! local len = 32; local buf1, buf2 local t = {} for i = 0, len * 1024 do t[i] = string.char(math.mod(i,256)) end buf1 = table.concat(t) display("Sending a large buffer (32K) to the server ..."); m_sock:Write(string.char(len), 1); m_sock:Write(buf1, len * 1024); display(iff(m_sock:Error(), "failed !", "done")) display("Receiving the buffer back from server ..."); buf2 = m_sock:Read(len * 1024); display(iff(m_sock:Error(), "failed !", "done")) display("Comparing the two buffers ..."); if buf1 ~= buf2 then display("failed!") display("Test 3 failed !") else display("done") display("Test 3 passed !") end display("=== Test 3 ends ===") m_busy = false; UpdateStatusBar(); end) function GetUrl() return wx.wxGetTextFromUser("Enter an URL to get", "URL:", "http:--localhost"); end frame:Connect(TESTURL_ID, wx.wxEVT_COMMAND_MENU_SELECTED, function(event) -- Note that we are creating a new socket here, so this -- won't mess with the client/server demo. -- Ask for the URL display("\n=== URL test begins ===") local urlname = GetUrl() -- Parse the URL local url = wx.wxURL(urlname); if url:GetError() ~= wx.wxURL_NOERR then display("Error: couldn't parse URL") display("=== URL test ends ===") return; end -- Try to get the input stream (connects to the given URL) display("Trying to establish connection...") data = url:GetInputStream(); if not data then display("Error: couldn't read from URL") display("=== URL test ends ===") return; end -- Print the contents type and file size display("Contents type: " .. url:GetProtocol():GetContentType() .. "File size: " ..data.Size .. "\nStarting to download...") -- Get the data local fileTest = wx.wxFile("test.url", wx.wxFile.write) local sout = wx.wxFileOutputStream(fileTest) if not sout.Ok then display("Error: couldn't open file for output") display("=== URL test ends ===") return; end data:Read(sout); display("Results written to file: test.url") display("Done.") display("=== URL test ends ===") fileTest:delete() end) frame:Connect(NEWURL_ID, wx.wxEVT_COMMAND_MENU_SELECTED, function(event) display("\n=== URL test begins ===") local file = wx.wxFileSystem():OpenFile(GetUrl()) if not file then display("Could not access URL") return end local data = file:GetStream() local text = {} repeat local c= data.C if data:LastRead() ~= 0 then if c > 0 then text[#text+1]=string.char(c) end end until data:LastRead() == 0 local fileTest = wx.wxFile("test.url", wx.wxFile.write) if not fileTest then display('Could not open "test.url" to output!') display("=== URL test ends ===") return; end fileTest:Write(table.concat(text)) display("Results written to file: test.url") display("Done.") display("=== URL test ends ===") fileTest:delete() end) function UpdateStatusBar() local s if not m_sock:IsConnected() then s = "Not connected" else local addr = wx.wxIPV4address() m_sock:GetPeer(addr) s = addr:Hostname()..' : '..addr:Service() end frame:SetStatusText(s); end -- Setup the event handler and subscribe to most events m_sock:SetEventHandler(frame, SOCKET_ID); m_sock:SetNotify(wx.wxSOCKET_CONNECTION_FLAG + wx.wxSOCKET_INPUT_FLAG + wx.wxSOCKET_LOST_FLAG); m_sock:Notify(true); display('Client ready') UpdateStatusBar(); -- Call wx.wxGetApp():MainLoop() last to start the wxWidgets event loop, -- otherwise the wxLua program will exit immediately. -- Does nothing if running from wxLua, wxLuaFreeze, or wxLuaEdit since the -- MainLoop is already running or will be started by the C++ program. wx.wxGetApp():MainLoop()