From 6d49cff3ee1d35d8411d9bb3b3e01e0d5e3f832c Mon Sep 17 00:00:00 2001 From: Luke Ceddia Date: Tue, 26 Jan 2016 23:11:05 +1100 Subject: [PATCH 1/3] Add preliminary support for tcp/ip on Linux _OPEN{CONNECTION, HOST, CLIENT}, _CONNECTIONADDRESS$ now do something, as the underlying tcp routines are implemented (with the exception of tcp_connected and thus _CONNECTED). --- internal/c/common.cpp | 2 +- internal/c/libqb.cpp | 170 +++++++++++++++++++++++++++++++++++------- 2 files changed, 142 insertions(+), 30 deletions(-) diff --git a/internal/c/common.cpp b/internal/c/common.cpp index 2057a96c2..8147de594 100644 --- a/internal/c/common.cpp +++ b/internal/c/common.cpp @@ -92,7 +92,7 @@ #include #include #include - +#include //OS/compiler specific includes #ifdef QB64_WINDOWS diff --git a/internal/c/libqb.cpp b/internal/c/libqb.cpp index a4f4418e5..145f505e5 100644 --- a/internal/c/libqb.cpp +++ b/internal/c/libqb.cpp @@ -52,7 +52,6 @@ #ifdef QB64_WINDOWS -#include #include #endif @@ -22983,6 +22982,7 @@ int32 func__printwidth(qbs* text, int32 screenhandle, int32 passed){ #else #include #include +#include #endif @@ -22994,22 +22994,32 @@ int32 func__printwidth(qbs* text, int32 screenhandle, int32 passed){ static int32 init=0; if (!init){ init=1; -#if defined(QB64_WINDOWS) && defined(DEPENDENCY_SOCKETS) +#if !defined(DEPENDENCY_SOCKETS) +#elif defined(QB64_WINDOWS) sockVersion = MAKEWORD(1, 1); WSAStartup(sockVersion, &wsaData); +#elif defined(QB64_LINUX) +#else #endif } } void tcp_done(){ -#if defined(QB64_WINDOWS) && defined(DEPENDENCY_SOCKETS) +#if !defined(DEPENDENCY_SOCKETS) +#elif defined(QB64_WINDOWS) WSACleanup(); +#elif defined(QB64_LINUX) +#else #endif } struct tcp_connection{ -#if defined(QB64_WINDOWS) && defined(DEPENDENCY_SOCKETS) +#if !defined(DEPENDENCY_SOCKETS) +#elif defined(QB64_WINDOWS) SOCKET socket; +#elif defined(QB64_LINUX) + int socket; +#else #endif int32 port;//connection to host & clients only uint8 ip4[4];//connection to host only @@ -23019,8 +23029,9 @@ int32 func__printwidth(qbs* text, int32 screenhandle, int32 passed){ void *tcp_host_open(int64 port){ tcp_init(); if ((port<0)||(port>65535)) return NULL; - -#if defined(QB64_WINDOWS) && defined(DEPENDENCY_SOCKETS) +#if !defined(DEPENDENCY_SOCKETS) + return NULL; +#elif defined(QB64_WINDOWS) //Ref. from 'winsock.h': typedef u_int SOCKET; static SOCKET listeningSocket; listeningSocket = socket(AF_INET, // Go over TCP/IP @@ -23048,9 +23059,43 @@ int32 func__printwidth(qbs* text, int32 screenhandle, int32 passed){ connection=(tcp_connection*)calloc(sizeof(tcp_connection),1); connection->socket=listeningSocket; return (void*)connection; -#endif +#elif defined(QB64_LINUX) + struct addrinfo hints, *servinfo, *p; + int sockfd; + char str_port[6]; + int yes = 1; + snprintf(str_port, 6, "%d", port); + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + if (getaddrinfo(NULL, str_port, &hints, &servinfo) != 0) return NULL; + for(p = servinfo; p != NULL; p = p->ai_next) { + sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol); + if (sockfd == -1) continue; + setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)); + if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) { + close(sockfd); + continue; + } + break; //if we get here, all is good + } + freeaddrinfo(servinfo); + if (p == NULL) return NULL; //indicates none of the entries succeeded + fcntl(sockfd, F_SETFL, O_NONBLOCK); //make socket non-blocking + + if (listen(sockfd, SOMAXCONN) == -1) { + close(sockfd); + return NULL; + } + tcp_connection *connection; + connection=(tcp_connection*)calloc(sizeof(tcp_connection),1); + connection->socket=sockfd; + return (void*)connection; +#else return NULL; +#endif } @@ -23066,8 +23111,9 @@ int32 func__printwidth(qbs* text, int32 screenhandle, int32 passed){ tcp_init(); if ((port<0)||(port>65535)) return NULL; - -#if defined(QB64_WINDOWS) && defined(DEPENDENCY_SOCKETS) +#if !defined(DEPENDENCY_SOCKETS) + return NULL; +#elif defined(QB64_WINDOWS) static LPHOSTENT hostEntry; hostEntry=gethostbyname((char*)host); if (!hostEntry) return NULL; @@ -23100,18 +23146,49 @@ int32 func__printwidth(qbs* text, int32 screenhandle, int32 passed){ connection=(tcp_connection*)calloc(sizeof(tcp_connection),1); connection->socket=theSocket; connection->port=port; - connection->hostname=(uint8*)malloc(strlen((char*)host)); + connection->hostname=(uint8*)malloc(strlen((char*)host)+1); memcpy(connection->hostname,host,strlen((char*)host)+1); return (void*)connection; -#endif +#elif defined(QB64_LINUX) + struct addrinfo hints, *servinfo, *p; + int sockfd; + char str_port[6]; + snprintf(str_port, 6, "%d", port); + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + if (getaddrinfo((char*)host, str_port, &hints, &servinfo) != 0) return NULL; + for(p = servinfo; p != NULL; p = p->ai_next) { + sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol); + if (sockfd == -1) continue; + if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) { + close(sockfd); + continue; + } + break; //if we get here, all is good + } + freeaddrinfo(servinfo); + if (p == NULL) return NULL; //indicates none of the entries succeeded + fcntl(sockfd, F_SETFL, O_NONBLOCK); //make socket non-blocking + //now we just need to create a struct tcp_connection to return + tcp_connection *connection; + connection=(tcp_connection*)calloc(sizeof(tcp_connection),1); + connection->socket=sockfd; + connection->port=port; + connection->hostname=(uint8*)malloc(strlen((char*)host)+1); + memcpy(connection->hostname,host,strlen((char*)host)+1); + return (void*)connection; +#else return NULL; +#endif } void *tcp_connection_open(void *host_tcp){ - -#if defined(QB64_WINDOWS) && defined(DEPENDENCY_SOCKETS) +#if !defined(DEPENDENCY_SOCKETS) + return NULL; +#elif defined(QB64_WINDOWS) static tcp_connection *host; host=(tcp_connection*)host_tcp; static sockaddr sa; static int sa_size; @@ -23131,30 +23208,57 @@ int32 func__printwidth(qbs* text, int32 screenhandle, int32 passed){ connection->port=*((uint16*)sa.sa_data); *((uint32*)(connection->ip4))=*((uint32*)(sa.sa_data+2)); return (void*)connection; -#endif +#elif defined(QB64_LINUX) + tcp_connection *host; host=(tcp_connection*)host_tcp; + struct sockaddr remote_addr; + socklen_t addr_size; + int fd; + + addr_size = sizeof(remote_addr); + fd = accept(host->socket, &remote_addr, &addr_size); + if (fd == -1) return NULL; + fcntl(fd, F_SETFL, O_NONBLOCK); //make socket non-blocking - return NULL; + tcp_connection *connection; + connection=(tcp_connection*)calloc(sizeof(tcp_connection),1); + connection->socket=fd; + //IPv4: port,port,ip,ip,ip,ip + connection->port=*((uint16*)remote_addr.sa_data); + *((uint32*)(connection->ip4))=*((uint32*)(remote_addr.sa_data+2)); + return (void*)connection; +#else + return NULL: +#endif } void tcp_close(void* connection){ - static tcp_connection *tcp=(tcp_connection*)connection; -#if defined(QB64_WINDOWS) && defined(DEPENDENCY_SOCKETS) - shutdown(tcp->socket,SD_BOTH); - closesocket(tcp->socket); + tcp_connection *tcp=(tcp_connection*)connection; + if (tcp->socket) { +#if !defined(DEPENDENCY_SOCKETS) +#elif defined(QB64_WINDOWS) + shutdown(tcp->socket,SD_BOTH); + closesocket(tcp->socket); +#elif defined(QB64_LINUX) + shutdown(tcp->socket, SHUT_RDWR); + close(tcp->socket); #endif + } if (tcp->hostname) free(tcp->hostname); free(tcp); } void tcp_out(void *connection,void *offset,ptrszint bytes){ - -#if defined(QB64_WINDOWS) && defined(DEPENDENCY_SOCKETS) +#if !defined(DEPENDENCY_SOCKETS) +#elif defined(QB64_WINDOWS) || defined(QB64_LINUX) static tcp_connection *tcp; tcp=(tcp_connection*)connection; static int nret; + //should check nret to make sure all data is sent + //MSG_NOSIGNAL? nret = send(tcp->socket, (char*)offset, bytes, 0); +#else #endif } @@ -23188,8 +23292,8 @@ int32 func__printwidth(qbs* text, int32 screenhandle, int32 passed){ void stream_update(stream_struct *stream){ - -#if defined(QB64_WINDOWS) && defined(DEPENDENCY_SOCKETS) +#if !defined(DEPENDENCY_SOCKETS) +#elif defined(QB64_WINDOWS) || defined(QB64_LINUX) //assume tcp static connection_struct *connection; @@ -23215,20 +23319,22 @@ int32 func__printwidth(qbs* text, int32 screenhandle, int32 passed){ bytes = recv(tcp->socket,(char*)(stream->in+stream->in_size), stream->in_limit-stream->in_size, 0); +#ifdef QB64_WINDOWS if (bytes==SOCKET_ERROR){ - //Known error codes: - //10035 WSAEWOULDBLOCK Resource temporarily unavailable. This error is returned from operations on nonblocking sockets that cannot be completed immediately, for example recv when no data is queued to be read from the socket. It is a nonfatal error, and the operation should be retried later. It is normal for WSAEWOULDBLOCK to be reported as the result from calling connect on a nonblocking SOCK_STREAM socket, since some time must elapse for the connection to be established. static ptrszint e; e=WSAGetLastError(); return; } +#else + if (bytes == -1) return; +#endif if (bytes<=0) return; stream->in_size+=bytes; if (stream->in_size==stream->in_limit) goto expand_and_retry; -#endif - +#else +#endif } @@ -23513,7 +23619,9 @@ int32 func__printwidth(qbs* text, int32 screenhandle, int32 passed){ int32 tcp_connected (void *connection){ static tcp_connection *tcp=(tcp_connection*)connection; -#if defined(QB64_WINDOWS) && defined(DEPENDENCY_SOCKETS) +#if !defined(DEPENDENCY_SOCKETS) + return 0; +#elif defined(QB64_WINDOWS) char buf; int length=recv(tcp->socket, &buf, 0, 0); int nError=WSAGetLastError(); @@ -23524,8 +23632,12 @@ int32 func__printwidth(qbs* text, int32 screenhandle, int32 passed){ if (nError==0){ if (length==0) return 0; } -#endif return -1; +#elif defined(QB64_LINUX) + return -1; +#else + return 0; +#endif } int32 func__connected(int32 i){ From 264ad29f22d976e89bcb9c7a1e781415560bdd2c Mon Sep 17 00:00:00 2001 From: Luke Ceddia Date: Sat, 30 Jan 2016 19:10:14 +1100 Subject: [PATCH 2/3] Make tcp_out try harder to send data. The function now interprets the return value from send() and recalls if needed to transmit all data. --- internal/c/libqb.cpp | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/internal/c/libqb.cpp b/internal/c/libqb.cpp index 145f505e5..745bace47 100644 --- a/internal/c/libqb.cpp +++ b/internal/c/libqb.cpp @@ -22975,6 +22975,7 @@ int32 func__printwidth(qbs* text, int32 screenhandle, int32 passed){ //Referenced: http://johnnie.jerrata.com/winsocktutorial/ + //Much of the unix sockets code based on http://beej.us/guide/bgnet/ #ifdef QB64_WINDOWS #include WSADATA wsaData; @@ -23250,21 +23251,22 @@ int32 func__printwidth(qbs* text, int32 screenhandle, int32 passed){ void tcp_out(void *connection,void *offset,ptrszint bytes){ #if !defined(DEPENDENCY_SOCKETS) #elif defined(QB64_WINDOWS) || defined(QB64_LINUX) - static tcp_connection *tcp; tcp=(tcp_connection*)connection; - static int nret; - //should check nret to make sure all data is sent - //MSG_NOSIGNAL? - nret = send(tcp->socket, - (char*)offset, - bytes, - 0); + tcp_connection *tcp; tcp=(tcp_connection*)connection; + int total = 0; // how many bytes we've sent + int bytesleft = bytes; // how many we have left to send + int n; + + while(total < bytes) { + n = send(tcp->socket, (char*)(offset + total), bytesleft, 0); + if (n == -1) break; + total += n; + bytesleft -= n; + } + //if (n == -1) error occured. #else #endif - } - - int32 cloud_port_redirect=-1; struct connection_struct{ int8 in_use;//0=not being used, 1=in use From 992a74c7ee156f236aa837add31e3493e2e49a5b Mon Sep 17 00:00:00 2001 From: Luke Ceddia Date: Wed, 3 Feb 2016 15:42:57 +1100 Subject: [PATCH 3/3] Implemented a better _CONNECTED It is no longer confused by buffered data. However, it no longer makes an explicit call to send()/recv(), so one of those must first fail to signal the disconnection. This allows _CONNECTED to be used to check if a GET/PUT succeeded (on the local end, anyway). --- internal/c/libqb.cpp | 60 +++++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 31 deletions(-) diff --git a/internal/c/libqb.cpp b/internal/c/libqb.cpp index 745bace47..3caef932e 100644 --- a/internal/c/libqb.cpp +++ b/internal/c/libqb.cpp @@ -23025,6 +23025,7 @@ int32 func__printwidth(qbs* text, int32 screenhandle, int32 passed){ int32 port;//connection to host & clients only uint8 ip4[4];//connection to host only uint8* hostname;//clients only + int connected; }; void *tcp_host_open(int64 port){ @@ -23059,6 +23060,7 @@ int32 func__printwidth(qbs* text, int32 screenhandle, int32 passed){ static tcp_connection *connection; connection=(tcp_connection*)calloc(sizeof(tcp_connection),1); connection->socket=listeningSocket; + connection->connected = -1; return (void*)connection; #elif defined(QB64_LINUX) struct addrinfo hints, *servinfo, *p; @@ -23093,6 +23095,7 @@ int32 func__printwidth(qbs* text, int32 screenhandle, int32 passed){ tcp_connection *connection; connection=(tcp_connection*)calloc(sizeof(tcp_connection),1); connection->socket=sockfd; + connection->connected = -1; return (void*)connection; #else return NULL; @@ -23147,6 +23150,7 @@ int32 func__printwidth(qbs* text, int32 screenhandle, int32 passed){ connection=(tcp_connection*)calloc(sizeof(tcp_connection),1); connection->socket=theSocket; connection->port=port; + connection->connected = -1; connection->hostname=(uint8*)malloc(strlen((char*)host)+1); memcpy(connection->hostname,host,strlen((char*)host)+1); return (void*)connection; @@ -23177,6 +23181,7 @@ int32 func__printwidth(qbs* text, int32 screenhandle, int32 passed){ connection=(tcp_connection*)calloc(sizeof(tcp_connection),1); connection->socket=sockfd; connection->port=port; + connection->connected = -1; connection->hostname=(uint8*)malloc(strlen((char*)host)+1); memcpy(connection->hostname,host,strlen((char*)host)+1); return (void*)connection; @@ -23207,6 +23212,7 @@ int32 func__printwidth(qbs* text, int32 screenhandle, int32 passed){ connection->socket=new_socket; //IPv4: port,port,ip,ip,ip,ip connection->port=*((uint16*)sa.sa_data); + connection->connected = -1; *((uint32*)(connection->ip4))=*((uint32*)(sa.sa_data+2)); return (void*)connection; #elif defined(QB64_LINUX) @@ -23223,6 +23229,7 @@ int32 func__printwidth(qbs* text, int32 screenhandle, int32 passed){ tcp_connection *connection; connection=(tcp_connection*)calloc(sizeof(tcp_connection),1); connection->socket=fd; + connection->connected = -1; //IPv4: port,port,ip,ip,ip,ip connection->port=*((uint16*)remote_addr.sa_data); *((uint32*)(connection->ip4))=*((uint32*)(remote_addr.sa_data+2)); @@ -23258,11 +23265,13 @@ int32 func__printwidth(qbs* text, int32 screenhandle, int32 passed){ while(total < bytes) { n = send(tcp->socket, (char*)(offset + total), bytesleft, 0); - if (n == -1) break; + if (n < 0) { + tcp->connected = 0; + return; + } total += n; bytesleft -= n; } - //if (n == -1) error occured. #else #endif } @@ -23313,28 +23322,29 @@ int32 func__printwidth(qbs* text, int32 screenhandle, int32 passed){ expand_and_retry: //expand buffer if 'in' stream is full + //also guarantees that bytes requested from recv() is not 0 if (stream->in_size==stream->in_limit){ stream->in_limit*=2; stream->in=(uint8*)realloc(stream->in,stream->in_limit); } bytes = recv(tcp->socket,(char*)(stream->in+stream->in_size), - stream->in_limit-stream->in_size, - 0); + stream->in_limit-stream->in_size, + 0); + if (bytes < 0) { //some kind of error #ifdef QB64_WINDOWS - if (bytes==SOCKET_ERROR){ - static ptrszint e; - e=WSAGetLastError(); - return; - } + if (WSAGetLastError() != WSAEWOULDBLOCK) tcp->connected = 0; //fatal error #else - if (bytes == -1) return; -#endif - if (bytes<=0) return; - stream->in_size+=bytes; - - - if (stream->in_size==stream->in_limit) goto expand_and_retry; + if (errno != EAGAIN && errno != EWOULDBLOCK) tcp->connected = 0; +#endif + } + else if (bytes == 0) { //graceful shutdown occured + tcp->connected = 0; + } + else { + stream->in_size+=bytes; + if (stream->in_size==stream->in_limit) goto expand_and_retry; + } #else #endif } @@ -23620,23 +23630,11 @@ int32 func__printwidth(qbs* text, int32 screenhandle, int32 passed){ } int32 tcp_connected (void *connection){ - static tcp_connection *tcp=(tcp_connection*)connection; + tcp_connection *tcp=(tcp_connection*)connection; #if !defined(DEPENDENCY_SOCKETS) return 0; -#elif defined(QB64_WINDOWS) - char buf; - int length=recv(tcp->socket, &buf, 0, 0); - int nError=WSAGetLastError(); - if(nError!=WSAEWOULDBLOCK&&nError!=0) - { - return 0; - } - if (nError==0){ - if (length==0) return 0; - } - return -1; -#elif defined(QB64_LINUX) - return -1; +#elif defined(QB64_WINDOWS) || defined(QB64_LINUX) + return tcp->connected; #else return 0; #endif