Module: sockets-internals Author: Toby Synopsis: TCP sockets Copyright: Original Code is Copyright (c) 1995-2004 Functional Objects, Inc. All rights reserved. License: Functional Objects Library Public License Version 1.0 Dual-license: GNU Lesser General Public License Warranty: Distributed WITHOUT WARRANTY OF ANY KIND /// CLIENTSIDE define abstract primary class () end class; define abstract primary class () end class; define method initialize (stream :: , #rest initargs, #key service :: false-or(type-union(, )), host: requested-host :: false-or(type-union(, )) = #f, port: requested-port :: false-or() = #f, descriptor :: false-or() = #f, buffer-size: requested-buffer-size :: false-or() = #f, direction: requested-direction = #"input-output") => () apply(next-method, stream, direction: requested-direction, initargs); // Descriptor is really only for internal use, in order to create // sockets from socket descriptors returned by accept. unless(descriptor | service | requested-port) error("either port: or service: keyword to make is required"); end unless; // port/service doesn't work let the-remote-port = if (requested-port) requested-port // ignore service if also specified elseif (service) accessor-get-port-for-service(as(, service), "tcp") else #f end if; // host let remote-host = select (requested-host by instance?) => requested-host; => make(, name: requested-host); => unless (descriptor) error("host: keyword to make is required"); end unless; end select; unless (stream.accessor) stream.accessor := apply(new-accessor, type-for-socket(stream), remote-host: remote-host, remote-port: the-remote-port, descriptor: descriptor, initargs); end unless; // Initializing the buffers has to be done here since we don't know // the accessor-preferred-buffer-size until we have an accessor. // Actually better might be to have this code in the initialize // method for and merely mess with the // buffer-size keyword but then the next method call has to be // elsewhere. Might not work? Investigate. let direction = stream.stream-direction; let size-for-buffers :: = if (requested-buffer-size) requested-buffer-size else accessor-preferred-buffer-size(stream.accessor) end if; if ((direction == #"input") | (direction == #"input-output")) stream-input-buffer(stream) := make(, size: size-for-buffers) end; if ((direction == #"output") | (direction == #"input-output")) stream-output-buffer(stream) := make(, size: size-for-buffers) end end method initialize; define method type-for-socket (socket :: ) => (type == #"TCP") #"TCP" end method; define method make (class == , #rest initargs, #key element-type = , direction: requested-direction = #"input-output") => (stream :: ) apply(make, client-class-for-element-type(class, element-type), direction: requested-direction, initargs) end method make; define generic client-class-for-element-type (class :: subclass(), element-type :: ) => (class :: subclass()); define method client-class-for-element-type (class == , element-type == ) => (class == ) end method; define method client-class-for-element-type (class == , element-type == ) => (class == ) end method; define method client-class-for-element-type (class == , element-type :: ) => (class == ) end method; define class (, , ) inherited slot stream-element-type = ; end class ; define class (, , ) inherited slot stream-element-type = ; end class ; define class (, , ) inherited slot stream-element-type = ; end class ; define method client-class-for-protocol (protocol == #"TCP") => (class == ) end method; /// SERVERSIDE define primary class (, ) slot default-element-type :: , init-keyword: element-type:, init-value: ; end class; define primary class () end class; define method server-class-for-protocol (protocol == #"TCP") => (class == ) end method; define inline method socket-code (socket :: ) $SOCK-STREAM end method; define method initialize (new-server-socket :: , #rest initargs, #key service :: false-or(type-union(, )), host: requested-host :: false-or(type-union(, )) = #f, port: requested-port :: false-or() = #f) => () next-method(); new-server-socket.socket-descriptor := accessor-new-socket-descriptor(socket-code(new-server-socket)); if (service) error("service keyword to make not supported yet"); end if; // host let host-to-bind = select (requested-host by instance?) => requested-host; => make(, name: requested-host); => #"wildcard"; end select; // port let port-to-bind = if (requested-port) requested-port elseif (service) accessor-get-port-for-service(as(, service), "tcp") else #"wildcard" end if; // // LispWorks sockets sets these options before calling bind. Investigate // what this is about later. // Y 4Jun94 put this into work, solve some problems. // if (*use_so_reuseaddr*) // if (0 > setsockopt(fd, *sockopt_sol_socket*, *sockopt_so_reuseaddr*, // pointer-cast(, on), size-of-on)) // close-socket(fd); // return-from-create-tcp-socket-for-service(#f, -3); // end if; // end if; // if (*sockopt_so_dontlinger*) // if (0 > setsockopt(fd, *sockopt_sol_socket*, // *sockopt_so_dontlinger*, // pointer-cast(, on), size-of-on)) // close-socket(fd); // return-from-create-tcp-socket-for-service(#f, -4); // end if; // else // with-stack-structure(ls :: ) // ls.l-onoff-value := 0; // if (0 > setsockopt(fd, *sockopt_sol_socket*, *sockopt_so_linger*, // pointer-cast(, ls), size-of())) // close-socket(fd); // return-from-create-tcp-socket-for-service(#f, -5); // end if; // end with-stack-structure; // end if; // bind accessor-bind(new-server-socket, host-to-bind, port-to-bind); let (bound-host :: false-or(), bound-port :: false-or()) = if ((host-to-bind == #"wildcard") | (port-to-bind == #"wildcard")) accessor-local-address-and-port(new-server-socket.socket-descriptor); end if; new-server-socket.local-host := if (host-to-bind == #"wildcard") bound-host else host-to-bind end; new-server-socket.local-port := if (port-to-bind == #"wildcard") bound-port else port-to-bind end; // if (*sockopt_tcp_nodelay*) // with-stack-structure(mi :: ) // setsockopt(fd, *sockopt_ipproto_tcp*, *sockopt_tcp_nodelay*, // pointer-cast(, mi), size-of()); // end with-stack-structure; // end if; // listen, sometime propagate backlog keyword to initialize method accessor-listen(new-server-socket); // make the socket non-blocking, I think this may be unnecessary // since the event-select call in wait-for-socket-io is a stronger // way to make it non-blocking. // with-stack-structure(on :: ) // pointer-value(on) := 1; // ioctlsocket(fd, *ioctlsocket-fionbio*, on); // end with-stack-structure; end method; define method client-class-for-server (server-socket :: ) => (class == ) end method;