Added new parameter to connect API to allow the setting of a timeout value when using non-blocking sockets.
This commit is contained in:
+86
-4
@@ -88,6 +88,8 @@ public class Socket: SocketReader, SocketWriter {
|
||||
public static let SOCKET_ERR_CONNECTION_RESET = -9971
|
||||
public static let SOCKET_ERR_SET_RECV_TIMEOUT_FAILED = -9970
|
||||
public static let SOCKET_ERR_SET_WRITE_TIMEOUT_FAILED = -9969
|
||||
public static let SOCKET_ERR_CONNECT_TIMEOUT = -9968
|
||||
public static let SOCKET_ERR_GETSOCKOPT_FAILED = -9967
|
||||
|
||||
|
||||
///
|
||||
@@ -1644,10 +1646,12 @@ public class Socket: SocketReader, SocketWriter {
|
||||
/// Connects to the named host on the specified port.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - host: The host name to connect to.
|
||||
/// - port: The port to be used.
|
||||
/// - host: The host name to connect to.
|
||||
/// - port: The port to be used.
|
||||
/// - timeout: Timeout to use (in msec). *Note: Socket must be set to non-blocking mode or this
|
||||
/// parameter is ignored.*
|
||||
///
|
||||
public func connect(to host: String, port: Int32) throws {
|
||||
public func connect(to host: String, port: Int32, timeout: UInt = 0) throws {
|
||||
|
||||
// The socket must've been created and must not be connected...
|
||||
if self.socketfd == Socket.SOCKET_INVALID_DESCRIPTOR {
|
||||
@@ -1669,7 +1673,7 @@ public class Socket: SocketReader, SocketWriter {
|
||||
|
||||
throw Error(code: Socket.SOCKET_ERR_INVALID_PORT, reason: "The port specified is invalid. Must be in the range of 1-65535.")
|
||||
}
|
||||
|
||||
|
||||
// Tell the delegate to initialize as a client...
|
||||
do {
|
||||
|
||||
@@ -1745,6 +1749,22 @@ public class Socket: SocketReader, SocketWriter {
|
||||
if socketDescriptor == -1 {
|
||||
continue
|
||||
}
|
||||
|
||||
// Check to see if the socket is in non-blocking mode and if it is, set our trial socket to be non-blocking as well...
|
||||
if !self.isBlocking {
|
||||
|
||||
let flags = fcntl(socketDescriptor!, F_GETFL)
|
||||
if flags < 0 {
|
||||
|
||||
throw Error(code: Socket.SOCKET_ERR_GET_FCNTL_FAILED, reason: self.lastError())
|
||||
}
|
||||
|
||||
let result = fcntl(socketDescriptor!, F_SETFL, flags | O_NONBLOCK)
|
||||
if result < 0 {
|
||||
|
||||
throw Error(code: Socket.SOCKET_ERR_SET_FCNTL_FAILED, reason: self.lastError())
|
||||
}
|
||||
}
|
||||
|
||||
// Connect to the server...
|
||||
#if os(Linux)
|
||||
@@ -1757,6 +1777,68 @@ public class Socket: SocketReader, SocketWriter {
|
||||
if status == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
// If this is a non-blocking socket, check errno for EINPROGRESS and if set we've got a timeout, wait the appropriate time...
|
||||
if errno == EINPROGRESS {
|
||||
|
||||
if timeout > 0 {
|
||||
|
||||
// Set up for the select call...
|
||||
var writefds = fd_set()
|
||||
FD.ZERO(set: &writefds)
|
||||
FD.SET(fd: socketDescriptor!, set: &writefds)
|
||||
|
||||
var timer = timeval()
|
||||
|
||||
// First get seconds...
|
||||
let secs = Int(Double(timeout / 1000))
|
||||
timer.tv_sec = secs
|
||||
|
||||
// Now get the leftover millisecs...
|
||||
let msecs = Int32(Double(timeout % 1000))
|
||||
|
||||
// Note: timeval expects microseconds, convert now...
|
||||
let uSecs = msecs * 1000
|
||||
|
||||
// Now the leftover microseconds...
|
||||
#if os(Linux)
|
||||
timer.tv_usec = Int(uSecs)
|
||||
#else
|
||||
timer.tv_usec = Int32(uSecs)
|
||||
#endif
|
||||
|
||||
let count = select(socketDescriptor! + Int32(1), nil, &writefds, nil, &timer)
|
||||
if count < 0 {
|
||||
|
||||
throw Error(code: Socket.SOCKET_ERR_SELECT_FAILED, reason: self.lastError())
|
||||
}
|
||||
|
||||
// If the socket is writable, we're probably connected, but check anyway to be sure...
|
||||
// Otherwise, we've timed out waiting to connect.
|
||||
if FD.ISSET(fd: socketDescriptor!, set: &writefds) {
|
||||
|
||||
// Check the socket...
|
||||
var result: Int = 0
|
||||
var resultLength = socklen_t(MemoryLayout<Int>.size)
|
||||
if getsockopt(socketDescriptor!, SOL_SOCKET, SO_ERROR, &result, &resultLength) < 0 {
|
||||
|
||||
throw Error(code: Socket.SOCKET_ERR_GETSOCKOPT_FAILED, reason: self.lastError())
|
||||
}
|
||||
|
||||
// Check the result of the socket connect...
|
||||
if result == 0 {
|
||||
|
||||
// Success, we're connected, clear status and break out of the loop...
|
||||
status = 0
|
||||
break
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
throw Error(code: Socket.SOCKET_ERR_CONNECT_TIMEOUT, reason: self.lastError())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Close the socket that was opened... Protocol family may have changed...
|
||||
#if os(Linux)
|
||||
|
||||
Reference in New Issue
Block a user