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

iOS端TCP连接消息发送成功但服务端未接收问题排查

iOS NWConnection TCP发送消息本地成功但服务端无接收的排查与修复

问题概述

通过NWConnection建立TCP连接,连接和消息接收功能正常,但发送消息时本地日志提示发送成功,服务端无接收记录。此前功能正常,迭代后失效且无法回退。

预期行为

在ViewController中输入IP地址和端口,点击「连接」按钮,通过UIAlertController接收JSON数据,点击「确定」按钮后发送字符串。

实际行为

连接与消息接收功能正常,点击「确定」按钮后本地提示消息发送成功,但服务端仅显示连接记录,未收到消息。

相关代码

class ViewController: UIViewController {

    @IBOutlet weak var ipAddress: UITextField!
    @IBOutlet weak var portNumber: UITextField!
    @IBOutlet weak var statusLabel: UILabel!
    
    var connection: NWConnection?
    var message: String?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let backgroundImageView = UIImageView()
        backgroundImageView.image = UIImage(named: "bg")
        backgroundImageView.contentMode = .scaleAspectFill
        backgroundImageView.frame = view.bounds
        view.insertSubview(backgroundImageView, at: 0)
        
        let hideKeyboard = UITapGestureRecognizer(target: self, action: #selector(hideKeyboard))
        view.addGestureRecognizer(hideKeyboard)
    }

    @IBAction func connectTapped(_ sender: Any) {
        guard let ipAddress = ipAddress.text, !ipAddress.isEmpty else {
            makeAlert(titleInput: "Error!", messageInput: "Please enter IP Address")
            return
        }
        
        guard let portText = portNumber.text, let port = UInt16(portText), !portText.isEmpty else {
            makeAlert(titleInput: "Error!", messageInput: "Please enter port number")
            return
        }
        self.connectToServer(ipAddress: ipAddress, port: port)

    }
    
 //**Connection to Server**
    func connectToServer(ipAddress: String, port: UInt16) {
        let host = NWEndpoint.Host(ipAddress)
        let port = NWEndpoint.Port(rawValue: port)!
        connection = NWConnection(host: host, port: port, using: .tcp)

        connection?.stateUpdateHandler = { [weak self] newState in
            switch newState {
            case .ready:
                DispatchQueue.main.async {
                    self?.statusLabel.text = "Connected to server."
                    self?.receiveMessage()

                }
            case .failed(let error):
                DispatchQueue.main.async {
                    self?.statusLabel.text = "Failed to connect: \(error.localizedDescription)"
                }
                self?.connection?.cancel()
                self?.connection = nil
            default:
                break
            }
        }

        connection?.start(queue: .global(qos: .background))
    }

        func makeAlert(titleInput: String, messageInput: String, completion: (() -> Void)? = nil) {
        let alert = UIAlertController(title: titleInput, message: messageInput, preferredStyle: .alert)
        let okButton = UIAlertAction(title: "Okay", style: .default) { _ in
            completion?()
        }
        alert.addAction(okButton)
        self.present(alert, animated: true, completion: nil)
    }

//  **receive Message**
       func receiveMessage() {
        connection?.receive(minimumIncompleteLength: 1, maximumLength: 1024) { [weak self] data, context, isComplete, error in
            if let data = data, !data.isEmpty {
                self?.message = String(data: data, encoding: .utf8) ?? "Received data, but cannot decode."
                DispatchQueue.main.async {
                    self?.makeAlert(titleInput: "Message received", messageInput: "\(self?.message ?? "no message")") {
                        if let messageToSend = self?.message {
                            self?.sendMessage(messageToSend)
                        }
                    }
                }
            }
            if isComplete {
                self?.connection?.cancel()
                self?.connection = nil
            } else if let error = error {
                DispatchQueue.main.async {
                    self?.statusLabel.text = "Receive error: \(error.localizedDescription)"
                }
                self?.connection?.cancel()
                self?.connection = nil
            } else {
                self?.receiveMessage()
            }
        }
    }

// **send Message**
    func sendMessage(_ message: String) {
        guard let connection = connection else {
            print("Connection is not established")
            DispatchQueue.main.async {
                self.statusLabel.text = "Connection is not established"
            }
            return
        }
        
        guard let data = message.data(using: .utf8) else {
            print("Failed to encode message to data")
            DispatchQueue.main.async {
                self.statusLabel.text = "Failed to encode message to data"
            }
            return
        }
        
        print("Sending message: \(message)")
        
        connection.send(content: data, completion: .contentProcessed({ [weak self] error in
            if let error = error {
                print("Failed to send data: \(error.localizedDescription)")
                DispatchQueue.main.async {
                    self?.statusLabel.text = "Send error: \(error.localizedDescription)"
                }
                return
            }
            print("Data sent successfully")
            DispatchQueue.main.async {
                self?.makeAlert(titleInput: "Sent successfully", messageInput: "Sending message") {
                }
                self?.statusLabel.text = "Data sent successfully"
            }
        }))
    }

   @objc func hideKeyboard(gestureRecognizer: UITapGestureRecognizer) {
        view.endEditing(true)
    }
}

排查与修复方案

1. 补充TCP消息分隔符(核心修复点)

TCP是流式协议,服务端通常依赖特定分隔符(如换行符\n、固定长度前缀)识别完整消息。当前代码发送原始字符串,无分隔符时服务端可能无法判定消息结束,导致一直等待后续数据。

修改sendMessage方法,添加服务端预期的分隔符:

func sendMessage(_ message: String) {
    guard let connection = connection else {
        print("Connection is not established")
        DispatchQueue.main.async {
            self.statusLabel.text = "Connection is not established"
        }
        return
    }
    
    // 按服务端要求添加消息分隔符,示例为换行符
    let messageWithDelimiter = message + "\n"
    guard let data = messageWithDelimiter.data(using: .utf8) else {
        print("Failed to encode message to data")
        DispatchQueue.main.async {
            self.statusLabel.text = "Failed to encode message to data"
        }
        return
    }
    
    print("Sending message: \(messageWithDelimiter)")
    
    connection.send(content: data, completion: .contentProcessed({ [weak self] error in
        if let error = error {
            print("Failed to send data: \(error.localizedDescription)")
            DispatchQueue.main.async {
                self?.statusLabel.text = "Send error: \(error.localizedDescription)"
            }
            return
        }
        print("Data sent successfully")
        DispatchQueue.main.async {
            self?.makeAlert(titleInput: "Sent successfully", messageInput: "Sending message") {
            }
            self?.statusLabel.text = "Data sent successfully"
        }
    }))
}

2. 增加连接状态校验

确保发送消息时连接处于.ready状态,避免因连接状态异常导致的发送无效:

// 在sendMessage开头添加状态检查
guard connection.state == .ready else {
    print("Connection is not ready")
    DispatchQueue.main.async {
        self.statusLabel.text = "Connection is not ready"
    }
    return
}

3. 抓包验证数据包是否发出

使用Wireshark等工具抓取iOS设备与服务端之间的TCP数据包,确认消息是否已实际发送到服务端:

  • 若数据包已发出,问题出在服务端接收逻辑;
  • 若数据包未发出,检查iOS端网络权限、防火墙或代理设置。

4. 检查服务端接收逻辑

确认服务端是否正确处理TCP流式数据:

  • 服务端是否按约定的分隔符解析消息;
  • 服务端接收缓冲区是否足够,是否存在数据堆积;
  • 服务端是否正确处理异步接收事件。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 14:52:05