In a previous post we showed how to create an echo server using IBM’s BlueSocket framework. This server was single threaded therefore it could
only communicate with one client at a time.
In this post we will show how we can add libdispatch (GCD) to our code
to create a multi-client server.
To read an explanation on how the server itself was
written, please refer to the previous post Using the BlueSocket framework tocreate an echo server. The complete code for this project
can be found in the Mastering Swift github repository.
The first thing we need to do is add the dispatch
framework to the import list. We should
now have the following imports:
#if os(Linux)
import Glibc
#else
import Darwin
#endif
import Foundation
import Socket
import Dispatch
Now we will need to change the start()
method a little bit so when it calls the newConnection(socket:)
method it uses a separate thread. The
new start() method looks like this:
func start() throws {
let socket = try Socket.create()
listenSocket = socket
try socket.listen(on: port)
print("Listening port:
\(socket.listeningPort)")
let queue =
DispatchQueue(label: "clientQueue.hoffman.jon", attributes:
.concurrent)
repeat {
let connectedSocket = try
socket.acceptClientConnection()
print("Connection from:
\(connectedSocket.remoteHostname)")
queue.async{self.newConnection(socket:
connectedSocket)}
} while acceptNewConnection
}
There are two parts of this code that changed. The first is the let queue =
DispatchQueue(label: "clientQueue.hoffman.jon", attributes:
.concurrent) line which was added to our code. This line creates a new concurrent dispatch
queue. The second change is we used the queue.async() method to submit the newConnection(socket:) call to the dispatch
queue. You can use the EchoServer class exactly they same way as we did in the previous post.
Those are the only changes you need to make the code a
multi-client server. Here is the full
code for the multi-client EchoServer class :
class EchoServer {
let
bufferSize = 1024
let
port: Int
var
listenSocket: Socket? = nil
var
connected = [Int32: Socket]()
var
acceptNewConnection = true
init(port: Int) {
self.port = port
}
deinit {
for socket in connected.values {
socket.close()
}
listenSocket?.close()
}
func
start() throws {
let socket = try Socket.create()
listenSocket = socket
try socket.listen(on: port)
print("Listening port: \(socket.listeningPort)")
let queue = DispatchQueue(label: "clientQueue.hoffman.jon",
attributes: .concurrent)
repeat {
let connectedSocket = try socket.acceptClientConnection()
print("Connection from:
\(connectedSocket.remoteHostname)")
queue.async{self.newConnection(socket: connectedSocket)}
}
while acceptNewConnection
}
func
newConnection(socket: Socket) {
connected[socket.socketfd]
= socket
var cont = true
var dataRead = Data(capacity: bufferSize)
repeat {
do {
let bytes = try socket.read(into: &dataRead)
if bytes > 0 {
if let readStr =
String(data: dataRead, encoding: .utf8) {
print("Received:
\(readStr)")
try socket.write(from:
readStr)
if
readStr.hasPrefix("quit") {
cont = false
socket.close()
}
dataRead.count = 0
}
}
} catch let error {
print("error: \(error)")
}
}
while cont
connected.removeValue(forKey: socket.socketfd)
socket.close()
}
}
You can download the full code listing from the MasteringSwift github repository.
I use echo server in an iOS app, it's worked correctly but when I run it The GUI is frizzed until stop it, How can I leave it work in background without effecting in GUI
ReplyDelete