You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift基于TCP实现聊天APP的疑问及代码报错咨询

TCP双向字符串传输疑问解答及代码修复

疑问解答

  • 两台不同Mac设备的模拟器是否可实现?
    可以。前提是两台Mac处于同一局域网,模拟器能够访问对方Mac的IP地址,同时两台Mac的防火墙都允许8080端口的TCP连接请求。
  • 同一Mac设备的两个模拟器是否可实现?
    完全可以。可以使用Mac的本地回环地址127.0.0.1或者局域网IP,将两个模拟器分别配置为TCP客户端和服务端即可。
  • 替换Mac的IP并使用8080端口是否可行?
    可行,但需要确认两点:一是8080端口未被其他程序占用;二是Mac的防火墙允许该端口的TCP入站/出站流量。

报错原因分析

你遇到的nw_socket_handle_socket_event [C1:1] Socket SO_ERROR [61: Connection refused]错误,本质是TCP连接请求被拒绝,核心原因是:你的代码只实现了TCP客户端,但没有对应的TCP服务端在目标IP的8080端口上监听连接请求。

代码修复与补充

1. 补充TCP服务端实现

首先需要实现一个TCP服务端来监听8080端口,接收客户端连接并处理双向通信:

class NWTCPServer: NSObject {
    var listener: NWListener!
    
    func startServer() {
        let port = NWEndpoint.Port(rawValue: 8080)!
        let queue = DispatchQueue(label: "TCP Server Queue")
        
        let tcpOptions = NWProtocolTCP.Options()
        tcpOptions.noDelay = true
        let params = NWParameters(tls: nil, tcp: tcpOptions)
        
        do {
            listener = try NWListener(using: params, on: port)
        } catch {
            print("Failed to create listener: \(error)")
            return
        }
        
        listener.stateUpdateHandler = { newState in
            switch newState {
            case .ready:
                print("Server is ready to accept connections")
            case .failed(let error):
                print("Server failed: \(error)")
            default:
                break
            }
        }
        
        listener.newConnectionHandler = { connection in
            print("New client connected")
            self.handleConnection(connection: connection)
        }
        
        listener.start(queue: queue)
    }
    
    private func handleConnection(connection: NWConnection) {
        let queue = DispatchQueue(label: "Connection Queue")
        connection.start(queue: queue)
        
        connection.stateUpdateHandler = { newState in
            switch newState {
            case .ready:
                print("Client connection ready")
                self.receiveFromConnection(connection: connection)
                // 示例:给客户端发送响应
                self.sendToConnection(connection: connection, message: "Hello from server!")
            case .failed(let error):
                print("Client connection failed: \(error)")
                connection.cancel()
            case .cancelled:
                print("Client connection cancelled")
            default:
                break
            }
        }
    }
    
    private func sendToConnection(connection: NWConnection, message: String) {
        guard let data = message.data(using: .utf8) else { return }
        connection.send(content: data, completion: .contentProcessed { error in
            if let error = error {
                print("Server send error: \(error)")
            } else {
                print("Server sent message: \(message)")
            }
        })
    }
    
    private func receiveFromConnection(connection: NWConnection) {
        connection.receiveMessage { data, context, isComplete, error in
            if let error = error {
                print("Server receive error: \(error)")
                return
            }
            
            if let data = data, isComplete {
                if let message = String(data: data, encoding: .utf8) {
                    print("Server received message: \(message)")
                }
                // 循环接收下一条消息
                self.receiveFromConnection(connection: connection)
            }
        }
    }
}

2. 修复客户端代码的问题

你的客户端代码存在两个主要问题:一是receive方法只接收一次消息,二是未处理连接失败的具体状态。以下是修复后的客户端代码:

class NWTCPConnection: NSObject {
    var connection: NWConnection!

    func connectToTcp() {
        let PORT: NWEndpoint.Port = 8080
        // 同一Mac模拟器用127.0.0.1,不同Mac用对方的局域网IP
        let ipAddress: NWEndpoint.Host = "127.0.0.1" 
        let queue = DispatchQueue(label: "TCP Client Queue")
        
        let tcp = NWProtocolTCP.Options()
        tcp.noDelay = true
        let params = NWParameters(tls: nil, tcp: tcp)
        connection = NWConnection(to: NWEndpoint.hostPort(host: ipAddress, port: PORT), using: params)
        
        connection.stateUpdateHandler = { [weak self] newState in
            guard let self = self else { return }
            switch newState {
            case .ready:
                print("Socket State: Ready")
                UserDefaults.standard.set(true, forKey: "isConnected")
                self.sendMSG()
                self.receive()
            case .failed(let error):
                print("Socket connection failed: \(error)")
                UserDefaults.standard.set(false, forKey: "isConnected")
                self.connection.cancel()
            case .cancelled:
                print("Socket connection cancelled")
                UserDefaults.standard.set(false, forKey: "isConnected")
            default:
                UserDefaults.standard.set(false, forKey: "isConnected")
                break
            }
        }
        connection.start(queue: queue)
    }

    func sendMSG() {
        print("send data")
        let message1 = "hello world"
        guard let content = message1.data(using: .utf8) else { return }
        connection.send(content: content, completion: NWConnection.SendCompletion.contentProcessed({ error in
            if let error = error {
                print("ERROR! Error when sending data: \(error)")
            } else {
                print("Data was sent to TCP destination ")
            }
        }))
    }

    // 循环接收消息,确保能持续接收服务端发送的内容
    func receive() {
        connection.receiveMessage { [weak self] data, context, isComplete, error in
            guard let self = self else { return }
            if let error = error {
                print("Client receive error: \(error)")
                return
            }
            
            if isComplete, let data = data {
                print("Receive is complete, count bytes: \(data.count)")
                if let message = String(data: data, encoding: .utf8) {
                    print("Client received message: \(message)")
                } else {
                    print("Data is not valid UTF-8 string")
                }
                // 继续接收下一条消息
                self.receive()
            }
        }
    }
}

3. 使用方式

  • 在其中一个模拟器中启动服务端:
    let server = NWTCPServer()
    server.startServer()
    
  • 在另一个模拟器(或另一台Mac的模拟器)中启动客户端:
    let client = NWTCPConnection()
    client.connectToTcp()
    

额外注意事项

  • 确认端口占用:可以在Mac终端用lsof -i :8080命令检查8080端口是否被占用,若被占用需更换端口或关闭占用程序。
  • 防火墙设置:进入Mac系统偏好设置→安全与隐私→防火墙,确保允许你的应用程序接收传入连接。

内容的提问来源于stack exchange,提问作者danu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 01:55:10