【问题标题】:Basic tcp/ip server in swiftswift中的基本tcp/ip服务器
【发布时间】:2016-02-17 02:45:06
【问题描述】:

我一直在尝试快速编写一个简单的 TCP/IP 服务器,但我想不出一个解决方案。我已经尝试在此处和其他网络上进行搜索,但找不到适用于我正在使用的最新 swift 版本的内容:

Apple Swift 2.1 版 (swiftlang-700.1.101.6 clang-700.1.76)

目标:x86_64-apple-darwin14.5.0

操作系统:

Mac OS X 10.10.5 优胜美地

以下代码已基于此:Socket Server Example with Swift

import Foundation

var sock_fd: Int32
var server_addr_size: Int

sock_fd = socket(AF_INET, SOCK_STREAM, 0);
if sock_fd == -1 {
    print("Failure: creating socket")
    exit(EXIT_FAILURE)
}
server_addr_size = sizeof(sockaddr_in)
var server_addr = NSMutableData(length: server_addr_size)!
memset(UnsafeMutablePointer<Void>(server_addr.mutableBytes), 0, server_addr_size)
var addr = UnsafeMutablePointer<sockaddr_in>(server_addr.mutableBytes)
addr.memory.sin_len = __uint8_t(server_addr_size)
addr.memory.sin_family = sa_family_t(AF_INET) // chooses IPv4
addr.memory.sin_port = 12321 // chooses the port

let bind_server = bind(sock_fd, UnsafePointer<sockaddr>(server_addr.mutableBytes), socklen_t(server_addr_size))

if bind_server == -1 {
    print("Failure: binding port")
    exit(EXIT_FAILURE)
}

if listen(sock_fd, 5) == -1 {
    print("Failure: listening")
    exit(EXIT_FAILURE)
}
var client_addr = NSMutableData(length: server_addr_size)!
memset(UnsafeMutablePointer<Void>(client_addr.mutableBytes), 0, server_addr_size)
let client_fd = accept(sock_fd, UnsafeMutablePointer<sockaddr>(client_addr.mutableBytes), UnsafeMutablePointer<socklen_t>(bitPattern: server_addr_size))

if client_fd == -1 {
    print("Failure: accepting connection")
    exit(EXIT_FAILURE);
}

accept 调用失败,从上述代码给出的输出中可以看出:

失败:接受连接

程序以退出代码结束:1

除了修复代码方面的帮助外,我还想知道如何在连接中读取和写入。

谢谢,

G 奥利维拉

【问题讨论】:

  • 您还需要打印出strerror(errno) 以查看accept() 失败的原因。
  • 感谢您的建议,这样可以更轻松地识别问题。

标签: swift tcp server ip


【解决方案1】:

UnsafeMutablePointer<socklen_t>(bitPattern: server_addr_size)

您将整数变量重新解释为指针。这使得没有 在运行时感知和崩溃,因为变量的内容确实 不指向有效内存。 accept() 函数需要 地址 socklen_t 类型的变量。

还有其他问题,例如套接字地址中的端口号必须是大端字节序:

server_addr.sin_port = UInt16(12321).bigEndian // chooses the port

否则您的程序会侦听端口 8496 而不是 12321。

使用NSMutableData 并不是必须的,因为 Swift 1.2 你可以 创建一个所有元素都设置为零的sockaddr_in 结构 只需:

var server_addr = sockaddr_in()

然后withUnsafePointer() 可用于获取结构的地址。

如果系统调用失败,则将全局变量errno 设置为非零值,指示错误原因。 perror()可用于打印errno对应的错误信息。

您可能需要设置SO_REUSEADDR 套接字选项以避免 绑定套接字时出现“地址已在使用”错误,请参阅 Uses of SO_REUSEADDR?Socket options SO_REUSEADDR and SO_REUSEPORT, how do they differ? Do they mean the same across all major operating systems? 了解更多信息。

这是您的代码的清理版本,可以按预期工作 在我的测试中:

let sock_fd = socket(AF_INET, SOCK_STREAM, 0);
if sock_fd == -1 {
    perror("Failure: creating socket")
    exit(EXIT_FAILURE)
}

var sock_opt_on = Int32(1)
setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &sock_opt_on, socklen_t(sizeofValue(sock_opt_on)))

