Skip to content

Where should I run the EchoServer Sample code in my program? #183

Description

@pekochun

I get an error when I try to launch the app by writing EchoServer.run inside ViewDidLoad as below. Where should I write EchoServer.run?

import Foundation
import Socket
import Dispatch
import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let port = 1337
        let server = EchoServer(port: port)
        server.run()

        self.view.backgroundColor = .yellowColor()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}

class EchoServer {
	
	static let quitCommand: String = "QUIT"
	static let shutdownCommand: String = "SHUTDOWN"
	static let bufferSize = 4096
	
	let port: Int
	var listenSocket: Socket? = nil
	var continueRunningValue = true
	var connectedSockets = [Int32: Socket]()
	let socketLockQueue = DispatchQueue(label: "com.ibm.serverSwift.socketLockQueue")
	var continueRunning: Bool {
		set(newValue) {
			socketLockQueue.sync {
				self.continueRunningValue = newValue
			}
		}
		get {
			return socketLockQueue.sync {
				self.continueRunningValue
			}
		}
	}

	init(port: Int) {
		self.port = port
	}
	
	deinit {
		// Close all open sockets...
		for socket in connectedSockets.values {
			socket.close()
		}
		self.listenSocket?.close()
	}
	
	func run() {
		
		let queue = DispatchQueue.global(qos: .userInteractive)
		
		queue.async { [unowned self] in
			
			do {
				// Create an IPV6 socket...
				try self.listenSocket = Socket.create(family: .inet6)
				
				guard let socket = self.listenSocket else {
					
					print("Unable to unwrap socket...")
					return
				}
				
				try socket.listen(on: self.port)
				
				print("Listening on port: \(socket.listeningPort)")
				
				repeat {
					let newSocket = try socket.acceptClientConnection()
					
					print("Accepted connection from: \(newSocket.remoteHostname) on port \(newSocket.remotePort)")
					print("Socket Signature: \(String(describing: newSocket.signature?.description))")
					
					self.addNewConnection(socket: newSocket)
					
				} while self.continueRunning
				
			}
			catch let error {
				guard let socketError = error as? Socket.Error else {
					print("Unexpected error...")
					return
				}
				
				if self.continueRunning {
					
					print("Error reported:\n \(socketError.description)")
					
				}
			}
		}
		dispatchMain()
	}
	
	func addNewConnection(socket: Socket) {
		
		// Add the new socket to the list of connected sockets...
		socketLockQueue.sync { [unowned self, socket] in
			self.connectedSockets[socket.socketfd] = socket
		}
		
		// Get the global concurrent queue...
		let queue = DispatchQueue.global(qos: .default)
		
		// Create the run loop work item and dispatch to the default priority global queue...
		queue.async { [unowned self, socket] in
			
			var shouldKeepRunning = true
			
			var readData = Data(capacity: EchoServer.bufferSize)
			
			do {
				// Write the welcome string...
				try socket.write(from: "Hello, type 'QUIT' to end session\nor 'SHUTDOWN' to stop server.\n")
				
				repeat {
					let bytesRead = try socket.read(into: &readData)
					
					if bytesRead > 0 {
						guard let response = String(data: readData, encoding: .utf8) else {
							
							print("Error decoding response...")
							readData.count = 0
							break
						}
						if response.hasPrefix(EchoServer.shutdownCommand) {
							
							print("Shutdown requested by connection at \(socket.remoteHostname):\(socket.remotePort)")
							
							// Shut things down...
							self.shutdownServer()
							
							return
						}
						print("Server received from connection at \(socket.remoteHostname):\(socket.remotePort): \(response) ")
						let reply = "Server response: \n\(response)\n"
						try socket.write(from: reply)
						
						if (response.uppercased().hasPrefix(EchoServer.quitCommand) || response.uppercased().hasPrefix(EchoServer.shutdownCommand)) &&
							(!response.hasPrefix(EchoServer.quitCommand) && !response.hasPrefix(EchoServer.shutdownCommand)) {
							
							try socket.write(from: "If you want to QUIT or SHUTDOWN, please type the name in all caps. 😃\n")
						}
						
						if response.hasPrefix(EchoServer.quitCommand) || response.hasSuffix(EchoServer.quitCommand) {
							
							shouldKeepRunning = false
						}
					}
					
					if bytesRead == 0 {
						
						shouldKeepRunning = false
						break
					}
					
					readData.count = 0
					
				} while shouldKeepRunning
				
				print("Socket: \(socket.remoteHostname):\(socket.remotePort) closed...")
				socket.close()
				
				self.socketLockQueue.sync { [unowned self, socket] in
					self.connectedSockets[socket.socketfd] = nil
				}
				
			}
			catch let error {
				guard let socketError = error as? Socket.Error else {
					print("Unexpected error by connection at \(socket.remoteHostname):\(socket.remotePort)...")
					return
				}
				if self.continueRunning {
					print("Error reported by connection at \(socket.remoteHostname):\(socket.remotePort):\n \(socketError.description)")
				}
			}
		}
	}
	
	func shutdownServer() {
		print("\nShutdown in progress...")

		self.continueRunning = false
		
		// Close all open sockets...
		for socket in connectedSockets.values {
			
			self.socketLockQueue.sync { [unowned self, socket] in
				self.connectedSockets[socket.socketfd] = nil
				socket.close()
			}
		}
		
		DispatchQueue.main.sync {
			exit(0)
		}
	}
}

ErrorMessage is below

Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'threading violation: expected the main thread'

If I rewrite ViewDidLoad as below, I get other errors.

    override func viewDidLoad() {
        super.viewDidLoad()

        DispatchQueue.main.async
        {
            let port = 1337
            let server = EchoServer(port: port)
            server.run()
        }

        self.view.backgroundColor = .yellowColor()
    }

The details of the error are as follows.

Thread 1: EXC_BAD_ACCESS (code=1, address=0x5c)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions