static CdiReturnStatus SocketEndpointOpen()

in src/cdi/adapter_socket.c [226:329]


static CdiReturnStatus SocketEndpointOpen(AdapterEndpointHandle endpoint_handle, const char* remote_address_str,
                                          int port_number)
{
    CdiReturnStatus ret = kCdiStatusOk;

    // Create an Internet socket which will be used for writing or reading.
    CdiSocket new_socket;
    if (CdiOsSocketOpen(remote_address_str, port_number, &new_socket)) {
        // Allocate memory in which to store socket endpoint specific state.
        endpoint_handle->type_specific_ptr = (SocketEndpointState*)CdiOsMemAllocZero(sizeof(SocketEndpointState));
        SocketEndpointState* private_state_ptr = (SocketEndpointState*)endpoint_handle->type_specific_ptr;
        if (private_state_ptr == NULL) {
            ret = kCdiStatusNotEnoughMemory;
        } else {
            // Save the now open file descriptor for use inside of receive thread or transmit function.
            private_state_ptr->socket = new_socket;
            private_state_ptr->destination_port_number = port_number;

            if (endpoint_handle->adapter_con_state_ptr->direction == kEndpointDirectionReceive ||
                endpoint_handle->adapter_con_state_ptr->direction == kEndpointDirectionBidirectional) {
                bool pool_created = false;
                bool thread_created = false;

                // Create the receive thread shutdown signal.
                bool signal_created = CdiOsSignalCreate(&private_state_ptr->shutdown);
                if (!signal_created) {
                    CDI_LOG_THREAD(kLogError, "Failed to create socket receive thread shutdown signal.");
                } else {
                    // Create a pool of ReceiveBufferRecord structures.
                    pool_created = CdiPoolCreateAndInitItems("socket receiver", RX_SOCKET_BUFFER_SIZE,
                                                             RX_SOCKET_BUFFER_SIZE_GROW, MAX_POOL_GROW_COUNT,
                                                             sizeof(ReceiveBufferRecord), true,
                                                             &private_state_ptr->receive_buffer_pool,
                                                             SocketEndpointPoolItemInit, NULL);
                    if (!pool_created) {
                        CDI_LOG_THREAD(kLogError, "Failed to allocate socket receive buffer pool.");
                    }
                }
                if (pool_created) {
                    // Start the receive thread.
                    thread_created = CdiOsThreadCreate(SocketReceiveThread, &private_state_ptr->receive_thread_id,
                                                       "socket receiver", endpoint_handle, NULL);
                    if (!thread_created) {
                        CDI_LOG_THREAD(kLogError, "Failed to start socket receive thread.");
                    }
                }

                // Make sure that everything got created. If not, clean up and return error.
                if (!(signal_created && pool_created && thread_created)) {
                    CdiPoolDestroy(private_state_ptr->receive_buffer_pool); // Not set to NULL (freed below).
                    CdiOsSignalDelete(private_state_ptr->shutdown);
                    CdiOsSocketClose(new_socket);
                    ret = kCdiStatusAllocationFailed;
                }
            }
        }
    } else {
        CDI_LOG_HANDLE(endpoint_handle->adapter_con_state_ptr->log_handle, kLogError,
                       "Failed to open socket on Destination Port[%d].", port_number);
        ret = kCdiStatusOpenFailed;
    }

    if (kCdiStatusOk == ret && (endpoint_handle->adapter_con_state_ptr->direction == kEndpointDirectionSend ||
        endpoint_handle->adapter_con_state_ptr->direction == kEndpointDirectionBidirectional)) {
        // This small delay helps when using cdi_test to send to a receiver in the same invocation. No means of
        // synchronizing between the transmitting and receiving connections is available so delaying the transmitter
        // helps give the receiver a better chance of being ready before packets start flowing to it.
        CdiOsSleep(50);
    }

    if (kCdiStatusOk == ret) {
        CdiProtocolVersionNumber version = {
            .version_num = 1,
            .major_version_num = 0,
            .probe_version_num = 0
        };
        if (endpoint_handle->cdi_endpoint_handle) {
            EndpointManagerProtocolVersionSet(endpoint_handle->cdi_endpoint_handle, &version);
        } else {
            // The control interface does not have a cdi_endpoint_handle, so set the protocol version directly here.
            ProtocolVersionSet(&version, &endpoint_handle->protocol_handle);
        }

        endpoint_handle->connection_status_code = kCdiConnectionStatusConnected;

        if (endpoint_handle->adapter_con_state_ptr->data_state.connection_cb_ptr) {
            // Notify application that we are connected.
            CdiCoreConnectionCbData cb_data = {
                .status_code = kCdiConnectionStatusConnected,
                .err_msg_str = NULL,
                .connection_user_cb_param = endpoint_handle->adapter_con_state_ptr->data_state.connection_user_cb_param
            };
            (endpoint_handle->adapter_con_state_ptr->data_state.connection_cb_ptr)(&cb_data);
        }
    } else {
        // An error occurred, so free the private memory, if it was allocated.
        if (endpoint_handle->type_specific_ptr) {
            CdiOsMemFree(endpoint_handle->type_specific_ptr);
            endpoint_handle->type_specific_ptr = NULL;
        }
    }

    return ret;
}