首页 > 解决方案 > swift上的简单TCP监听器

问题描述

我向远程计算机上运行的应用程序发送消息。我以这种方式发送消息。

C#:

    void web_data_work()
    {
        try
        {
            TcpClient client = new TcpClient(address, port);
            Byte[] data = Encoding.UTF8.GetBytes(string.Format("{0}", message));
            NetworkStream stream = client.GetStream();
            try
            {
                if (message != "")
                {
                    stream.Write(data, 0, data.Length);
                    Byte[] readingData = new Byte[256];
                    String responseData = String.Empty;
                    StringBuilder completeMessage = new StringBuilder();
                    int numberOfBytesRead = 0;
                    do
                    {
                        numberOfBytesRead = stream.Read(readingData, 0, readingData.Length);
                        completeMessage.AppendFormat("{0}", Encoding.UTF8.GetString(readingData, 0, numberOfBytesRead));
                    }
                    while (stream.DataAvailable);
                    responseData = completeMessage.ToString();
                    this.Invoke((MethodInvoker)delegate ()
                    {
                        output_list.Items.Add(string.Format("Sended – {0}", responseData));
                        message = "";
                    });
                }
            }
            finally
            {
                stream.Close();
                client.Close();
            }
        }
        catch
        {
            this.Invoke((MethodInvoker)delegate ()
            {
                output_list.Items.Add("Not sended");
                message = "";
            });
        }
    }

迅速:

func web_data_work()
{
    let address = address_box.stringValue
    let port = port_box.intValue
    
    let task = URLSession.shared.streamTask(withHostName: address, port: Int(port))
    let data = message.data(using: .utf8)!

    task.write(data as Data, timeout: 0)
    {
        error in
        //om = "Not sended"
        //self.output_message()
    }

    task.resume()
}

在 c# 中,我可以使用TcpListener读取消息,我该如何快速做到这一点?只使用了两个参数:“地址”——消息发送到的计算机的 IP 地址,该计算机的 TcpListener 正在侦听该地址。“端口”——计算机之间发送和接收的端口。

PS最好没有额外的库。

标签: swiftcocoatcpclienttcplistener

解决方案


您只需使用两个类即可实现此目的:SocketPort& FileHandle

在复制和粘贴下面的示例之前,请阅读我最后的评论。

import Cocoa

class TcpEchoClient {
    var readToEndOfFileCompletionHandler: (() -> Void)?
    let fileHandle: FileHandle
    var observer: NSObjectProtocol?
    
    init(fileHandle: FileHandle) {
        self.fileHandle = fileHandle
        
        // Register observer for the data & EOF
        self.observer = NotificationCenter.default.addObserver(forName: .NSFileHandleReadToEndOfFileCompletion,
                                                               object: fileHandle,
                                                               queue: OperationQueue.main) { [weak self] note in
            self?.handleReadToEndOfFileCompletion(notification: note)
        }
        
        // Instruct the handle to read till the EOF & notify
        fileHandle.readToEndOfFileInBackgroundAndNotify()
    }
    
    func handleReadToEndOfFileCompletion(notification note: Notification) {
        defer {
            // No matter what happens, call the completion handle by the end
            readToEndOfFileCompletionHandler?()
        }
        
        // Is there an error?
        if let errorCode = note.userInfo?["NSFileHandleError"] as? NSNumber {
            print("Client \(fileHandle.fileDescriptor) error: File handle error \(errorCode.intValue)")
            return
        }
        
        // No error, we should have data available
        guard let data = note.userInfo?[NSFileHandleNotificationDataItem] as? Data else {
            print("Client \(fileHandle.fileDescriptor) error: Unable to get data")
            return
        }
        
        // Convert them to UTF-8 string
        guard let text = String(data: data, encoding: .utf8) else {
            print("Client \(fileHandle.fileDescriptor) error: Unable to convert data to UTF-8 string")
            return
        }
        
        // Print the text
        print("Client \(fileHandle.fileDescriptor) received: \(text)")
    }
    
