I try to establish a connection to a TCP server, unfortunately without success. Here is my actual way. Does anyone knows, where the error is? I'm getting the error at CFSocketConnectToAddress (I get the .error result, so my code results in the error while connecting print).
Any ideas?
host = "192.168.0.20"
port = 8888
socket = CFSocketCreate(kCFAllocatorDefault,
AF_INET,
SOCK_STREAM,
IPPROTO_TCP,
CFSocketCallBackType.readCallBack.rawValue, { (socket: CFSocket?, callBackType: CFSocketCallBackType, address: CFData?, data: UnsafeRawPointer?, info: UnsafeMutableRawPointer?) -> Void in print("callback test") }, nil)
if socket == nil {
print("Error while creating socket")
} else {
var sin = sockaddr_in(
sin_len: UInt8(MemoryLayout<sockaddr_in>.size),
sin_family: sa_family_t(AF_INET),
sin_port: in_port_t(port),
sin_addr: in_addr(s_addr: inet_addr(host)),
sin_zero: (0,0,0,0,0,0,0,0)
)
addressData = NSData(bytes: &sin, length: MemoryLayout.size(ofValue: sin)) as CFData
let connectResult: CFSocketError = CFSocketConnectToAddress(socket, addressData!, 10)
switch connectResult {
case .success:
print("Connected")
case .error:
print("Error while connecting")
case .timeout:
print("Timeout while connecting")
}
The problem was the port! The htons macro is not available in swift. This is the working way now:
func connectToServer(timeout: Int=10) throws {
// check, if address is valid https://linux.die.net/man/3/inet_makeaddr
let inAddr = inet_addr(host)
if inAddr == INADDR_NONE {
throw SocketError.noValidAddress
}
let socket = CFSocketCreate(kCFAllocatorDefault,
AF_INET,
SOCK_STREAM,
IPPROTO_TCP,
CFSocketCallBackType.readCallBack.rawValue,
{ (socket, callBackType, address, data, info) in
TCPClientCallBack(socket: socket, callBackType: callBackType, address: address, data: data, info: info)
},
nil)
if socket == nil {
throw SocketError.socketCreationFailed
}
var sin = sockaddr_in() // https://linux.die.net/man/7/ip
sin.sin_len = __uint8_t(MemoryLayout.size(ofValue: sin))
sin.sin_family = sa_family_t(AF_INET)
sin.sin_port = UInt16(port).bigEndian
sin.sin_addr.s_addr = inAddr
let addressDataCF = NSData(bytes: &sin, length: MemoryLayout.size(ofValue: sin)) as CFData
let socketErr = CFSocketConnectToAddress(socket, addressDataCF, CFTimeInterval(timeout))
switch socketErr {
case .success:
print("connected")
case .error:
throw SocketError.connectionError
case .timeout:
throw SocketError.connectionTimeout
}
}
enum SocketError: Error {
case noValidAddress
case socketCreationFailed
case connectionError
case connectionTimeout
}
Related
I try to connect with Discord using IPC, but whatever i try, the error message stays the same Optional("\u{02}\0\0\0(\0\0\0{"code":1003,"message":"protocol error"}")
I started with the encode function, but that is UInt8 because Swift apparently does not support an UInt32 data object.
Then i tried to create an UInt32 (memory) array, to convert that to Data, that failed as well.
can someone point me into the direction where I make a mistake.
import Foundation
import Cocoa
import Network
class Discord {
enum opcode: UInt32 {
case handshake = 0
case frame = 1
case close = 2
case ping = 3
case pong = 4
}
var appID: String
private var connection: NWConnection?
private let endpoint: String = NSTemporaryDirectory() + "discord-ipc-0"
init(appID: String) {
self.appID = appID
}
func connect() {
print("Connecting to \(endpoint)")
connection = NWConnection(
to: NWEndpoint.unix(path: endpoint),
using: .tcp
)
connection?.stateUpdateHandler = { state in
switch state {
case .setup:
print("Setting up...")
case .preparing:
print("Prepairing...")
case .waiting(let error):
print("Waiting: \(error)")
case .ready:
print("Ready...")
case .failed(let error):
print("Failed: \(error)")
case .cancelled:
print("Cancelled :'(")
default:
break
}
}
connection?.receiveMessage { completeContent, contentContext, isComplete, error in
print(
String(data: completeContent ?? Data(), encoding: .utf8),
error
)
}
connection?.start(queue: .global())
}
func uint32encode(opcode: opcode, message string: String) -> Data {
let payload = string.data(using: .utf8)!
var buffer = UnsafeMutableRawBufferPointer.allocate(byteCount: 8 + payload.count, alignment: 0)
defer { buffer.deallocate() }
buffer.copyBytes(from: payload)
buffer[8...] = buffer[..<payload.count]
buffer.storeBytes(of: opcode.rawValue, as: UInt32.self)
buffer.storeBytes(of: UInt32(payload.count), toByteOffset: 4, as: UInt32.self)
let uIntData = Data(bytes: &buffer, count: 8 + payload.count)
return uIntData
}
func encode(opcode: opcode, message string: String) -> Data {
let jsondata = string.data(using: .utf8)!
var data = Data()
data.append(UInt8(opcode.rawValue))
data.append(UInt8(jsondata.count))
data.append(contentsOf: [UInt8](jsondata))
/*
uint32 opcode (0 or 1)
uint32 length (length)
byte[length] jsonData (??)
*/
return data
}
func handshake() {
connect()
// We should say "hello", with opcode handshake
let hello = encode(opcode: .handshake, message: "{\"v\":1,\"client_id\":\"\(appID)\"}")
print("Sending \(String.init(data: hello, encoding: .utf8))")
connection?.send(
content: hello,
completion: .contentProcessed({ error in
print("Error:", error?.localizedDescription)
})
)
}
func handshakev2() {
connect()
// We should say "hello", with opcode handshake
let hello = uint32encode(opcode: .handshake, message: "{\"v\":1,\"client_id\":\"\(appID)\"}")
print("Sending (V2) \(String.init(data: hello, encoding: .utf8))")
connection?.send(
content: hello,
completion: .contentProcessed({ error in
print("Error (V2):", error?.localizedDescription)
})
)
}
}
For a local server I need to specify a port, which must not be in use. There's a really neat solution in Python to get a free port. However, such a socket library is not available in Swift. So I tried Using BSD Sockets in Swift, but that actually wants a port to be specified upfront and I cannot get the bind command to work. Here's the code I tried:
let socketFD = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if socketFD == -1 {
print("Error creating BSD Socket")
return
}
var hints = addrinfo(
ai_flags: AI_PASSIVE, // Assign the address of the local host to the socket structures
ai_family: AF_UNSPEC, // Either IPv4 or IPv6
ai_socktype: SOCK_STREAM, // TCP
ai_protocol: 0,
ai_addrlen: 0,
ai_canonname: nil,
ai_addr: nil,
ai_next: nil)
var servinfo: UnsafeMutablePointer<addrinfo>? = nil
let addrInfoResult = getaddrinfo(
nil, // Any interface
"8000", // The port on which will be listenend
&hints, // Protocol configuration as per above
&servinfo);
if addrInfoResult != 0 {
print("Error getting address info: \(errno)")
return
}
let bindResult = Darwin.bind(socketFD, servinfo!.pointee.ai_addr, socklen_t(servinfo!.pointee.ai_addrlen));
if bindResult == -1 {
print("Error binding socket to Address: \(errno)")
return
}
let listenResult = Darwin.listen(socketFD, 1);
if listenResult == -1 {
print("Error setting our socket to listen")
return
}
let port = Darwin.getsockname(socketFD, nil, nil);
The bind call always returns -1 and since I want to get a free port it makes no sense to specify one in getaddrinfo. What's the correct way here?
Here's a working solution:
func findFreePort() -> UInt16 {
var port: UInt16 = 8000;
let socketFD = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if socketFD == -1 {
//print("Error creating socket: \(errno)")
return port;
}
var hints = addrinfo(
ai_flags: AI_PASSIVE,
ai_family: AF_INET,
ai_socktype: SOCK_STREAM,
ai_protocol: 0,
ai_addrlen: 0,
ai_canonname: nil,
ai_addr: nil,
ai_next: nil
);
var addressInfo: UnsafeMutablePointer<addrinfo>? = nil;
var result = getaddrinfo(nil, "0", &hints, &addressInfo);
if result != 0 {
//print("Error getting address info: \(errno)")
close(socketFD);
return port;
}
result = Darwin.bind(socketFD, addressInfo!.pointee.ai_addr, socklen_t(addressInfo!.pointee.ai_addrlen));
if result == -1 {
//print("Error binding socket to an address: \(errno)")
close(socketFD);
return port;
}
result = Darwin.listen(socketFD, 1);
if result == -1 {
//print("Error setting socket to listen: \(errno)")
close(socketFD);
return port;
}
var addr_in = sockaddr_in();
addr_in.sin_len = UInt8(MemoryLayout.size(ofValue: addr_in));
addr_in.sin_family = sa_family_t(AF_INET);
var len = socklen_t(addr_in.sin_len);
result = withUnsafeMutablePointer(to: &addr_in, {
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
return Darwin.getsockname(socketFD, $0, &len);
}
});
if result == 0 {
port = addr_in.sin_port;
}
Darwin.shutdown(socketFD, SHUT_RDWR);
close(socketFD);
return port;
}
I’m sure someone can solve this in seconds but I’m very new to swift, using playgrounds on the iPad. I’m trying to modify some SendUDP code to recieve instead, but I can’t solve the compile error (unsafepointer is not convertible to unsaferawbufferpointer) on the readResult= line. The SEND works fine with very similar code, but I’m really struggling here, way out of my depth...
Here’s the code
func readUDP() {
guard
let addresses =
try ? addressesFor(host: "192.168.4.1", port: 80)
else {
print("host not found")
return
}
if addresses.count != 1 {
print("host ambiguous; using the first one")
}
address = addresses[0]
fd1 = socket(Int32(address.ss_family), SOCK_DGRAM, 0)
guard fd1 >= 0
else {
print("`socket` failed`")
return
}
defer {
let junk = close(fd1)
assert(junk == 0)
}
var message = [UInt8](repeating: 0, count: 1024)
let messageCount = message.count
var readResult = message.withUnsafeBytes {
(messagePtr: UnsafePointer < UInt8 > ) - > Int in
return address.withSockAddr {
(sa, saLen) - > Int in
return recvfrom(fd1, messagePtr, messageCount, 0, sa, & saLen)
}
}
guard readResult >= 0
else {
print("read failed")
return
}
print("success")
}
You can use Swift's implicit bridging to simplify to something like this:
var message = [UInt8](repeating: 0, count: 1024)
let messageCount = message.count
var readResult = address.withSockAddr {
(sa, saLen) - > Int in
return recvfrom(fd1, &message, messageCount, 0, sa, &saLen)
}
guard readResult >= 0
else {
print("read failed")
return
}
I am trying to program a simple server socket. It is successful to connect to client. But after I disconnect, and try to re-establish the server socket, during binding I got an error (errno = 48, EADDRINUSE). Anyone know how to fix it?
func initServerSocket(serverSocket: Int32) -> Int32 {
var clientSocket: Int32 = -1
var server_addr_size: Int
var client_addr_size = socklen_t(MemoryLayout<sockaddr_in>.size)
var server_addr = sockaddr_in()
var client_addr = sockaddr_in()
server_addr_size = MemoryLayout<sockaddr_in>.size
memset(&server_addr, 0, server_addr_size);
server_addr.sin_family = sa_family_t(AF_INET)
server_addr.sin_port = UInt16(8080).bigEndian
server_addr.sin_addr.s_addr = UInt32(0x00000000)
let bind_server = withUnsafePointer(to: &server_addr) {
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
bind(serverSocket, $0, socklen_t(MemoryLayout<sockaddr_in>.stride))
}
}
if bind_server == -1 {
print("[Fail] Bind Port, Error: \(errno)");
exit(1);
} else {
print("[Success] Binded Port \(bind_server)");
}
if listen(serverSocket, 5) == -1 {
print("[Fail] Listen");
exit(1);
} else {
print("[Success] Listening : \(server_addr.sin_port) Port ...");
}
clientSocket = withUnsafeMutablePointer(to: &client_addr) {
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
accept(serverSocket, $0, &client_addr_size)
}
}
if clientSocket == -1 {
print("[Fail] Accept Client Connection \(errno)");
exit(1);
} else {
print("[Success] Accepted Client : \(inet_ntoa(client_addr.sin_addr)!) : \(client_addr.sin_port)!");
}
return clientSocket
}
Disconnect function:
func disconnect() {
if clientSocket != -1 {
self.inputStream.close()
self.outputStream.close()
if close(HiveConfig.clientSocket) == -1 {
print("serverSocket close error")
}
if close(HiveConfig.serverSocket) == -1 {
print("serverSocket close error")
}
}
}
I figure out that on my code before bind need to add the following code to make the address reused.
var opt:Int = 1
setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, &opt, socklen_t(MemoryLayout.size(ofValue: opt)));
I am building an HTTP server in Swift. The server will eventually run on Linux, but right now only compiles on Mac OS.
Anyways, I have the socket connections working perfectly and I'm able to receive the data stream, but I am having difficulty finding the best way to determine how to detect the end of the socket data stream.
I know there's many ways todo this, and I know that sockets basically just spit out pure binary data and have no concept of a "packet" that has a beginning and end. My question what are the best techniques used to detect the end of a packet sent from a web browser requesting a web page? I know for JSON it's pretty easy, but I'm curious about when the client is requesting a web page specifically.
Thanks
InputStream did that for you, you don't have to care about that.
Try to use this:
import Foundation
typealias OnComing = (String) -> (Void)
class SocketClient: NSObject, StreamDelegate {
var host:String?
var port:Int?
var inputStream: InputStream?
var outputStream: OutputStream?
var status = false;
var output = ""
var bufferSize = 1024;
var onComing:OnComing!
func makeCFStreamConnection(host: String, port: Int, onComing:#escaping OnComing) {
self.host = host
self.port = port
self.onComing = onComing
Stream.getStreamsToHost(withName: host, port: port, inputStream: &self.inputStream, outputStream: &self.outputStream)
if self.inputStream != nil && self.outputStream != nil {
self.inputStream!.delegate = self
self.outputStream!.delegate = self
self.inputStream!.schedule(in: .main, forMode: RunLoopMode.defaultRunLoopMode)
self.outputStream!.schedule(in: .main, forMode: RunLoopMode.defaultRunLoopMode)
self.inputStream!.open()
self.outputStream!.open()
}
}
func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
if aStream === self.inputStream {
switch eventCode {
case Stream.Event.errorOccurred:
break
case Stream.Event.openCompleted:
break
case Stream.Event.hasBytesAvailable:
break
read()
default:
break
}
} else if aStream === self.outputStream {
switch eventCode {
case Stream.Event.errorOccurred:
break
case Stream.Event.openCompleted:
break
case Stream.Event.hasSpaceAvailable:
break
default:
break
}
}
}
func read() {
var buffer = [UInt8](repeating: 0, count: bufferSize)
while (self.inputStream!.hasBytesAvailable) {
let bytesRead: Int = inputStream!.read(&buffer, maxLength: buffer.count)
if bytesRead >= 0 {
output += NSString(bytes: UnsafePointer(buffer), length: bytesRead, encoding: String.Encoding.ascii.rawValue)! as String
} else {
print("# Stream read() error")
}
}
self.onComing(output)
}
func write(message:String) {
let encodedDataArray = [UInt8]("\(message)\n".utf8)
self.outputStream?.write(encodedDataArray, maxLength: encodedDataArray.count)
}
}
Your problem is resolved by read()