SQLite format 3 @
QM2 -
q {tabletextstextsCREATE TABLE texts(id INTEGER PRIMARY KEY REFERENCES items(id) ON DELETE CASCADE ON UPDATE CASCADE,date BLOB,text TEXT)wtableitemsitemsCREATE TABLE items(id INTEGER PRIMARY KEY,pid INT,ord BLOB,flags INT,name TEXT,[trigger] TEXT,icon TEXT,td,guid BLOB)
` ` s YЎC0OM To test TCP server and client, at first run this function.
It creates TCP server thread that runs until you end this thread.
Then run test_TcpSocket_client, one or more times.
if(getopt(nthreads)>1) ret ;;allow single instance
AddTrayIcon "cut.ico" "test_TcpSocket_server[]Ctrl+click to end."
#compile "__TcpSocket"
TcpSocket x.ServerStart(5032 &sub.OnClientConnected)
#sub OnClientConnected
function TcpSocket&client $clientIp param !*reserved
This function is called in server side, when a client connects.
This function runs in separate thread for each client connection.
out F"SERVER: client connected: {clientIp}"
str s
client.Receive(s 1000)
out F"SERVER: client request: {s}"
client.Send("response 1")
client.Receive(s 1000)
out F"SERVER: client request: {s}"
client.Send("response 2")
#sub OnClickTrayIcon
out 1
̇
HvvH
j xj
̇ }ЎC%8T Connects to server created by test_TcpSocket_server.
out
#compile "__TcpSocket"
TcpSocket x.ClientConnect("localhost" 5032)
x.Send("request 1")
str s
x.Receive(s 1000)
x.Receive(s 11)
x.Receive(s 0 2000)
x.Receive(s 0)
out F"CLIENT: server response: {s}"
x.Send("request 2")
x.Receive(s 1000)
x.Receive(s 0)
out F"CLIENT: server response: {s}"
TA"
if(!ShowDialog(dd &sub.DlgProc &controls win("" "QM_Editor") 0 0 0 0 0 100)) ret
#sub DlgProc
function# hDlg message wParam lParam
TcpSocket- t_server
sel message
case WM_INITDIALOG
case WM_DESTROY
t_server.Close
case WM_COMMAND goto messages2
ret
messages2
sel wParam
case 6 ;;Start
t_server.ServerStart(GetDlgItemInt(hDlg 5 0 0) &sub.OnClientConnected hDlg 1)
EnableWindow lParam 0
EnableWindow id(7 hDlg) 1
case 7 ;;Stop
t_server.Close
EnableWindow lParam 0
EnableWindow id(6 hDlg) 1
ret 1
err+ out _error.description
#sub OnClientConnected
function TcpSocket&client $clientIp hDlg !*reserved
This function is called in server side, when a client connects.
This function runs in separate thread for each client connection.
out F"<>SERVER: client connected: {clientIp}"
str s
client.Receive(s 1000)
out F"<>SERVER: client request:"
out s
s.getwintext(id(15 hDlg))
client.Send(s)
err+ out _error.description
E E 0 S@x㜟\Dialog_Editor
To test:
Run dialog_TcpSocket_server and click Start.
Then run dialog_TcpSocket_client and click Send.
if(getopt(nthreads)>1) ret ;;allow single instance
#compile "__TcpSocket"
str dd=
BEGIN DIALOG
0 "" 0x90C80AC8 0x0 0 0 230 116 "TCP server"
4 Static 0x54000000 0x0 170 8 22 12 "Port"
5 Edit 0x54032000 0x200 196 6 32 14 "port"
6 Button 0x54032000 0x0 6 6 48 14 "Start"
7 Button 0x5C032000 0x0 58 6 48 14 "Stop"
14 Static 0x54000000 0x0 6 30 36 12 "Response"
15 Edit 0x54231044 0x200 46 28 182 85 "res"
END DIALOG
DIALOG EDITOR: "" 0x2030300 "*" "" ""
str controls = "5 15"
str e5por e15res
e5por=5033
minimal HTTP response with no data. For example, for a POST request. Note the two [][] at the end.
e15res="HTTP/1.1 200 OK[][]"
HTTP response with some data. For example, for a GET request.
e15res="HTTP/1.1 200 OK[]Content-Type: text/plain[]Content-Length: 4[][]DA rver"
10 Edit 0x54030080 0x200 32 4 124 14 "server"
11 Static 0x54000000 0x0 4 26 26 13 "Data"
12 Edit 0x54231044 0x200 32 22 196 84 "data"
13 Button 0x54032000 0x0 92 112 48 14 "Send"
END DIALOG
DIALOG EDITOR: "" 0x2030300 "*" "" ""
str controls = "5 10 12"
str e5por e10ser e12dat
e5por=5033
e10ser="localhost"
send a HTTP POST request
e12dat=
POST / HTTP/1.1
User-Agent: QM2
Content-Type: text/plain
Content-Length: 4
DATA
if(!ShowDialog(dd &sub.DlgProc &controls win("" "QM_Editor") 0 0 0 0 0 400)) ret
#sub DlgProc
function# hDlg message wParam lParam
sel message
case WM_INITDIALOG
case WM_DESTROY
case WM_COMMAND goto messages2
ret
messages2
sel wParam
case 13 ;;Send
str s
s.getwintext(id(10 hDlg))
TcpSocket x.ClientConnect(s GetDlgItemInt(hDlg 5 0 0))
s.getwintext(id(12 hDlg))
x.Send(s)
if(x.Receive(s 1000 1000)=2 and !s.len) s=""
out "<>CLIENT: server response:"
out s
ret 1
err+ out _error.description
} } {̇ss TCP server/client functions.
Can be used to create TCP server (listen to incoming connections) or client (connect to a server).
Server calls ServerStart. Client calls ClientConnect. Other functions are common.
EXAMPLES: in "test" folder
q U@x5
\Dialog_Editor
To test:
Run dialog_TcpSocket_server and click Start.
Then run dialog_TcpSocket_client and click Send.
#compile "__TcpSocket"
str dd=
BEGIN DIALOG
0 "" 0x90C80AC8 0x0 0 0 230 130 "TCP client"
4 Static 0x54000000 0x0 170 8 22 12 "Port"
5 Edit 0x54032000 0x200 196 6 32 14 "port"
9 Static 0x54000000 0x0 4 6 26 13 "Se
p Ṡclass TcpSocket -m_socket -!m_isServer
type __sockaddr_in @sin_family @sin_port sin_addr !sin_zero[8]
use different function names to avoid conflicts
dll ws2_32
[shutdown]#sock_shutdown s how
[send]#sock_send s $buf len flags
[recv]#sock_recv s $buf len flags
[connect]#sock_connect s !*name namelen
[bind]#sock_bind s !*name namelen
[listen]#sock_listen s backlog
[accept]#sock_accept s !*addr *addrlen
[inet_ntoa]$sock_inet_ntoa in
[select]#sock_select nfds fd_set*readfds fd_set*writefds fd_set*exceptfds timeval*timeout
#ifndef ERR_FAILED
def ERR_FAILED "573 failed"
#endif
y y
{ρrfunction $server @port
Creates client socket and connects to server.
Error if failed.
server - server address (eg "www.xxx.com"), or computer name, or IP ("xxx.xxx.xxx.xxx").
To connect to a server on this computer, can be used "localhost" or "127.0.0.1" or "".
port - port number.
CreateSocket
hostent* hp
int ip
__sockaddr_in sa
ip=inet_addr(server)
if(ip==INADDR_NONE or ip=0) hp=gethostbyname(server)
else hp=gethostbyaddr(+&ip 4 AF_INET)
if(!hp) E(1)
int* p=hp.h_addr_list[0]
sa.sin_addr=*p
sa.sin_family=AF_INET
sa.sin_port=htons(port)
if(sock_connect(m_socket &sa sizeof(sa))) E(1)
uns in separate thread for each connection, therefore multiple clients can connect simultaneously.
It is possible that the thread runs after ending function/thread that called ServerStart. Therefore use param carefully.
param - some value to pass to the clientFunc function.
flags:
1 - start listening and don't wait. If not used, this function does not return until you end this thread or call Close.
REMARKS
When using in dialog procedure, use TcpSocket variable with thread scope, and call this function with flag 1.
CreateSocket
__sockaddr_in sa
sa.sin_family=AF_INET
sa.sin_port=htons(port)
if(sock_bind(m_socket &sa sizeof(sa))) E(1)
if(sock_listen(m_socket SOMAXCONN)) E(1)
m_isServer=1
_i=mac("TcpSocket_ServerThread" "" m_socket clientFunc param)
if(flags&1) ret
opt waitmsg -1
wait 0 H _i
notes:
Always uses other thread because otherwise could not correctly end this thread. Now, when ending this thread, dtor closes socket, then accept() fails and it breaks loop.
f f , K͞
Closes socket.
Also disconnects, if connected.
If it is server, stops it. However does not end threads of client connections.
Called implicitly when destroying the variable.
if m_socket
sock_shutdown(m_socket SD_BOTH)
closesocket(m_socket)
m_socket=0
m_isServer=0
` 3λ99function @port clientFunc [param] [flags] ;;flags: 1 don't wait
Creates server socket and listens (or starts listening).
Error if failed.
port - port number. Recommended values are between 5000 and 65000.
clientFunc - address of a user-defined function that will be called when a client connects.
The function must begin with:
function TcpSocket&client $clientIp param !*reserved
client - client socket. You can call Send and Receive with it.
clientIp - client IP string.
param - param of ServerStart.
reserved - currently not used.
The function r
a xk
a λ99function [flags] ;;flags: 1 call Close
_s.dllerror("Socket error: " "" WSAGetLastError)
if(flags&1) Close
end _s 2
77Close
66\ +̇>> Returns socket handle that can be used with API functions.
ret m_socket
ẇuufunction# str&s nBytesMax [timeout] [flags] ;;flags: 1 append
Receives data.
Error if failed.
Returns: 0 all data received (sender closed connection), 1 possibly more data exists, 2 timeout. In most cases returns 1.
s - variable for data
}̇ggfunction $s [lens]
Sends data.
Error if failed.
s - string or binary data to send.
lens - number of bytes to send. If omitted or <0, uses s string length.
REMARKS
This function does not wait until server receives data.
if(getopt(nargs)<2 or lens<0) lens=len(s)
rep
_i=sock_send(m_socket s lens 0)
if(_i<0) E
if(_i>=lens) break
lens-_i; s+_i
.
nBytesMax - max number of bytes to receive. Default: 0.
This function may receive nBytesMax or less data. Can receive less in two cases:
1. Not all data is received. To get more data, call this function again, until you somehow know that all data is received.
2. All data is received, but nBytesMax is specified greater. It's OK.
If 0, receives all, until sender closes connection or shuts down sending. However, if server does not do it, this function will hang; in such case use nBytesMax.
timeout - number of seconds to wait for data. If 0, waits forever.
if(flags&1=0) s.fix(0)
int nr(8100) nr2 nrAll
if(nBytesMax and nBytesMaxnBytesMax) nr=nBytesMax-nrAll; if(!nr) break
if(!Wait(1 timeout)) ret 2
nr2=sock_recv(m_socket sb nr 0)
out nr2
if(nr2<0) E
if(!nr2) ret 0
s.fromn(s s.len sb nr2); nrAll+nr2
if(nBytesMax and nr2!nr) break
if(!timeout) timeout=10000
ret 1
\ u \ * İ@@function sock
m_socket=sock
̇\ +̇uu \
function clientSock clientIp clientFunc param
TcpSocket x.set_handle(clientSock)
call clientFunc &x sock_inet_ntoa(clientIp) param 0
sock_shutdown x.handle SD_BOTH
x.set_handle(0) ;;don't close
" 7̇XX \
function servSock clientFunc param
__sockaddr_in sa
rep
_i=sizeof(sa)
int sock=sock_accept(servSock &sa &_i)
if(sock=INVALID_SOCKET) break ;;probably closed servSock
mac "TcpSocket_ClientThread" "" sock sa.sin_addr clientFunc param; err out _error.description
c 9SS
#ifdef __WsInit ;;QM 2.3.5
__WsInit
#else
type __TcpSocket_Init -!m_inited
__TcpSocket_Init+ __ts_init
if !__ts_init
lock
if !__ts_init
WSADATA wsaData
if(WSAStartup(0x202 &wsaData)) end ERR_FAILED 2
__ts_init=1
lock-
#endif
Close
_i=socket(AF_INET SOCK_STREAM IPPROTO_TCP); if(_i=INVALID_SOCKET) E
m_socket=_i
0 UO"Q i 07
9 , TcpSocket.CreateSocket<Gf.z,
# , TcpSocket.E½5V
Lr>+
! , TcpSocket.RlIVAuv&
, private*2N8ٲx~L0
- , TcpSocket.handleX~4H^BqV1
/ , TcpSocket.Receive{8{\AQOq.
) ,
TcpSocket.Sendss"4CU/
+ , TcpSocket.Closej<A#8p5
7 , TcpSocket.ServerStartϝ[I7qtdC7
; ,
TcpSocket.ClientConnectZtLxn!+
# , __TcpSocket =NqO2[1.
) , TcpSocket helpANxZ/u8
; , dialog_TcpSocket_client)`AHAd)?p(8
; , dialog_TcpSocket_serverw)J`X;;(
# , with dialogV9g(L7p 6
7 , test_TcpSocket_clientyJmgK}6
7 , test_TcpSocket_server$TGLHt+
) , without dialog\k7ҢD2R!
, test1^F>FN%q<1!
, file
; 2
/ , __TcpSocket_Init.C)=taHHdQ/
) , TcpSocket.Wait
EH0Vs5
5 , TcpSocket.set_handleVj[IXr:Q
;E , TcpSocket_ProtectThread@15 "TcpSocket_ServerThread"xn2El[vTO7
9 , TcpSocket_ClientThread##K~DDF7
9 , TcpSocket_ServerThreadP:. aD'SN
% ?Y
Yif(m_inited) WSACleanup
̇PPfunction! !read timeout
fd_set f; fd_set* r w; timeval* tp
f.fd_count=1; f.fd_array[0]=m_socket; if(read) r=&f; else w=&f
if(timeout) timeval t.tv_sec=timeout/1000; t.tv_usec=timeout%1000*1000; tp=&t
_i=sock_select(0 r w 0 tp)
if(_i<0) E
ret _i>0