如何在Swift中连接Socket以读取数据并写入数据

7
如何在Swift中创建一个监听并向其写入数据的socket?GCDAsyncSocket是正确的方式吗?还有其他的方式吗?我使用了CFStreamCreatePairWithSocketToHost来连接socket,但无法读取数据。

你是在iOS还是OS X上工作?你想让套接字处于打开和监听连接的状态吗?还是你正连接到现有主机,并且只是想进行双向通信? - Jeremy Pope
在iOS中,我想要使用socket连接到现有的主机,发送数据并监听传入的消息。 - user2056962
仅监听连接怎么样,@JeremyPope?(适用于OS X,Objective-C)我正在尝试使用CFStreamCreatePairWithSocketToHost,但没有成功,只有“无法建立连接”(确切地说是:Domain=NSPOSIXErrorDomain Code=61 "Connection refused",没有http和Error Domain=kCFErrorDomainCFNetwork Code=2 "(null)" UserInfo={kCFGetAddrInfoFailureKey=8}与“http:/localhost”)(主机:@"localhost"尝试使用@"http://localhost"或不同的错误,但也没有成功)。 - Mykhailo Lysenko
1
这篇帖子已经有些年头了,但对于任何寻找最新Swift套接字包的人来说,这可能是您想要的:https://github.com/IBM-Swift/BlueSocket - RenniePet
1
另一个可能性是这个:https://github.com/Balancingrock/SwifterSockets - RenniePet
3个回答

4

对于那些对Swift 3实现感兴趣的人。 这是从raywenderlich的示例中大量引用并进行了一些编辑。

func connect() {
    Stream.getStreamsToHost(withName: self.address!, port: self.port!, inputStream: &self.inputStream, outputStream: &self.outputStream);

    guard let _ = inputStream, let _ = outputStream else {
        // your own execption or otherwise
        return;
    }

    self.inputStream?.delegate = self;
    self.outputStream?.delegate = self;

    self.inputStream?.schedule(in: RunLoop.current, forMode: RunLoopMode.defaultRunLoopMode);
    self.outputStream?.schedule(in: RunLoop.current, forMode: RunLoopMode.defaultRunLoopMode);

    self.inputStream?.open();
    self.outputStream?.open();
}

func disconnect() {
    if let stream = self.inputStream {
        stream.close();
        stream.remove(from: RunLoop.current, forMode: RunLoopMode.commonModes);
    }
    if let stream = self.outputStream {
        stream.close();
        stream.remove(from: RunLoop.current, forMode: RunLoopMode.commonModes);
    }
    self.inputStream = nil;
    self.outputStream = nil;
}

func write(data:Data) {
    let _ = data.withUnsafeBytes { (unsafePointer:UnsafePointer<UInt8>) in
        let bytesWritten = self.outputStream?.write(unsafePointer, maxLength: data.count);
    };
}

func readAvailableBytes(stream: InputStream) {
    var buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: maxReadLength);
    while stream.hasBytesAvailable {
        let numberOfBytesRead = stream.read(buffer, maxLength: maxReadLength)
        if numberOfBytesRead < 0 {
            if let _ = stream.streamError {
                break
            }
        }
        //Construct the Message object
        if(numberOfBytesRead > 0){
            let output = String(cString:buffer)
            NSLog("server said: %@", output)
        } else {
            NSLog("empty string from stream")
        }

    }
    buffer.deallocate(capacity: maxReadLength);
}

请确保在您的处理类中实现StreamDelegate和委托方法:

func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
    NSLog("Receieve stream event: %d", eventCode.rawValue);
    switch (eventCode){
    case Stream.Event.errorOccurred:
        NSLog("ErrorOccurred")
        break;
    case Stream.Event.endEncountered:
        NSLog("EndEncountered")
        break;
    case Stream.Event.hasBytesAvailable:
        NSLog("HasBytesAvaible");
        break;
    case Stream.Event.openCompleted:
        NSLog("OpenCompleted");
        break;
    case Stream.Event.hasSpaceAvailable:
        NSLog("HasSpaceAvailable");
        break;
    default:
        NSLog("default reached. unknown stream event")
        break;
    }
} 

