-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgserver.rb
175 lines (150 loc) · 4 KB
/
gserver.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# Copyright (C) 2001 John W. Small All Rights Reserved
# mailto:[email protected] subject:ruby-generic-server
# Freeware
require "socket"
require "thread"
class GServer
DEFAULT_HOST = "127.0.0.1"
def serve(io)
end
@@services = {} # Hash of opened ports, i.e. services
@@servicesMutex = Mutex.new
def GServer.stop(port, host = DEFAULT_HOST)
@@servicesMutex.synchronize {
@@services[host][port].stop
}
end
def GServer.in_service?(port, host = DEFAULT_HOST)
@@services.has_key?(host) and
@@services[host].has_key?(port)
end
def stop
@connectionsMutex.synchronize {
if @tcpServerThread
@tcpServerThread.raise "stop"
end
}
end
def stopped?
@tcpServerThread == nil
end
def shutdown
@shutdown = true
end
def connections
@connections.size
end
def join
@tcpServerThread.join if @tcpServerThread
end
attr_reader :port, :host, :maxConnections
attr_accessor :stdlog, :audit, :debug
def connecting(client)
addr = client.peeraddr
log("#{self.class.to_s} #{@host}:#{@port} client:#{addr[1]} " +
"#{addr[2]}<#{addr[3]}> connect")
true
end
def disconnecting(clientPort)
log("#{self.class.to_s} #{@host}:#{@port} " +
"client:#{clientPort} disconnect")
end
protected :connecting, :disconnecting
def starting()
log("#{self.class.to_s} #{@host}:#{@port} start")
end
def stopping()
log("#{self.class.to_s} #{@host}:#{@port} stop")
end
protected :starting, :stopping
def error(detail)
log(detail.backtrace.join("\n"))
end
def log(msg)
if @stdlog
@stdlog.puts("[#{Time.new.ctime}] %s" % msg)
@stdlog.flush
end
end
protected :error, :log
def initialize(port, host = DEFAULT_HOST, maxConnections = 4,
stdlog = $stderr, audit = false, debug = false)
@tcpServerThread = nil
@port = port
@host = host
@maxConnections = maxConnections
@connections = []
@connectionsMutex = Mutex.new
@connectionsCV = ConditionVariable.new
@stdlog = stdlog
@audit = audit
@debug = debug
end
def start(maxConnections = -1)
raise "running" if !stopped?
@shutdown = false
@maxConnections = maxConnections if maxConnections > 0
@@servicesMutex.synchronize {
if GServer.in_service?(@port,@host)
raise "Port already in use: #{host}:#{@port}!"
end
@tcpServer = TCPServer.new(@host,@port)
@port = @tcpServer.addr[1]
@@services[@host] = {} unless @@services.has_key?(@host)
@@services[@host][@port] = self;
}
@tcpServerThread = Thread.new {
begin
starting if @audit
while !@shutdown
@connectionsMutex.synchronize {
while @connections.size >= @maxConnections
@connectionsCV.wait(@connectionsMutex)
end
}
client = @tcpServer.accept
@connections << Thread.new(client) { |myClient|
begin
myPort = myClient.peeraddr[1]
serve(myClient) if !@audit or connecting(myClient)
rescue => detail
error(detail) if @debug
ensure
begin
myClient.close
rescue
end
@connectionsMutex.synchronize {
@connections.delete(Thread.current)
@connectionsCV.signal
}
disconnecting(myPort) if @audit
end
}
end
rescue => detail
error(detail) if @debug
ensure
begin
@tcpServer.close
rescue
end
if @shutdown
@connectionsMutex.synchronize {
while @connections.size > 0
@connectionsCV.wait(@connectionsMutex)
end
}
else
@connections.each { |c| c.raise "stop" }
end
@tcpServerThread = nil
@@servicesMutex.synchronize {
@@services[@host].delete(@port)
}
stopping if @audit
end
}
self
end
end