On 6/13/05, felix winkelmann <[EMAIL PROTECTED]> wrote:
>
> Ahem, no. Well it does, but it doesn't print the error message on retry.
> Give me a sec...
>
I forgot: you also need a new tcp.scm (attached).
BTW, is it somehow possible to nail down under what circumstances
the error occurs? (client, request/reply data, ..)
cheers,
felix
;;;; tcp.scm - Networking stuff
;
; Copyright (c) 2000-2005, Felix L. Winkelmann
; All rights reserved.
;
; Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
; conditions are met:
;
; Redistributions of source code must retain the above copyright notice, this list of conditions and the following
; disclaimer.
; Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
; disclaimer in the documentation and/or other materials provided with the distribution.
; Neither the name of the author nor the names of its contributors may be used to endorse or promote
; products derived from this software without specific prior written permission.
;
; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
; AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
; CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
; SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
; OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
; POSSIBILITY OF SUCH DAMAGE.
;
; Send bugs, suggestions and ideas to:
;
; [EMAIL PROTECTED]
;
; Felix L. Winkelmann
; Unter den Gleichen 1
; 37130 Gleichen
; Germany
(declare
(unit tcp)
(uses extras scheduler)
(usual-integrations)
(fixnum-arithmetic)
(no-bound-checks)
(export tcp-close tcp-listen tcp-connect tcp-accept tcp-accept-ready? ##sys#tcp-port->fileno tcp-listener? tcp-addresses
tcp-abandon-port tcp-listener-port tcp-listener-fileno)
(bound-to-procedure
##net#socket ##net#bind ##net#connect ##net#listen ##net#accept
##net#close ##net#recv ##net#send ##net#select ##net#select-write ##net#gethostaddr ##net#io-ports ##sys#update-errno
##sys#error ##sys#signal-hook ##net#getservbyname ##net#parse-host ##net#fresh-addr
##net#bind-socket ##net#shutdown)
(foreign-declare #<<EOF
#include <errno.h>
#ifdef _WIN32
# include <winsock2.h>
static WSADATA wsa;
# define fcntl(a, b, c) 0
# define EWOULDBLOCK 0
# define EINPROGRESS 0
#else
# include <fcntl.h>
# include <sys/types.h>
# include <sys/socket.h>
# include <sys/time.h>
# include <netinet/in.h>
# include <unistd.h>
# include <netdb.h>
# define SD_RECEIVE 0
# define SD_SEND 1
# define closesocket close
# define INVALID_SOCKET -1
#endif
static char addr_buffer[ 20 ];
EOF
) )
(register-feature! 'tcp)
(cond-expand
[unsafe
(eval-when (compile)
(define-macro (##sys#check-structure x y . _) '(##core#undefined))
(define-macro (##sys#check-range x y z) '(##core#undefined))
(define-macro (##sys#check-pair x) '(##core#undefined))
(define-macro (##sys#check-list x) '(##core#undefined))
(define-macro (##sys#check-symbol x) '(##core#undefined))
(define-macro (##sys#check-string x) '(##core#undefined))
(define-macro (##sys#check-char x) '(##core#undefined))
(define-macro (##sys#check-exact x) '(##core#undefined))
(define-macro (##sys#check-port x) '(##core#undefined))
(define-macro (##sys#check-number x) '(##core#undefined))
(define-macro (##sys#check-byte-vector x) '(##core#undefined)) ) ]
[else] )
(define-foreign-variable errno int "errno")
(define-foreign-variable strerror c-string "strerror(errno)")
(define-foreign-type sockaddr* (pointer "struct sockaddr"))
(define-foreign-type sockaddr_in* (pointer "struct sockaddr_in"))
(define-foreign-variable _af_inet int "AF_INET")
(define-foreign-variable _sock_stream int "SOCK_STREAM")
(define-foreign-variable _sock_dgram int "SOCK_DGRAM")
(define-foreign-variable _sockaddr_size int "sizeof(struct sockaddr)")
(define-foreign-variable _sockaddr_in_size int "sizeof(struct sockaddr_in)")
(define-foreign-variable _sd_receive int "SD_RECEIVE")
(define-foreign-variable _sd_send int "SD_SEND")
(define-foreign-variable _ipproto_tcp int "IPPROTO_TCP")
(define-foreign-variable _invalid_socket int "INVALID_SOCKET")
(define-foreign-variable _ewouldblock int "EWOULDBLOCK")
(define-foreign-variable _einprogress int "EINPROGRESS")
(define ##net#socket (foreign-lambda int "socket" int int int))
(define ##net#bind (foreign-lambda int "bind" int scheme-pointer int))
(define ##net#listen (foreign-lambda int "listen" int int))
(define ##net#accept (foreign-lambda int "accept" int c-pointer c-pointer))
(define ##net#close (foreign-lambda int "closesocket" int))
(define ##net#send (foreign-lambda int "send" int scheme-pointer int int))
(define ##net#recv (foreign-lambda int "recv" int scheme-pointer int int))
(define ##net#shutdown (foreign-lambda int "shutdown" int int))
(define ##net#connect (foreign-lambda int "connect" int scheme-pointer int))
(define ##net#make-nonblocking
(foreign-lambda* bool ([int fd])
"int val = fcntl(fd, F_GETFL, 0);"
"if(val == -1) return(0);"
"return(fcntl(fd, F_SETFL, val | O_NONBLOCK) != -1);") )
(define ##net#getsockname
(foreign-lambda* c-string ([int s])
"struct sockaddr_in sa;"
"unsigned char *ptr;"
"int len = sizeof(struct sockaddr_in);"
"if(getsockname(s, (struct sockaddr *)&sa, &len) != 0) return(NULL);"
"ptr = (unsigned char *)&sa.sin_addr;"
"sprintf(addr_buffer, \"%d.%d.%d.%d\", ptr[ 0 ], ptr[ 1 ], ptr[ 2 ], ptr[ 3 ]);"
"return(addr_buffer);") )
(define ##net#getsockport
(foreign-lambda* int ([int s])
"struct sockaddr_in sa;"
"unsigned char *ptr;"
"int len = sizeof(struct sockaddr_in);"
"if(getsockname(s, (struct sockaddr *)&sa, &len) != 0) return(-1);"
"else return(ntohs(sa.sin_port));") )
(define ##net#getpeername
(foreign-lambda* c-string ([int s])
"struct sockaddr_in sa;"
"unsigned char *ptr;"
"int len = sizeof(struct sockaddr_in);"
"if(getpeername(s, (struct sockaddr *)&sa, &len) != 0) return(NULL);"
"ptr = (unsigned char *)&sa.sin_addr;"
"sprintf(addr_buffer, \"%d.%d.%d.%d\", ptr[ 0 ], ptr[ 1 ], ptr[ 2 ], ptr[ 3 ]);"
"return(addr_buffer);") )
(define ##net#startup
(foreign-lambda* bool () #<<EOF
#ifdef _WIN32
return(WSAStartup(MAKEWORD(1, 1), &wsa) == 0);
#else
return(1);
#endif
EOF
) )
(unless (##net#startup)
(##sys#signal-hook #:network-error "can not initialize Winsock") )
(define ##net#getservbyname
(foreign-lambda* int ((c-string serv) (c-string proto))
"struct servent *se;
if((se = getservbyname(serv, proto)) == NULL) return(0);
else return(se->s_port);") )
(define ##net#select
(foreign-lambda* int ((int fd))
"fd_set in;
struct timeval tm;
int rv;
FD_ZERO(&in);
FD_SET(fd, &in);
tm.tv_sec = tm.tv_usec = 0;
rv = select(fd + 1, &in, NULL, NULL, &tm);
if(rv > 0) { rv = FD_ISSET(fd, &in) ? 1 : 0; }
return(rv);") )
(define ##net#select-write
(foreign-lambda* int ((int fd))
"fd_set out;
struct timeval tm;
int rv;
FD_ZERO(&out);
FD_SET(fd, &out);
tm.tv_sec = tm.tv_usec = 0;
rv = select(fd + 1, NULL, &out, NULL, &tm);
if(rv > 0) { rv = FD_ISSET(fd, &out) ? 1 : 0; }
return(rv);") )
(define ##net#gethostaddr
(foreign-lambda* bool ((scheme-pointer saddr) (c-string host) (unsigned-short port))
"struct hostent *he = gethostbyname(host);"
"struct sockaddr_in *addr = (struct sockaddr_in *)saddr;"
"if(he == NULL) return(0);"
"memset(addr, 0, sizeof(struct sockaddr_in));"
"addr->sin_family = AF_INET;"
"addr->sin_port = htons((short)port);"
"addr->sin_addr = *((struct in_addr *)he->h_addr);"
"return(1);") )
(define (yield)
(##sys#call-with-current-continuation
(lambda (return)
(let ((ct ##sys#current-thread))
(##sys#setslot ct 1 (lambda () (return (##core#undefined))))
(##sys#schedule) ) ) ) )
(define ##net#parse-host
(let ([substring substring])
(lambda (host proto)
(let ([len (##sys#size host)])
(let loop ([i 0])
(if (fx>= i len)
(values host #f)
(let ([c (##core#inline "C_subchar" host i)])
(if (char=? c #\:)
(values
(substring host (add1 i) len)
(let* ([s (substring host 0 i)]
[p (##net#getservbyname s proto)] )
(when (eq? 0 p)
(##sys#update-errno)
(##sys#signal-hook
#:network-error 'tcp-connect (##sys#string-append "can not compute port from service - " strerror)
s) )
p) )
(loop (fx+ i 1)) ) ) ) ) ) ) ) )
(define ##net#fresh-addr
(foreign-lambda* void ((scheme-pointer saddr) (unsigned-short port))
"struct sockaddr_in *addr = (struct sockaddr_in *)saddr;"
"memset(addr, 0, sizeof(struct sockaddr_in));"
"addr->sin_family = AF_INET;"
"addr->sin_port = htons(port);"
"addr->sin_addr.s_addr = htonl(INADDR_ANY);") )
(define (##net#bind-socket port style host)
(##sys#check-exact port)
(let ([s (##net#socket _af_inet style 0)])
(when (eq? _invalid_socket s)
(##sys#update-errno)
(##sys#error "can not create socket") )
;; PLT makes this an optional arg to tcp-listen. Should we as well?
(when (eq? -1 ((foreign-lambda* int ((int socket))
"int yes = 1;
return(setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, (const char *)&yes, sizeof(int)));")
s) )
(##sys#update-errno)
(##sys#signal-hook #:network-error 'tcp-listen (##sys#string-append "error while setting up socket - " strerror) s) )
(let ([addr (make-string _sockaddr_in_size)])
(if host
(unless (##net#gethostaddr addr host port)
(##sys#signal-hook #:network-error 'tcp-listen "getting listener host IP failed - " host port) )
(##net#fresh-addr addr port) )
(let ([b (##net#bind s addr _sockaddr_in_size)])
(when (eq? -1 b)
(##sys#update-errno)
(##sys#signal-hook #:network-error 'tcp-listen (##sys#string-append "can not bind to socket - " strerror) s port) )
(values s addr) ) ) ) )
(define-constant default-backlog 10)
(define (tcp-listen port . more)
(let-optionals more ([w default-backlog] [host #f])
(let-values ([(s addr) (##net#bind-socket port _sock_stream host)])
(##sys#check-exact w)
(let ([l (##net#listen s w)])
(when (eq? -1 l)
(##sys#update-errno)
(##sys#signal-hook #:network-error 'tcp-listen (##sys#string-append "can not listen on socket - " strerror) s port) )
(##sys#make-structure 'tcp-listener s) ) ) ) )
(define (tcp-listener? x)
(and (##core#inline "C_blockp" x)
(##sys#structure? x 'tcp-listener) ) )
(define (tcp-close tcpl)
(##sys#check-structure tcpl 'tcp-listener)
(let ([s (##sys#slot tcpl 1)])
(when (fx= -1 (##net#close s))
(##sys#update-errno)
(##sys#signal-hook #:network-error 'tcp-close (##sys#string-append "can not close TCP socket - " strerror) tcpl) ) ) )
(define-constant buffer-size 1024)
(define ##net#io-ports
(let ([make-input-port make-input-port]
[make-output-port make-output-port]
[make-string make-string]
[substring substring] )
(lambda (fd)
(unless (##net#make-nonblocking fd)
(##sys#update-errno)
(##sys#signal-hook #:network-error (##sys#string-append "can not create TCP ports - " strerror)) )
(let* ([buf (make-string buffer-size)]
[data (vector fd #f #f)]
[buflen 0]
[bufindex 0]
[iclosed #f]
[oclosed #f]
[in
(make-input-port
(lambda ()
(when (fx>= bufindex buflen)
(let ([n (let loop ()
(let ([n (##net#recv fd buf buffer-size 0)])
(if (eq? -1 n)
(if (eq? errno _ewouldblock)
(begin
(##sys#thread-block-for-i/o! ##sys#current-thread fd #t)
(yield)
(loop) )
(begin
(##sys#update-errno)
(##sys#signal-hook
#:network-error
(##sys#string-append "can not read from socket - " strerror)
fd) ) )
n) ) ) ] )
(set! buflen n)
(set! bufindex 0) ) )
(if (fx>= bufindex buflen)
(end-of-file)
(let ([c (##core#inline "C_subchar" buf bufindex)])
(set! bufindex (fx+ bufindex 1))
c) ) )
(lambda ()
(or (fx< bufindex buflen)
(let ([f (##net#select fd)])
(when (eq? f -1)
(##sys#update-errno)
(##sys#signal-hook #:network-error (##sys#string-append "can not check socket for input - " strerror)
fd) )
(eq? f 1) ) ) )
(lambda ()
(unless iclosed
(set! iclosed #t)
(unless (##sys#slot data 1) (##net#shutdown fd _sd_receive))
(when (and oclosed (eq? -1 (##net#close fd)))
(##sys#update-errno)
(##sys#signal-hook #:network-error (##sys#string-append "can not close socket input port - " strerror)
fd) ) ) ) ) ]
[out
(make-output-port
(lambda (s)
(let ([len (##sys#size s)])
(let loop ()
(let ([n (##net#send fd s len 0)])
(cond [(eq? -1 n)
(if (eq? errno _ewouldblock)
(begin
;(##sys#thread-block-for-i/o! ##sys#current-thread fd #f)
(yield)
(loop) )
(begin
(##sys#update-errno)
(##sys#signal-hook
#:network-error (##sys#string-append "can not write to socket - " strerror) fd s) ) ) ]
[(fx< n len)
(set! s (substring s n len))
(set! len (##sys#size s))
(loop) ] ) ) ) ) )
(lambda ()
(unless oclosed
(set! oclosed #t)
(unless (##sys#slot data 2) (##net#shutdown fd _sd_send))
(when (and iclosed (eq? -1 (##net#close fd)))
(##sys#update-errno)
(##sys#signal-hook
#:network-error (##sys#string-append "can not close socket output port - " strerror) fd) ) ) ) ) ] )
(##sys#setslot in 3 "(tcp)")
(##sys#setslot out 3 "(tcp)")
(##sys#setslot in 7 'socket)
(##sys#setslot out 7 'socket)
(##sys#setslot (##sys#port-data in) 0 data)
(##sys#setslot (##sys#port-data out) 0 data)
(values in out) ) ) ) )
(define (tcp-accept tcpl)
(##sys#check-structure tcpl 'tcp-listener)
(let ([fd (##sys#slot tcpl 1)])
(let loop ()
(if (eq? 1 (##net#select fd))
(let ([fd (##net#accept fd #f #f)])
(when (eq? -1 fd)
(##sys#update-errno)
(##sys#signal-hook
#:network-error 'tcp-accept (##sys#string-append "could not accept from listener - " strerror)
tcpl) )
(##net#io-ports fd) )
(begin
(##sys#thread-block-for-i/o! ##sys#current-thread fd #t)
(yield)
(loop) ) ) ) ) )
(define (tcp-accept-ready? tcpl)
(##sys#check-structure tcpl 'tcp-listener 'tcp-accept-ready?)
(let ([f (##net#select (##sys#slot tcpl 1))])
(when (eq? -1 f)
(##sys#update-errno)
(##sys#signal-hook
#:network-error 'tcp-accept-ready? (##sys#string-append "can not check socket for input - " strerror)
tcpl) )
(eq? 1 f) ) )
(define (tcp-connect host . more)
(let ([port (:optional more #f)])
(##sys#check-string host)
(unless port
(set!-values (host port) (##net#parse-host host "tcp"))
(unless port (##sys#signal-hook #:network-error 'tcp-connect "no port specified" host)) )
(##sys#check-exact port)
(let ([addr (make-string _sockaddr_in_size)]
[s (##net#socket _af_inet _sock_stream 0)] )
(define (fail)
(##net#close s)
(##sys#update-errno)
(##sys#signal-hook
#:network-error 'tcp-connect (##sys#string-append "can not connect to socket - " strerror)
host port) )
(when (eq? -1 s)
(##sys#update-errno)
(##sys#signal-hook #:network-error 'tcp-connect (##sys#string-append "can not create socket - " strerror) host port) )
(unless (##net#gethostaddr addr host port)
(##sys#signal-hook #:network-error 'tcp-connect "can not find host address" host) )
(unless (##net#make-nonblocking s)
(##sys#update-errno)
(##sys#signal-hook #:network-error 'tcp-connect (##sys#string-append "fcntl() failed - " strerror)) )
(when (eq? -1 (##net#connect s addr _sockaddr_in_size))
(if (eq? errno _einprogress)
(let loop ()
(let ([f (##net#select-write s)])
(when (eq? f -1) (fail))
(unless (eq? f 1)
;(##sys#thread-block-for-i/o! ##sys#current-thread s #t)
(yield)
(loop) ) ) )
(fail) ) )
(##net#io-ports s) ) ) )
(define (##sys#tcp-port->fileno p)
(##sys#slot (##sys#tcp-port-data p) 0) )
(define (##sys#tcp-port-data p)
(##sys#check-port p)
(let ([d (##sys#port-data p)])
(if d
(##sys#slot d 0)
(##sys#signal-hook #:type-error "bad argument type - not a TCP port - " p) ) ) )
(define (tcp-addresses p)
(let ([fd (##sys#tcp-port->fileno p)])
(values
(or (##net#getsockname fd)
(##sys#signal-hook #:network-error 'tcp-addresses (##sys#string-append "can not compute local address - " strerror) p) )
(or (##net#getpeername fd)
(##sys#signal-hook #:network-error 'tcp-addresses (##sys#string-append "can not compute remote address - " strerror) p) ) ) ) )
(define (tcp-listener-port tcpl)
(##sys#check-structure tcpl 'tcp-listener 'tcp-listener-port)
(let* ([fd (##sys#slot tcpl 1)]
[port (##net#getsockport fd)] )
(when (eq? -1 port)
(##sys#signal-hook
#:network-error 'tcp-listener-port (##sys#string-append "can not obtain listener port - " strerror)
tcpl fd) )
port) )
(define (tcp-abandon-port p)
(##sys#setislot
(##sys#tcp-port-data p)
(if (##sys#slot p 1) 2 1)
#t) )
(define (tcp-listener-fileno l)
(##sys#check-structure l 'tcp-listener 'tcp-listener-fileno)
(##sys#slot l 1) )
_______________________________________________
Chicken-users mailing list
[email protected]
http://lists.nongnu.org/mailman/listinfo/chicken-users