注意:与raywnderlich的实现有一些变化,尤其是在委托方法中。


这个能用来打开并监听本地的Web服务器以进行OAuth2回调吗? - Potion
如何使用流套接字启用代理? - DAC84

2
你可以使用桥接到Swift的SocketRocket ObjC框架。

https://github.com/square/SocketRocket

然后你只需创建该类的实例并在 Swift 中使用它,即:
var socket = SRWebSocket(...)
socket.open()
socket.send()
socket.close()

SocketRocket sources

根据用户的请求,我附上桥接头提示的截图: enter image description here 此外,请记得将您的应用程序链接到libcucore.dylib: enter image description here

1
当然,我在我的iOS应用程序中每天都使用它。如果不行的话,我不会向您建议它。 - leonardo
添加了这个框架到“链接二进制文件”之后,我还需要做什么?请帮帮我。 - user2056962
谢谢。我在哪里设置我的主机的 IP 地址和端口?socket-bridging-header.h 文件中有什么? - user2056962
如果这个或任何答案解决了您的问题,请考虑通过点击复选标记接受它。这向更广泛的社区表明您已经找到了解决方案,并为回答者和您自己赢得了一些声誉。没有义务这样做。 - leonardo
对不起,我忘记了。非常感谢您提供的信息。 - user2056962
显示剩余16条评论

1
我已经找到了swift的答案。我的应用程序代码是swift 2,服务器代码是java。我在使用swift socket时遇到了问题,但我找到了答案。这是我的问题链接 如何在swift中使用socket(连接,发送和接收消息),我使用了https://github.com/swiftsocket/SwiftSocket 我创建了自己的socket类,如下:
import Foundation

类 SocketManager {

let socket : TCPClient

class var sharedInstance: SocketManager {
    struct Singleton {
        static let instance = SocketManager()
    }
    return Singleton.instance
}

init() {
    // Create the socket
    self.socket = TCPClient(addr: "www.apple.com", port: 9000)
    // Connect the socket
    let (success, msg )=self.socket.connect(timeout: 1)
    print("\(msg) : \(success)")

}

internal func sendMessage(data: String){
    let qualityOfServiceClass = QOS_CLASS_BACKGROUND
    let backgroundQueue = dispatch_get_global_queue(qualityOfServiceClass, 0)
    dispatch_async(backgroundQueue, {
        let request = self.sendRequest(data, client: self.socket)
        print("received message: \(request)")
        dispatch_async(dispatch_get_main_queue(), { () -> Void in
            print("This is run on the main queue, after the previous code in outer block")
        })

    })
}

private func sendRequest(data: String, client: TCPClient?) -> (String?) {
    // It use ufter connection
    if client != nil {
        // Send data  (WE MUST ADD SENDING MESSAGE '\n' )
        let (isSuccess, errorMessage) = client!.send(str: "\(data)\n")
        if isSuccess {
            // Read response data
            let data = client!.read(1024*10)
            if let d = data {
                // Create String from response data
                if let str = NSString(bytes: d, length: d.count, encoding: NSUTF8StringEncoding) as? String {
                    return (data: str)
                } else {
                    return (data: nil)
                }
            } else {
                return (data: nil)
            }
        } else {
            print(errorMessage)
            return (data: nil)
        }
    } else {
        return (data: nil)
    }
}

}

use like this:

SocketManager.sharedInstance.sendMessage("Salom Hammaga")

这个能用来监听数据吗?比如服务器以字节形式发送了一些数据,这个类怎么知道数据已经被接收到了呢? - B L
使用函数 sendRequest() 并获取结果数据,如果 isSuccess { } 语句为真,则必须读取数据 <let data = client!.read(1024*10)>。 - Clever
私有函数 sendRequest(data: String, client: TCPClient?) -> (String?),此函数会返回一个字符串,即结果(接收到的数据)。 - Clever

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接