var server_addr = sockaddr_in()
let server_addr_size = socklen_t(sizeofValue(server_addr))
server_addr.sin_len = UInt8(server_addr_size)
server_addr.sin_family = sa_family_t(AF_INET) // chooses IPv4
server_addr.sin_port = UInt16(12321).bigEndian // chooses the port

let bind_server = withUnsafePointer(&server_addr) { 
    bind(sock_fd, UnsafePointer($0), server_addr_size)
}
if bind_server == -1 {
    perror("Failure: binding port")
    exit(EXIT_FAILURE)
}

if listen(sock_fd, 5) == -1 {
    perror("Failure: listening")
    exit(EXIT_FAILURE)
}

var client_addr = sockaddr_storage()
var client_addr_len = socklen_t(sizeofValue(client_addr))
let client_fd = withUnsafeMutablePointer(&client_addr) { 
    accept(sock_fd, UnsafeMutablePointer($0), &client_addr_len)
}
if client_fd == -1 {
    perror("Failure: accepting connection")
    exit(EXIT_FAILURE);
}

print("connection accepted")

然后使用read()write() 系统调用从 并写入接受的套接字。

【讨论】:

    【解决方案2】:

    只是对 Swift 4 的一点更新。

    import Foundation
    
    let sock_fd = socket(AF_INET, SOCK_STREAM, 0);
    if sock_fd == -1 {
        perror("Failure: creating socket")
        exit(EXIT_FAILURE)
    }
    
    var sock_opt_on = Int32(1)
    setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &sock_opt_on, socklen_t(MemoryLayout.size(ofValue: sock_opt_on)))
    
    var server_addr = sockaddr_in()
    let server_addr_size = socklen_t(MemoryLayout.size(ofValue: server_addr))
    server_addr.sin_len = UInt8(server_addr_size)
    server_addr.sin_family = sa_family_t(AF_INET) // chooses IPv4
    server_addr.sin_port = UInt16(12321).bigEndian // chooses the port
    
    let bind_server = withUnsafePointer(to: &server_addr) {
        bind(sock_fd, UnsafeRawPointer($0).assumingMemoryBound(to: sockaddr.self), server_addr_size)
    }
    if bind_server == -1 {
        perror("Failure: binding port")
        exit(EXIT_FAILURE)
    }
    
    if listen(sock_fd, 5) == -1 {
        perror("Failure: listening")
        exit(EXIT_FAILURE)
    }
    
    var client_addr = sockaddr_storage()
    var client_addr_len = socklen_t(MemoryLayout.size(ofValue: client_addr))
    let client_fd = withUnsafeMutablePointer(to: &client_addr) {
        accept(sock_fd, UnsafeMutableRawPointer($0).assumingMemoryBound(to: sockaddr.self), &client_addr_len)
    }
    if client_fd == -1 {
        perror("Failure: accepting connection")
        exit(EXIT_FAILURE);
    }
    

    【讨论】:

    • 太好了!你测试了吗?
    • 是的,我还没有签入 4.1。
    • 在学习如何在 Swift 中从头开始制作 HTTP 服务器方面有什么好的参考资料吗?
    【解决方案3】:

    对于那些来到这个主题并在同一主题上寻求帮助的人,除了 Martin 的回答,我在下面展示了一个关于我如何读取和写入套接字的小示例:

    // reading one char at a time
    var buff_rcvd = CChar()
    read(client_fd, &buff_rcvd, 1)
    print(NSString(format:"Received: *%c*",buff_rcvd))
    
    // writing one chat at a time
    var buff_send: CChar = 65 // character "A" defined as CChar
    write(client_fd, &buff_send, 1)
    print(NSString(format:"Sent: *%c*",buff_send))
    

    代码完成后我们一定不要忘记关闭连接:

    close(sock_fd)
    close(client_fd)
    

    编辑:使用 FileDescriptors,我们可以做得更简单!

    let fileHandle = FileHandle(fileDescriptor: client_fd)
    let data = fileHandle.readDataToEndOfFile()
    print(String(data: data, encoding: .utf8))
    

    【讨论】:

    • 现在我会从文件描述符创建一个FileHandle,这样读/写就容易多了。
    • 呸,像这样! let fileHandle = FileHandle(fileDescriptor: client_fd) let data = fileHandle.readDataToEndOfFile() print(String(data: data, encoding: .utf8))
    猜你喜欢
    • 1970-01-01
    • 2016-12-30
    • 2020-04-15
    • 2010-10-22
    • 1970-01-01
    • 2010-10-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多