    deinit {
        // Remove observer for the data & EOF
        if let observer = observer {
            NotificationCenter.default.removeObserver(observer)
        }
        // Close the handle
        try? fileHandle.close()
    }
}

class TcpServer {
    let port: SocketPort
    let fileHandle: FileHandle
    var clients: Dictionary<Int32, TcpEchoClient>
    var observer: NSObjectProtocol?
    
    init?(tcpPort: UInt16) {
        guard let socketPort = SocketPort(tcpPort: tcpPort) else {
            return nil
        }

        // Keep the socket port around otherwise you'll get error 38
        port = socketPort
        // No clients for now
        clients = [:]
        // Create handle from the socket
        fileHandle = FileHandle(fileDescriptor: port.socket)
        // Register observer for the connection accepted
        observer = NotificationCenter.default.addObserver(forName: .NSFileHandleConnectionAccepted,
                                                          object: fileHandle,
                                                          queue: OperationQueue.main) { [weak self] note in
            if let handle = note.object as? FileHandle {
                // Ask immediately for another accepted connection notification
                handle.acceptConnectionInBackgroundAndNotify()
            }
            
            self?.handleConnectionAccepted(notification: note)
        }
        
        // Instruct the handle to accept connection & notify
        fileHandle.acceptConnectionInBackgroundAndNotify()
    }
    
    func handleConnectionAccepted(notification note: Notification) {
        // Is there an error?
        if let errorCode = note.userInfo?["NSFileHandleError"] as? NSNumber {
            print("Server error: File handle error \(errorCode.intValue)")
            return
        }
        
        // No, we should have received the client file handle
        guard let clientFileHandle = note.userInfo?[NSFileHandleNotificationFileHandleItem] as? FileHandle else {
            print("Server error: Unable to get accepted connection file handle")
            return
        }

        let fileDescriptor = clientFileHandle.fileDescriptor
        
        // Create new client from the file handle
        let client = TcpEchoClient(fileHandle: clientFileHandle)
        
        // Once it finishes, remove it from the clients dictionary
        client.readToEndOfFileCompletionHandler = { [weak self] in
            guard let self = self else { return }
            
            self.clients.removeValue(forKey: fileDescriptor)
            print("Server: Client removed \(fileDescriptor) (total \(self.clients.count))")
        }
        
        // Store the client in the clients dictionary
        clients[fileDescriptor] = client
        
        print("Server: New client \(fileDescriptor) added (total \(self.clients.count))")
    }
    
    deinit {
        // Remove all clients
        clients.removeAll()
        // Close the file handle
        try? fileHandle.close()
        // Invalidate the socket port
        port.invalidate()
        // Remove connection accepted observer
        if let observer = observer {
            NotificationCenter.default.removeObserver(observer)
        }
    }
}

然后创建一个属性 ( var server: TcpServer?) 并初始化它 ( server = TcpServer(tcpPort: 8080))。

测试:

parallel -j 1 echo -n Foo '|' nc 127.0.0.1 8080 ::: {1..4}

控制台输出:

Server: New client 7 added (total 1)
Client 7 received: Foo
Server: Client removed 7 (total 0)
Server: New client 7 added (total 1)
Client 7 received: Foo
Server: Client removed 7 (total 0)
Server: New client 7 added (total 1)
Client 7 received: Foo
Server: Client removed 7 (total 0)
Server: New client 7 added (total 1)
Client 7 received: Foo
Server: Client removed 7 (total 0)

注释:

  • 这个例子只是一个你可以开始的基本骨架。
  • 不要被paralleltest 命令误导。
    • 如果我想一次运行多个命令(相同的命令),我正在使用它。
    • 此示例中的所有内容都在主队列上运行。
  • 阅读我使用的所有这些类、方法和通知的文档。有问题,你必须熟悉运行循环等。

推荐阅读