11

I tried to make an example of simple socket server.

Build and run successfully. However it doesn't work well.

Client couldn't connect to this server.

How to solve this problem? I need your help, thanks.

import Foundation

let BUFF_SIZE = 1024

func initStruct<S>() -> S {
    let struct_pointer = UnsafePointer<S>.alloc(1)
    let struct_memory = struct_pointer.memory
    struct_pointer.destroy()
    return struct_memory
}

func sockaddr_cast(p: ConstUnsafePointer<sockaddr_in>) -> UnsafePointer<sockaddr> {
    return UnsafePointer<sockaddr>(p)
}

func socklen_t_cast(p: UnsafePointer<Int>) -> UnsafePointer<socklen_t> {
    return UnsafePointer<socklen_t>(p)
}

var server_socket: Int32
var client_socket: Int32
var server_addr_size: Int
var client_addr_size: Int

var server_addr: sockaddr_in = initStruct()
var client_addr: sockaddr_in = initStruct()

var buff_rcv: Array<CChar> = []
var buff_snd: String

server_socket = socket(PF_INET, SOCK_STREAM, 0);

if server_socket == -1
{
    println("[Fail] Create Server Socket")
    exit(1)
}
else
{
    println("[Success] Created Server Socket")
}

server_addr_size = sizeof(server_addr.dynamicType)
memset(&server_addr, 0, UInt(server_addr_size));

server_addr.sin_family = sa_family_t(AF_INET)
server_addr.sin_port = 4000
server_addr.sin_addr.s_addr = UInt32(0x00000000)    // INADDR_ANY = (u_int32_t)0x00000000 ----- <netinet/in.h>

let bind_server = bind(server_socket, sockaddr_cast(&server_addr), socklen_t(server_addr_size))

if bind_server == -1
{
    println("[Fail] Bind Port");
    exit(1);
}
else
{
    println("[Success] Binded Port");
}

if listen(server_socket, 5) == -1
{
    println("[Fail] Listen");
    exit(1);
}
else
{
    println("[Success] Listening : \(server_addr.sin_port) Port ...");
}

var n = 0

while n < 1
{
    client_addr_size = sizeof(client_addr.dynamicType)
    client_socket = accept(server_socket, sockaddr_cast(&client_addr), socklen_t_cast(&client_addr_size))

    if client_socket == -1
    {
        println("[Fail] Accept Client Connection");
        exit(1);
    }
    else
    {
        println("[Success] Accepted Client : \(inet_ntoa(client_addr.sin_addr)) : \(client_addr.sin_port)");
    }

    read(client_socket, &buff_rcv, UInt(BUFF_SIZE))

    println("[Success] Received : \(buff_rcv)")

    buff_snd = "\(strlen(buff_rcv)) : \(buff_rcv)"

    write(client_socket, &buff_snd, strlen(buff_snd) + 1)

    close(client_socket)
}
stevelee
  • 121
  • 1
  • 1
  • 3
  • have you cleaned up your code? I would use it also, I get an error at this line: `let struct_pointer = UnsafePointer.alloc(1)` **does not have a member named alloc** – János May 15 '15 at 12:04
  • This thread is rather old, but for anyone coming here looking for an up-to-date Swift socket package this might be what you want: https://github.com/IBM-Swift/BlueSocket – RenniePet Jan 27 '17 at 05:47
  • Another possibility is this one: https://github.com/Balancingrock/SwifterSockets – RenniePet Jan 28 '17 at 16:13

2 Answers2

4

The port number in the socket address must be in big-endian byte order:

server_addr.sin_port = UInt16(4000).bigEndian

So your program actually listens on port 40975 (hex 0xA00F) and not on port 4000 (hex 0x0FA0).

Another problem is here:

var buff_rcv: Array<CChar> = []
// ...
read(client_socket, &buff_rcv, UInt(BUFF_SIZE))

Your buffer is an empty array, but recv() expects a buffer of size BUFF_SIZE. The behaviour is undefined. To get a buffer of the required size, use

var buff_rcv = [CChar](count:BUFF_SIZE, repeatedValue:0)
// ...
read(client_socket, &buff_rcv, UInt(buff_rcv.count))

Remark: Here you cast the address of an Int to the address of an socklen_t and pass that to the accept() function:

client_socket = accept(server_socket, sockaddr_cast(&client_addr), socklen_t_cast(&client_addr_size))

That is not safe. If Int and socklen_t have different sizes then the behaviour will be undefined. You should declare server_addr_size and client_addr_size as socklen_t and remove the socklen_t_cast() function:

client_socket = accept(server_socket, sockaddr_cast(&client_addr), &client_addr_size)
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • There are actually more problems in your code. For example `buff_snd` is a Swift `String`. You cannot simply treat that as a `char` buffer in `write(client_socket, &buff_snd, strlen(buff_snd) + 1)`. – Martin R Jul 27 '14 at 08:07
  • Martin, could you recommend an easy implementation of socket server implemented in Swift that receives and responds strings? socket tutorial below claims, that only 4 method should be implemented, (socket, bind, listen, accept) but all the solution I checked out in SO and github are are so complex, differs from each other and overcomplicated http://www.cs.rpi.edu/~moorthy/Courses/os98/Pgms/socket.html – János May 15 '15 at 12:19
  • 1
    @János: Server socket programming isn't easy (at least if you want to handle multiple connections concurrently). GCDAsyncSocket (https://github.com/robbiehanson/CocoaAsyncSocket) is a nice wrapper written in Objective-C and can be used from Swift, but I do not have any recommendation. – Martin R May 15 '15 at 12:28
0

As Martin R commented before, the write command shouldn't be using the swift string as that. Something like this will work properly:

write(client_socket, buff_snd.cStringUsingEncoding(NSUTF8StringEncoding)!, countElements(buff_snd) + 1)
Paulo Garcia
  • 141
  • 2