123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344 |
- /**
- * cs_tcp.h
- * Copyright (c) 2007-2018 ls
- **/
- #include "../inc/cs.h"
- #include "../inc/cs_str.h"
- #include "../inc/cs_tcp.h"
- #ifdef _WIN32
- #pragma comment(lib,"ws2_32.lib")
- //#define EINPROGRESS WSAEWOULDBLOCK
- CS_API int cs_tcp_init() {
- WSADATA wsaData;
- WSAStartup(MAKEWORD(2, 2), &wsaData);
- return (CS_OK);
- }
- CS_API void cs_tcp_cleanup() {
- WSACleanup();
- }
- #endif
- static int cs_tcp_v6_only(cs_sock_t s) {
- int yes = 1;
- if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(yes)) == -1) {
- cs_tcp_close(s);
- return (CS_ERR);
- }
- return (CS_OK);
- }
- static int cs_tcp_reuseaddr(cs_sock_t fd) {
- int yes = 1;
- /* Make sure connection-intensive things like the redis benckmark
- * will be able to close/open sockets a zillion of times */
- if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1) {
- // cs_strerror(err, "setsockopt SO_REUSEADDR: %s", strerror(errno));
- return (CS_ERR);
- }
- return (CS_OK);
- }
- static int _cs_tcp_listen(
- cs_sock_t s, struct sockaddr *sa, socklen_t len, int backlog) {
- if (bind(s,sa,len) == -1) {
- // cs_strerror(err, "bind: %s", strerror(errno));
- cs_tcp_close(s);
- return (CS_ERR);
- }
- if (listen(s, backlog) == -1) {
- // cs_strerror(err, "listen: %s", strerror(errno));
- cs_tcp_close(s);
- return (CS_ERR);
- }
- return (CS_OK);
- }
- static cs_sock_t _cs_tcp_server(
- int port, char *bindaddr, int af, int backlog) {
- cs_sock_t s = -1;
- int rv;
- char _port[6]; /* strlen("65535") */
- struct addrinfo hints, *servinfo, *p;
- #ifdef _WIN32
- sprintf_s(_port, 6, "%d", port);
- #else
- snprintf(_port, 6, "%d", port);
- #endif
- cs_bzero(&hints, sizeof(hints));
- hints.ai_family = af;
- hints.ai_socktype = SOCK_STREAM;
- hints.ai_flags = AI_PASSIVE; /* No effect if bindaddr != NULL */
- if ((rv = getaddrinfo(bindaddr, _port, &hints, &servinfo)) != 0) {
- // cs_strerror(err, "%s", gai_strerror(rv));
- return (CS_ERR);
- }
- for (p = servinfo; p != NULL; p = p->ai_next) {
- #ifdef _WIN32
- if ((s = WSASocket(
- p->ai_family,
- p->ai_socktype,
- p->ai_protocol,
- NULL,
- 0,
- WSA_FLAG_OVERLAPPED)) == -1) {
- continue;
- }
- #else
- if ((s = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
- continue;
- }
- #endif
- if (af == AF_INET6 && cs_tcp_v6_only(s) == CS_ERR) {
- goto error;
- }
- if (cs_tcp_reuseaddr(s) == CS_ERR) {
- goto error;
- }
- if (_cs_tcp_listen(s, p->ai_addr, p->ai_addrlen, backlog) == CS_ERR) {
- goto error;
- }
-
- goto end;
- }
- if (p == NULL) {
- // cs_strerror(err, "unable to bind socket, errno: %d", errno);
- goto error;
- }
- error:
- if (s != -1) {
- cs_tcp_close(s);
- }
- s = CS_ERR;
- end:
- freeaddrinfo(servinfo);
- return (s);
- }
- /*
- static cs_sock_t cs_tcp_v4_create() {
- cs_sock_t s;
- int flag = 1;
-
- #ifdef _WIN32
- if ((s = WSASocket(
- AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED)) == -1) {
- return (CS_ERR);
- }
- #else
- if ((s = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
- return (CS_ERR);
- }
- #endif
- if (setsockopt(
- s, SOL_SOCKET, SO_REUSEADDR, (void *)&flag, sizeof(flag)) == 1) {
- return (CS_ERR);
- }
-
- return (s);
- }
- static int cs_tcp_v4_listen(
- cs_sock_t s, struct sockaddr *sa, unsigned int len) {
- if (bind(s, sa, len) == -1) {
- cs_tcp_close(s);
- return (CS_ERR);
- }
- if (listen(s, 511) == -1) { // 511 is from nginx
- cs_tcp_close(s);
- return (CS_ERR);
- }
- return (CS_OK);
- }
- // */
- CS_API cs_sock_t cs_tcp_v4_server(int port, char *bindaddr) {
- return _cs_tcp_server(port, bindaddr, AF_INET, 511);
- }
- CS_API cs_sock_t cs_tcp_v6_server(int port, char *bindaddr) {
- return _cs_tcp_server(port, bindaddr, AF_INET6, 511);
- }
- static cs_inline cs_sock_t _cs_tcp_generic_accept(
- cs_sock_t s, struct sockaddr *sa, unsigned int *len) {
- cs_sock_t fd;
- while (1) {
- fd = accept(s, sa, len);
- if (fd == -1) {
- if (cs_sock_errno == EINTR) {
- continue;
- } else {
- // cs_strerror(err, "accept: %s", strerror(errno));
- return (CS_ERR);
- }
- }
- break;
- }
- return (fd);
- }
- CS_API cs_sock_t cs_tcp_accept(
- cs_sock_t s, u_char *ip, size_t ip_len, int *port) {
- cs_sock_t fd;
- struct sockaddr_storage sa;
- socklen_t salen = sizeof(sa);
- if ((fd = _cs_tcp_generic_accept(s, (struct sockaddr*)&sa, &salen)) == -1) {
- return CS_ERR;
- }
- if (sa.ss_family == AF_INET) {
- struct sockaddr_in *s = (struct sockaddr_in *)&sa;
- if (ip) {
- inet_ntop(AF_INET, (void*)&(s->sin_addr), (char *)ip, ip_len);
- }
- if (port) {
- *port = ntohs(s->sin_port);
- }
- } else {
- struct sockaddr_in6 *s = (struct sockaddr_in6 *)&sa;
- if (ip) {
- inet_ntop(AF_INET6, (void*)&(s->sin6_addr), (char *)ip, ip_len);
- }
- if (port) {
- *port = ntohs(s->sin6_port);
- }
- }
- return (fd);
- }
- static int _cs_tcp_nodelay(cs_sock_t fd, int yes) {
- if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void *)&yes, sizeof(yes))
- == -1) {
- // cs_strerror(err, "setsockopt TCP_NODELAY: %s", strerror(errno));
- return (CS_ERR);
- }
- return (CS_OK);
- }
- CS_API int cs_tcp_enable_nodelay(cs_sock_t fd) {
- return _cs_tcp_nodelay(fd, 1);
- }
- CS_API int cs_tcp_disable_nodelay(cs_sock_t fd) {
- return _cs_tcp_nodelay(fd, 0);
- }
- CS_API int cs_tcp_keepalive(cs_sock_t fd) {
- int yes = 1;
-
- if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&yes, sizeof(yes))
- == -1) {
- return (CS_ERR);
- }
-
- return (CS_OK);
- }
- /* Set the socket send timeout (SO_SNDTIMEO socket option) to the specified
- * number of milliseconds, or disable it if the 'ms' argument is zero. */
- CS_API int cs_tcp_sndtimeo(cs_sock_t fd, long long ms) {
- struct timeval tv;
- tv.tv_sec = ms / 1000;
- tv.tv_usec = (ms % 1000) * 1000;
- if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) == -1) {
- // cs_strerror(err, "setsockopt SO_SNDTIMEO: %s", strerror(errno));
- return (CS_ERR);
- }
- return (CS_OK);
- }
- CS_API int cs_tcp_rcvtimeo(cs_sock_t fd, long long ms) {
- struct timeval tv;
- tv.tv_sec = ms / 1000;
- tv.tv_usec = (ms % 1000) * 1000;
- if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) {
- // cs_strerror(err, "setsockopt SO_RCVTIMEO: %s", strerror(errno));
- return (CS_ERR);
- }
- return (CS_OK);
- }
- static int _cs_tcp_blocking(cs_sock_t fd, int yes) {
- int flags;
- /* Set the socket blocking (if yes is zero) or non-blocking.
- * Note that fcntl(2) for F_GETFL and F_SETFL can't be
- * interrupted by a signal. */
- #ifdef _WIN32
- u_long arg = yes;
- if (ioctlsocket(fd, FIONBIO, &arg) == SOCKET_ERROR){
- return (CS_ERR);
- }
- #else
- if ((flags = fcntl(fd, F_GETFL)) == -1) {
- // cs_strerror(err, "fcntl(F_GETFL): %s", strerror(errno));
- return (CS_ERR);
- }
- if (yes) {
- flags |= O_NONBLOCK;
- } else {
- flags &= ~O_NONBLOCK;
- }
- if (fcntl(fd, F_SETFL, flags) == -1) {
- // cs_strerror(err, "fcntl(F_SETFL,O_NONBLOCK): %s", strerror(errno));
- return (CS_ERR);
- }
- #endif
- return (CS_OK);
- }
- CS_API int cs_tcp_nonblocking(cs_sock_t fd) {
- return _cs_tcp_blocking(fd, 1);
- }
- CS_API int cs_tcp_blocking(cs_sock_t fd) {
- return _cs_tcp_blocking(fd, 0);
- }
- CS_API int cs_tcp_close(cs_sock_t fd) {
- #ifdef _WIN32
- shutdown(fd, SD_BOTH);
- closesocket(fd);
- #else
- shutdown(fd, SHUT_RDWR);
- close(fd);
- #endif
- return (CS_OK);
- }
|