3

I've been trying to write a simple TCP/IP server in swift but I couldn't come up with a solution. I've already tried searching on both here and the rest of the web but I couldn't find something that works for the latest swift version which I am working with:

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

Target: x86_64-apple-darwin14.5.0

Operating system:

Mac OS X 10.10.5 Yosemitte

The following code has been made and it's based on this one: 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);
}

The call to accept fails as it can be seen on the output given by the mentioned code:

Failure: accepting connection

Program ended with exit code: 1

In addition to the assistance on fixing the code, I would also like to know how I could read and write to/from the connection.

Thanks,

G Oliveira

Community
  • 1
  • 1
G Oliveira
  • 324
  • 4
  • 17

3 Answers3

6

In

UnsafeMutablePointer<socklen_t>(bitPattern: server_addr_size)

you are re-interpreting an integer variable as a pointer. That makes no sense and crashes at runtime because the contents of the variable does not point to valid memory. The accept() function expects the address of a variable of type socklen_t here.

There are other problems as well, e.g. the port number in the socket address must be in big-endian byte order:

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

otherwise your program listens on port 8496 instead of 12321.

Using NSMutableData is not really necessary, Since Swift 1.2 you can create a sockaddr_in structure with all elements set to zero simply with:

var server_addr = sockaddr_in()

and then withUnsafePointer() can be used to get an address to the structure.

If a system call fails, the global variable errno is set to a non-zero value indicating the cause of the error. perror() can be used to print the error message corresponding to errno.

You may want to set the SO_REUSEADDR socket option to avoid an "Address is already in use" error when binding the socket, see Uses of SO_REUSEADDR? or Socket options SO_REUSEADDR and SO_REUSEPORT, how do they differ? Do they mean the same across all major operating systems? for more information.

Here is a cleaned-up version of your code which works as expected in my test:

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")

The read() and write() system calls are then used to read from and write to the accepted socket.

Community
  • 1
  • 1
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
6

Just a little update for 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);
}
rougeExciter
  • 7,435
  • 2
  • 21
  • 17
3

To those who came to this topic looking for some help on the same subject, in addition to Martin's answer, I present, below, a small example on how I read and wrote to/from the socket:

// 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))

and we must not forget to close the connection after the code is done:

close(sock_fd)
close(client_fd)

EDIT: With FileDescriptors, we can do it even simpler!

let fileHandle = FileHandle(fileDescriptor: client_fd)
let data = fileHandle.readDataToEndOfFile()
print(String(data: data, encoding: .utf8))
unixb0y
  • 979
  • 1
  • 10
  • 39
G Oliveira
  • 324
  • 4
  • 17
  • Nowadays I would create a [FileHandle](https://developer.apple.com/documentation/foundation/filehandle) from the file descriptor, then it's much easier to read/write. – Max Jun 18 '19 at 13:32
  • Jup, like this! let fileHandle = FileHandle(fileDescriptor: client_fd) let data = fileHandle.readDataToEndOfFile() print(String(data: data, encoding: .utf8)) – unixb0y Sep 13 '19 at 16:32