1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
15 # Server-side network services for games and such
17 # The following code creates a server that continuously listen for new clients,
18 # and exchange with them briefly before disconnecting.
21 # redef fun handshake_app_name do return "nitwork_test"
22 # redef fun handshake_app_version do return "1.0"
24 # Open a server on port 4444
25 # var server = new Server(4444)
28 # # Accept new clients
29 # var new_clients = server.accept_clients
30 # for client in new_clients do
31 # # A client is connected, communicate!
33 # print client.reader.deserialize.as(Object)
34 # client.writer.serialize "Goodbye client"
36 # # Done, close socket
40 # # `accept_clients` in non-blocking,
41 # # sleep before tying again, or do something else.
42 # nanosleep(0, 50000000)
50 # Game server controller
53 # Port for the `listening_socket`
56 # All connected `RemoteClient`
57 var clients
= new Array[RemoteClient]
59 # Socket accepting new connections
60 var listening_socket
: TCPServer is lazy
do
62 var socket
= new TCPServer(port
)
64 socket
.blocking
= false
67 init do listening_socket
69 # Accept currently waiting clients and return them as an array
70 fun accept_clients
: Array[RemoteClient]
72 assert not listening_socket
.closed
74 var new_clients
= new Array[RemoteClient]
76 var client_socket
= listening_socket
.accept
77 if client_socket
== null then break
79 var rc
= new RemoteClient(client_socket
)
81 var handshake_success
= rc
.handshake
82 if handshake_success
then
84 print
"Server: Client at {client_socket.address} passed the handshake"
86 print_error
"Server Error: Client at {client_socket.address} failed the handshake"
93 # Broadcast a `message` to all `clients`, then flush the connection
94 fun broadcast
(message
: Serializable)
96 for client
in clients
do
97 client
.writer
.serialize
(message
)
103 # Reference to a remote client connected to this server
106 # Communication socket with the client
107 var socket
: TCPStream
109 # Is this client connected?
110 fun connected
: Bool do return socket
.connected
112 # `BinarySerializer` used to send data to this client through `socket`
113 var writer
: BinarySerializer is noinit
115 # `BinaryDeserializer` used to receive data from this client through `socket`
116 var reader
: BinaryDeserializer is noinit
120 # Setup serialization
121 writer
= new BinarySerializer(socket
)
122 writer
.cache
= new AsyncCache(true)
123 reader
= new BinaryDeserializer(socket
)
127 # Check for compatibility with the client
130 print
"Server: Handshake requested by {socket.address}"
132 # Make sure it is the same app
133 var server_app
= sys
.handshake_app_name
134 var client_app
= socket
.read_string
135 if server_app
!= client_app
then
136 print_error
"Server Error: Client app name is '{client_app}'"
138 # Send an empty string so the client read it and give up
139 socket
.write_string
""
144 socket
.write_string server_app
147 var app_version
= sys
.handshake_app_version
148 var client_version
= socket
.read_string
149 if client_version
!= app_version
then
150 print_error
"Handshake Error: client version is different '{client_version}'"
151 socket
.write_string
""
156 socket
.write_string app_version