Swift实现非WebSocket的Socket客户端对接Python Socket服务器
Hey there! I totally get where you're stuck—when you're used to Python's dead-simple socket module, hunting for a raw TCP/UDP socket implementation in Swift can feel frustrating when all the search results are pushing WebSocket libraries. Let's fix that: you don't need any external packages—Swift's Foundation framework has everything you need to build a basic non-WebSocket socket client that talks directly to your Python server.
1. Use Swift's Native BSD Socket APIs
Under the hood, Swift (via Foundation) exposes the same BSD socket interface that Python's socket module uses. This means you can create, connect, send, and receive data just like you do in Python—no WebSocket framing or extra layers required.
2. Complete TCP Client Implementation
Here's a practical, thread-safe example that connects to your Python server, sends messages, and listens for responses. We'll use Grand Central Dispatch (GCD) to keep network operations off the main thread (critical for keeping your iOS app responsive):
import Foundation class RawTCPClient { private var host: String private var port: Int private var socketFD: Int32 = -1 init(host: String, port: Int) { self.host = host self.port = port } // Establish connection to server func connect(completion: @escaping (Bool) -> Void) { DispatchQueue.global(qos: .background).async { // Create TCP socket self.socketFD = socket(AF_INET, SOCK_STREAM, 0) guard self.socketFD != -1 else { print("Failed to create socket") DispatchQueue.main.async { completion(false) } return } // Configure server address var serverAddr = sockaddr_in() serverAddr.sin_family = sa_family_t(AF_INET) serverAddr.sin_port = UInt16(self.port).bigEndian // Convert to network byte order // Resolve host to IP address guard let ipAddr = self.resolveHost(toIP: self.host) else { print("Couldn't resolve host address") DispatchQueue.main.async { completion(false) } return } serverAddr.sin_addr = ipAddr // Connect to server let connectionResult = connect( self.socketFD, sockaddr_cast(&serverAddr), socklen_t(MemoryLayout<sockaddr_in>.size) ) DispatchQueue.main.async { completion(connectionResult == 0) } } } // Send data to server func send(data: Data, completion: @escaping (Bool) -> Void) { DispatchQueue.global(qos: .background).async { guard self.socketFD != -1 else { print("Not connected to server") DispatchQueue.main.async { completion(false) } return } let bytesSent = send(self.socketFD, data.bytes, data.count, 0) DispatchQueue.main.async { completion(bytesSent == data.count) } } } // Receive data from server func receive(maxBufferSize: Int = 1024, completion: @escaping (Data?) -> Void) { DispatchQueue.global(qos: .background).async { guard self.socketFD != -1 else { print("Not connected to server") DispatchQueue.main.async { completion(nil) } return } var buffer = [UInt8](repeating: 0, count: maxBufferSize) let bytesReceived = recv(self.socketFD, &buffer, maxBufferSize, 0) guard bytesReceived > 0 else { print("Connection closed or error receiving data") DispatchQueue.main.async { completion(nil) } return } let receivedData = Data(buffer[0..<bytesReceived]) DispatchQueue.main.async { completion(receivedData) } } } // Close the connection func disconnect() { if socketFD != -1 { close(socketFD) socketFD = -1 } } // Helper: Resolve hostname to IPv4 address private func resolveHost(toIP host: String) -> in_addr? { var hints = addrinfo( ai_flags: 0, ai_family: AF_INET, ai_socktype: SOCK_STREAM, ai_protocol: IPPROTO_TCP, ai_addrlen: 0, ai_canonname: nil, ai_addr: nil, ai_next: nil ) var infoPtr: UnsafeMutablePointer<addrinfo>? defer { freeaddrinfo(infoPtr) } let resolveResult = getaddrinfo(host, nil, &hints, &infoPtr) guard resolveResult == 0, let info = infoPtr else { return nil } let sockaddrIn = UnsafeMutablePointer<sockaddr_in>(OpaquePointer(info.pointee.ai_addr)) return sockaddrIn.pointee.sin_addr } } // Example usage let client = RawTCPClient(host: "192.168.1.100", port: 5000) // Replace with your server IP/port client.connect { success in if success { print("Connected to Python server!") // Send a test message let message = "Hello from Swift TCP Client!" if let messageData = message.data(using: .utf8) { client.send(data: messageData) { sendSuccess in if sendSuccess { print("Message sent") // Wait for response client.receive { responseData in if let data = responseData, let response = String(data: data, encoding: .utf8) { print("Received response: \(response)") } } } } } } else { print("Failed to connect") } } // Remember to disconnect when finished (e.g., in viewWillDisappear) // client.disconnect()
3. Critical Compatibility Tips for Your Python Server
- Match Socket Type: If your Python server uses TCP (
socket.SOCK_STREAM), the example above works perfectly. If you're using UDP, swapSOCK_STREAMwithSOCK_DGRAMin the Swift code, and usesendto/recvfrominstead ofsend/recv. - Consistent Encoding: Both your Python server and Swift client should use the same string encoding (UTF-8 is standard) when converting between strings and raw bytes.
- Threading Handling: Your Python server uses threading to manage multiple clients—make sure your Swift client handles disconnections gracefully and doesn't block the main thread (the example uses GCD to avoid this).
4. Why You're Seeing Mostly WebSocket Libraries
WebSockets are popular for real-time apps because they add a standardized framing layer and keep-alive mechanisms on top of TCP. But for a raw socket setup like your Python socket implementation, Swift's native APIs are all you need—no third-party libraries required.
内容的提问来源于stack exchange,提问作者Adam

