DispatchReadSource事件处理程序不为绑定套接字触发

时间:2017-08-10 20:06:02

标签: c swift sockets curl grand-central-dispatch

我试图在Swift中实现一个简单的TCP套接字服务器。这段代码应该与C实现非常相似,但需要额外的Swift-isms。

我尝试使用curl http://localhost:8080进行连接,这至少应触发"接受"即使服务器中没有HTTP逻辑。 curl的回复是Connection refused

这是我正在使用的代码。它输出2"听取描述符"线条,但没有"接受......"线

import LibC
import Dispatch

let port = 8080
let listenBacklogLen = 16

// Setup Sockets
let socketDescriptor = (
    ipv4: socket(PF_INET,  SOCK_STREAM, IPPROTO_TCP),
    ipv6: socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP)
)
let descriptors = [socketDescriptor.ipv4, socketDescriptor.ipv6]

var address = (
    ipv4: sockaddr_in(),
    ipv6: sockaddr_in6()
)

address.ipv4.sin_len = UInt8(MemoryLayout<sockaddr_in>.size)
address.ipv4.sin_family = sa_family_t(AF_INET)
address.ipv4.sin_port = in_port_t(port)
address.ipv4.sin_addr.s_addr = INADDR_ANY

address.ipv6.sin6_len = UInt8(MemoryLayout<sockaddr_in>.size)
address.ipv6.sin6_family = sa_family_t(AF_INET6)
address.ipv6.sin6_port = in_port_t(port)
address.ipv6.sin6_addr = in6addr_any

var reuseAddress: Int = 1
let reuseAddressLen = socklen_t(MemoryLayout.size(ofValue: reuseAddress))
withUnsafePointer(to: &reuseAddress) { ref in
    for descriptor in descriptors {
        assert(setsockopt(descriptor, SOL_SOCKET, SO_REUSEADDR, ref, reuseAddressLen) == 0)
    }
}

withUnsafePointer(to: &address.ipv4) { ref in
    ref.withMemoryRebound(to: sockaddr.self, capacity: 1) { reboundRef in
        assert(bind(socketDescriptor.ipv4, reboundRef, socklen_t(MemoryLayout.size(ofValue: ref.pointee))) == 0)
    }
}
withUnsafePointer(to: &address.ipv6) { ref in
    ref.withMemoryRebound(to: sockaddr.self, capacity: 1) { reboundRef in
        assert(bind(socketDescriptor.ipv6, reboundRef, socklen_t(MemoryLayout.size(ofValue: ref.pointee))) == 0)
    }
}

var acceptSources: [DispatchSourceRead] = []
var requestSources: [DispatchSourceRead] = []
for descriptor in descriptors {
    assert(listen(descriptor, Int32(listenBacklogLen)) == 0);

    let source = DispatchSource.makeReadSource(fileDescriptor: descriptor, queue: .global(qos: .userInitiated))
    source.setEventHandler {
        print("Accepting…")
        var address = sockaddr()
        var addressLen: socklen_t = socklen_t(MemoryLayout.size(ofValue: address))
        let requestSocketDescriptor = accept(descriptor, &address, &addressLen)
        assert(requestSocketDescriptor >= 0)

        let source = DispatchSource.makeReadSource(fileDescriptor: requestSocketDescriptor, queue: .global(qos: .userInitiated))
        source.setEventHandler { [unowned source] in
            var char: CChar = 0
            let bytesRead = read(requestSocketDescriptor, &char, 1)
            switch bytesRead {
            case -1:
                if ![EAGAIN, EINTR].contains(errno) {
                    print("Read returned error (\(errno)): \(strerror(errno))")
                    fallthrough
                }
            case 0:
                print("Done")
                source.cancel()
            default:
                print("C: \(char)")
            }
        }
        requestSources.append(source)
        source.resume()
    }
    acceptSources.append(source)

    print("Listening on descriptor: \(descriptor)")
    source.resume()
}

dispatchMain()
BTW,LibC是一个简单的模块,它统一了glibc和Darwin:

#if os(Linux)
    @_exported import Glibc
#else
    @_exported import Darwin.C
#endif

1 个答案:

答案 0 :(得分:0)

啊哈!端口必须是big-endian: address.ipv4.sin_port = in_port_t(port).bigEndian address.ipv6.sin_port = in_port_t(port).bigEndian

Socket Server Example with Swift

的帮助下计算出来