connection.c 6.18 KB
Newer Older
1 2
#include "common.h"
#include "connection.h"
3 4 5 6 7

#include <assert.h>
#include <stdlib.h>
#include <unistd.h>

8 9
//

10 11 12 13 14 15 16 17 18 19
#ifdef MINGW

#define setsockopt(a,b,c,d,e) setsockopt(a,b,c,(const void*)(d),e)

#else

#include <netinet/tcp.h>

#endif

20
//
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75

static void
__connection_init (struct connection * connection, const int sockfd) {
	connection->sockfd = sockfd;
	list_init (&connection->cp_link);

#ifdef DEBUG
	connection->sent_bytes = 0;
	connection->recv_bytes = 0;
#endif
}


/**
 * Opens a new connection to the given remote address and
 * returns a new connection structure.
 */
struct connection *
connection_open (struct addrinfo * addr) {
	//
	// Create a stream socket to the given address and connect to the server.
	// Upon connection, disable the Nagle algorithm to avoid delays on the
	// sender side and create a wrapper object for the connection.
	//
	int sockfd = socket(addr->ai_family, SOCK_STREAM, 0);
	check_std_error (sockfd < 0, "failed to create socket");

	int connect_result = connect(sockfd, addr->ai_addr, addr->ai_addrlen);
	check_std_error (connect_result < 0, "failed to connect to server");

	int tcp_nodelay = 1;
	int sso_result = setsockopt (
		sockfd, IPPROTO_TCP, TCP_NODELAY,
		&tcp_nodelay, sizeof (tcp_nodelay)
	);
	check_std_error (sso_result < 0, "failed to enable TCP_NODELAY");

	//

	struct connection * connection =
		(struct connection *) malloc (sizeof (struct connection));
	check_error (connection == NULL, "failed to allocate connection structure");

	__connection_init (connection, sockfd);
	return connection;
}


/**
 * Closes the connection and destroys the connection structure.
 */
void
connection_close (struct connection * connection) {
	assert (connection != NULL);

76
	debug (
77 78 79 80
		"socket %d: sent bytes %" PRIu64 ", recv bytes %" PRIu64 "\n",
		connection->sockfd, connection->sent_bytes, connection->recv_bytes
	);

81
	shutdown (connection->sockfd, SHUT_RDWR);
82 83 84 85 86 87 88 89
	close (connection->sockfd);
	free (connection);
}

//

typedef ssize_t (* xfer_fn) (int sockfd, void * buf, size_t len, int flags);

90
static inline ssize_t
91
__socket_xfer_full (xfer_fn xfer, const int sockfd, const void * buf, const size_t len) {
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
	unsigned char * buf_tail = (unsigned char *) buf;
	size_t remaining = len;

	while (remaining > 0) {
		ssize_t xferred = xfer (sockfd, buf_tail, remaining, 0);
		if (xferred < 0) {
			return -remaining;
		}

		remaining -= xferred;
		buf_tail += xferred;
	}

	return len;
}


/**
 * Sends data into the given connection. Does not return until all provided
 * data has been sent. Returns the number of bytes sent.
 */
ssize_t
114
connection_send_full (struct connection * connection, const void * buf, const size_t len) {
115
	assert (connection != NULL);
116
	assert ((buf != NULL) || (buf == NULL && len == 0));
117

118 119
	ssize_t sent = __socket_xfer_full ((xfer_fn) send, connection->sockfd, buf, len);
	check_std_error (sent < 0, "connection_send_full: error sending data to server");
120 121 122 123 124 125 126 127 128 129 130 131 132 133

#ifdef DEBUG
	connection->sent_bytes += sent;
#endif

	return sent;
}


/**
 * Receives a predefined amount of data from the given connection. Does not return
 * until all requested data has been received. Returns the number of bytes received.
 */
ssize_t
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
connection_recv_full (struct connection * connection, void * buf, const size_t len) {
	assert (connection != NULL);
	assert ((buf != NULL) || (buf == NULL && len == 0));

	ssize_t received = __socket_xfer_full ((xfer_fn) recv, connection->sockfd, buf, len);
	check_std_error (received < 0, "connection_recv_full: error receiving data from server");

#ifdef DEBUG
	connection->recv_bytes += received;
#endif

	return received;
}

/**
 * Receives data from the given connection.
 * Returns the number of bytes received.
 */
ssize_t
connection_recv (struct connection * connection, void * buf, const size_t len) {
154 155 156
	assert (connection != NULL);
	assert (buf != NULL);

157 158
	ssize_t received = recv (connection->sockfd, buf, len, 0);
	check_std_error (received < 0, "connection_recv: error receiving data from server");
159 160 161 162 163 164 165 166 167 168 169 170 171 172

#ifdef DEBUG
	connection->recv_bytes += received;
#endif

	return received;
}

//

#ifndef MINGW

typedef ssize_t (* xfer_iov_fn) (int sockfd, struct iovec * iovs, int iov_count);

173
static inline ssize_t
174
__socket_xfer_iov_full (
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
	xfer_iov_fn xfer_iov, const int sockfd, struct iovec * iovs, const int iov_count
) {
	ssize_t total = 0;

	int iov_index = 0;
	while (iov_index < iov_count) {
		//
		// Transfer as much of the remaining data as possible.
		// Return if an error occurred.
		//
		ssize_t res = xfer_iov (sockfd, iovs + iov_index, iov_count - iov_index);
		if (res >= 0) {
			size_t count = res;

			//
			// Remove bytes transferred from the beginning of the
			// chunk of data held in the IO vectors. If there is
			// data remaining in a vector, adjust its base and
			// length, and resume transfer.
			//
			total += count;
			while (iov_index < iov_count && count >= iovs [iov_index].iov_len) {
				count -= iovs [iov_index].iov_len;
				iov_index++;
			}

			if (count != 0) {
				iovs [iov_index].iov_base += count;
				iovs [iov_index].iov_len -= count;
			}
		} else {
			return res;
		}
	}

	return total;
}


/**
 * Sends vectored data into the given connection. Does not return until all
 * data have been sent. Returns the number of bytes sent.
 */
ssize_t
219
connection_send_iov_full (struct connection * connection, struct iovec * iovs, int iov_count) {
220 221 222 223
	assert (connection != NULL);
	assert (iovs != NULL);
	assert (iov_count >= 0);

224
	ssize_t sent = __socket_xfer_iov_full ((xfer_iov_fn) writev, connection->sockfd, iovs, iov_count);
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
	check_std_error (sent < 0, "error sending data to server");

#ifdef DEBUG
	connection->sent_bytes += sent;
#endif

	return sent;
}


/**
 * Receives vectored data from the given connection. Does not return until
 * all requested data have been received. Returns the number of bytes received.
 */
ssize_t
240
connection_recv_iov_full (struct connection * connection, struct iovec * iovs, int iov_count) {
241 242 243 244
	assert (connection != NULL);
	assert (iovs != NULL);
	assert (iov_count >= 0);

245 246
	ssize_t received = __socket_xfer_iov_full ((xfer_iov_fn) readv, connection->sockfd, iovs, iov_count);
	check_std_error (received < 0, "connection_recv_iov_full: error receiving data from server");
247 248 249 250 251 252 253 254 255

#ifdef DEBUG
	connection->recv_bytes += received;
#endif

	return received;
}

#endif /* !MINGW */