Adding send and receive support, with an example of how to use them with udp.

This commit is contained in:
Alex Shinn 2014-08-22 22:41:42 +09:00
parent d1eeea1a66
commit c8f61c8893
5 changed files with 108 additions and 2 deletions

22
examples/echo-server-udp.scm Executable file
View file

@ -0,0 +1,22 @@
#!/usr/bin/env chibi-scheme
(import (scheme base) (chibi net))
(define (get-udp-address-info host service)
(let ((hints (make-address-info address-family/inet
socket-type/datagram
ip-proto/udp)))
(get-address-info host service hints)))
;; create and bind a udp socket
(let* ((addr (get-udp-address-info #f 5556))
(sock (socket (address-info-family addr)
(address-info-socket-type addr)
(address-info-protocol addr))))
(bind sock (address-info-address addr) (address-info-address-length addr))
;; for every packet we receive, just send it back
(let lp ()
(cond
((receive sock 512 0 addr)
=> (lambda (bv) (send sock bv 0 addr))))
(lp)))

View file

@ -24,6 +24,44 @@ sexp sexp_accept (sexp ctx, sexp self, int sock, struct sockaddr* addr, socklen_
return sexp_make_fileno(ctx, sexp_make_fixnum(res), SEXP_FALSE);
}
/* likewise sendto and recvfrom should suspend the thread gracefully */
sexp sexp_sendto (sexp ctx, sexp self, int sock, const void* buffer, size_t len, int flags, struct sockaddr* addr, socklen_t addr_len) {
#if SEXP_USE_GREEN_THREADS
sexp f;
#endif
ssize_t res;
res = sendto(sock, buffer, len, flags, addr, addr_len);
#if SEXP_USE_GREEN_THREADS
if (res < 0 && errno == EWOULDBLOCK) {
f = sexp_global(ctx, SEXP_G_THREADS_BLOCKER);
if (sexp_opcodep(f)) {
((sexp_proc2)sexp_opcode_func(f))(ctx, f, 1, sexp_make_fixnum(sock));
return sexp_global(ctx, SEXP_G_IO_BLOCK_ERROR);
}
}
#endif
return sexp_make_fixnum(res);
}
sexp sexp_recvfrom (sexp ctx, sexp self, int sock, void* buffer, size_t len, int flags, struct sockaddr* addr, socklen_t addr_len) {
#if SEXP_USE_GREEN_THREADS
sexp f;
#endif
ssize_t res;
res = recvfrom(sock, buffer, len, flags, addr, &addr_len);
#if SEXP_USE_GREEN_THREADS
if (res < 0 && errno == EWOULDBLOCK) {
f = sexp_global(ctx, SEXP_G_THREADS_BLOCKER);
if (sexp_opcodep(f)) {
((sexp_proc2)sexp_opcode_func(f))(ctx, f, 1, sexp_make_fixnum(sock));
return sexp_global(ctx, SEXP_G_IO_BLOCK_ERROR);
}
}
#endif
return sexp_make_fixnum(res);
}
/* If we're listening on a socket from Scheme, we most likely want it */
/* to be non-blocking. */

View file

@ -1,12 +1,12 @@
;; Copyright (c) 2010-2012 Alex Shinn. All rights reserved.
;; BSD-style license: http://synthcode.com/license.txt
;;> \subsubsubsection{\scheme{(make-address-info family socktype proto [hints])}}
;;> \procedure{(make-address-info family socktype proto [hints])}
(define (make-address-info family socktype proto . o)
(%make-address-info family socktype proto (if (pair? o) (car o) 0)))
;;> \subsubsubsection{\scheme{(get-address-info host service [addrinfo])}}
;;> \procedure{(get-address-info host service [addrinfo])}
;;> Create and return a new addrinfo structure for the given host
;;> and service. \var{host} should be a string and \var{service} a
@ -106,3 +106,38 @@
(define (get-socket-option socket level name)
(let ((res (getsockopt socket level name)))
(and (pair? res) (car res))))
;;> Sends the bytevector \var{bv} to \var{socket} with sendto and
;;> returns the number of bytes sent, or a negative value on error.
;;> If \var{addrinfo} is unspecified, \var{socket} must previously
;;> have had a default address specified with \scheme{connect}.
(define (send socket bv . o)
(let* ((flags (if (pair? o) (car o) 0))
(addrinfo (and (pair? o) (pair? (cdr o)) (cadr o)))
(sockaddr (and addrinfo (address-info-address addrinfo)))
(sockaddr-len (if addrinfo (address-info-address-length addrinfo) 0)))
(%send socket bv flags sockaddr sockaddr-len)))
;;> Recieves data from \var{socket} to fill the bytevector \var{bv} by
;;> calling recvfrom. Returns the number of bytes read, or a negative
;;> value on error. If \var{addrinfo} is unspecified, \var{socket}
;;> must previously have had a default address specified with
;;> \scheme{connect}.
(define (receive! socket bv . o)
(let* ((flags (if (pair? o) (car o) 0))
(addrinfo (and (pair? o) (pair? (cdr o)) (cadr o)))
(sockaddr (and addrinfo (address-info-address addrinfo)))
(sockaddr-len (if addrinfo (address-info-address-length addrinfo) 0)))
(%receive! socket bv flags sockaddr sockaddr-len)))
;;> Shortcut for \scheme{receive}, returning a newly created
;;> bytevector of length \var{n} on success and \scheme{#f} on
;;> failure.
(define (receive socket n . o)
(let* ((bv (make-bytevector n))
(m (apply receive! socket bv o)))
(and (>= m 0)
(subbytes bv 0 m))))

View file

@ -4,6 +4,7 @@
socket connect bind accept listen open-socket-pair
sockaddr-name sockaddr-port make-sockaddr
with-net-io open-net-io make-listener-socket
send receive! receive
address-info-family address-info-socket-type address-info-protocol
address-info-address address-info-address-length address-info-next
address-family/unix address-family/inet address-family/unspecified

View file

@ -51,6 +51,16 @@
(define-c int connect (fileno sockaddr int))
(define-c sexp (%send "sexp_sendto")
((value ctx sexp) (value self sexp)
fileno bytevector (value (bytevector-length arg3) size_t) int
(maybe-null sockaddr) socklen_t))
(define-c sexp (%receive! "sexp_recvfrom")
((value ctx sexp) (value self sexp)
fileno bytevector (value (bytevector-length arg3) size_t) int
(maybe-null sockaddr) socklen_t))
;;> Returns a list of 2 new sockets, the input and output end of a new
;;> pipe, respectively.