Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
pekochun opened this issue Mar 18, 2020 · 0 comments
Open

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

pekochun opened this issue Mar 18, 2020 · 0 comments

Comments

@pekochun
Copy link

pekochun commented Mar 18, 2020

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)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant