/
socket.svnwiki
978 lines (757 loc) · 43 KB
/
socket.svnwiki
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
[[tags:egg net]]
== socket
'''socket''' provides an interface to the BSD socket API. For a somewhat higher-level interface, see the [[/egg/tcp6|tcp6]] and [[/egg/udp6|udp6]] extensions.
=== Overview
This extension provides a comprehensive interface to BSD sockets,
including socket creation, client and server setup, data transfer, i/o
ports, forward and reverse address resolution, socket options, and
socket-related integer constants. It supports both IPv4 and IPv6,
as well as UNIX sockets.
All socket operations block only the calling thread; other threads can
continue to run, even on Windows platforms.
[[toc:]]
=== Socket interface
==== Socket creation
<record>socket</record><br>
<procedure>(socket family type #!optional (protocol 0))</procedure><br>
<procedure>(socket? so)</procedure><br>
<procedure>(socket-fileno so)</procedure><br>
<procedure>(socket-family so)</procedure><br>
<procedure>(socket-type so)</procedure><br>
<procedure>(socket-protocol so)</procedure><br>
Socket objects. You construct a socket using the {{socket}} procedure,
passing an address family {{af/*}} constant for {{family}} (IPv4, IPv6) and a
socket type {{sock/*}} constant for socket {{type}} (TCP, UDP). {{protocol}}
should almost always be zero, unless you are creating raw sockets; it is
implicit in the socket type. Sockets take up a file descriptor in the
system until closed, which may or may not happen automatically on error.
All sockets are created in non-blocking mode.
Accessors:
; {{socket-fileno}} : The socket's file descriptor.
; {{socket-family}} : The socket family, an integer constant.
; {{socket-type}} : The socket type, an integer constant.
; {{socket-protocol}} : The socket protocol, an integer constant.
Note that sockets are also implicitly created by {{socket-connect/ai}}
and {{socket-accept}}.
Example:
(socket af/inet sock/stream)
; => #<socket fd:19 af/inet sock/stream>
(socket af/inet6 sock/dgram)
; => #<socket fd:20 af/inet6 sock/dgram>
<constant>af/inet</constant><br>
<constant>af/inet6</constant><br>
<constant>af/unix</constant><br>
<constant>af/unspec</constant><br>
<procedure>(integer->address-family int)</procedure>
<procedure>(address-family->integer sym)</procedure>
Address family constants for socket creation. It is possible to
convert between integer constants and symbols using the provided
procedures, but this is just for debugging convenience; the API
requires integer constants.
<constant>sock/stream</constant><br>
<constant>sock/dgram</constant><br>
<constant>sock/raw</constant><br>
<procedure>(integer->socket-type int)</procedure>
<procedure>(socket-type->integer sym)</procedure>
Socket type constants for socket creation.
<constant>ipproto/tcp</constant><br>
<constant>ipproto/udp</constant><br>
<procedure>(integer->protocol-type int)</procedure>
<procedure>(protocol-type->integer sym)</procedure>
Protocol constants for socket creation.
==== Socket addresses
<record>sockaddr</record><br>
<procedure>(sockaddr? sa)</procedure><br>
<procedure>(sockaddr-family sa)</procedure><br>
<procedure>(sockaddr-address sa)</procedure><br>
<procedure>(sockaddr-port sa)</procedure><br>
<procedure>(sockaddr-path sa)</procedure><br>
<procedure>(sockaddr->string sa)</procedure><br>
The socket address object. {{sockaddr}} is used throughout
the BSD sockets API to represent the address of a local or remote
socket endpoint.
The most convenient constructor for Internet socket addresses is
{{inet-address}}. The fundamental Internet socket address constructor
is {{address-information}}, which is more powerful but also more
complex to use. For UNIX sockets, use {{unix-address}}.
Socket address object accessors are:
; {{sockaddr-family}} : returns the socket address family as an integer constant, e.g. {{af/inet}}.
; {{sockaddr-address}} : returns the address of a socket as a string (for Internet sockets, this is the IP address).
; {{sockaddr-port}} : returns the socket port for Internet sockets; it is an error to call it on another type of socket.
; {{sockaddr-path}} : returns the pathname for UNIX sockets, or an error for other socket types.
; {{sockaddr->string}} : returns a compact representation of the socket address as a string. For Internet sockets, it returns "address" when port is 0; otherwise, it returns "address:port" for IPv4 addresses and "[address]:port" for IPv6 addresses.
<procedure>(inet-address addr port)</procedure>
Returns a {{sockaddr}} object constructed from IP address {{addr}} (a
string) and port {{port}} (a number or numeric string). If the address
or port input is invalid, an error is raised.
If {{addr}} is {{#f}}, the unspecified address is used ("::" or "0.0.0.0").
If {{port}} is {{#f}}, the unspecified port is used (integer 0).
It is an error for both {{addr}} and {{port}} to be unspecified.
Note that when IPv6 is preferred on your system, the unspecified
address {{#f}} is typically "::" and the resulting object will be of
family {{af/inet6}}. This may not be what you want, so it is a good
idea to specify which unspecified address (yes, really) you mean --
"::" or "0.0.0.0" -- in lieu of {{#f}}.
<procedure>(unix-address path)</procedure>
Returns a {{sockaddr}} object constructed from the pathname {{path}},
suitable for use with a socket in address family {{af/unix}}.
Throws an error if UNIX sockets are not supported on your platform.
==== Address resolution
<procedure>(address-information node service #!key family (type sock/stream) protocol flags)</procedure><br>
<constant>ai/numerichost</constant><br>
<constant>ai/passive</constant><br>
<constant>ai/canonname</constant><br>
Looks up node name and service name and translates them to numeric
values. Returns a list of {{addrinfo}} objects, each of which contains
a socket address ({{sockaddr}} object) suitable for use in socket calls
such as {{socket-bind}} and {{socket-connect}}.
{{node}} is either a node name (string) or IP address (string).
{{service}} may be a string representing a service name or port number, or
an integer. If {{node}} is {{#f}}, it is treated as the loopback address;
however, if {{ai/passive}} is set it is treated as the unspecified
address. If {{service}} is {{#f}}, it is treated as unspecified (0).
Keyword arguments accept numeric constants and restrict the returned addresses accordingly:
; {{family}} : Address family, either {{af/inet}} or {{af/inet6}}, defaulting to {{#f}}. If {{#f}}, both IPv6 and IPv4 addresses may be returned, depending on your system's configuration and IP stack.
; {{type}} : Socket type; usually {{sock/stream}} or {{sock/dgram}}, defaulting to {{sock/stream}}. Can be {{#f}}, but results may vary between systems, so it is safer to specify one. See examples.
; {{protocol}} : Protocol type, usually {{#f}}. Can also be {{ipproto/tcp}} or {{ipproto/udp}}; however, some systems (such as Windows) do not construct a proper socket address when {{type}} is unspecified, so it is safer to just provide a value for {{type}} and leave this as {{#f}}.
The behavior of {{address-information}} can be influenced by the value
of {{flags}}, which should be the {{bitwise-ior}} (or simply {{+}}) of any
of the following constants:
; {{ai/numerichost}} : The host is an IP address string; do not attempt to resolve it.
; {{ai/passive}} : The socket address is intended to be used in a call to bind(). The only difference is that an address of {{#f}} is translated into the unspecified address "::" or "0.0.0.0", rather than the loopback address.
; {{ai/canonname}} : Include the canonical (usually FQDN) hostname in the {{addrinfo}} object. If not provided, that field will be {{#f}}.
Examples:
(address-information "localhost" "http")
; => (#<addrinfo "[::1]:80" af/inet6 sock/stream ipproto/tcp>
#<addrinfo "[fe80::1%lo0]:80" af/inet6 sock/stream ipproto/tcp>
#<addrinfo "127.0.0.1:80" af/inet sock/stream ipproto/tcp>)
(address-information "127.0.0.1" 53 type: sock/dgram)
; => (#<addrinfo "127.0.0.1:53" af/inet sock/stream ipproto/udp>)
(address-information "he.net" 80)
; => (#<addrinfo "[2001:470:0:76::2]:80" af/inet6 sock/stream ipproto/tcp>
#<addrinfo "216.218.186.2:80" af/inet sock/stream ipproto/tcp>)
(address-information "he.net" 80 type: #f)
; Possible response on UNIX -- return both TCP and UDP addresses.
; Might also just return TCP.
; => (#<addrinfo "[2001:470:0:76::2]:80" af/inet6 sock/dgram ipproto/udp>
#<addrinfo "[2001:470:0:76::2]:80" af/inet6 sock/stream ipproto/tcp>
#<addrinfo "216.218.186.2:80" af/inet sock/dgram ipproto/udp>
#<addrinfo "216.218.186.2:80" af/inet sock/stream ipproto/tcp>)
; Possible response on Windows -- socket addresses are not valid for use
; => (#<addrinfo "[2001:470:0:76::2]:80" af/inet6 0 0>
#<addrinfo "216.218.186.2:80" af/inet 0 0>)
(address-information #f "http")
; => (#<addrinfo "[::1]:80" af/inet6 sock/stream ipproto/tcp>
#<addrinfo "127.0.0.1:80" af/inet sock/stream ipproto/tcp>)
(address-information #f "http" flags: ai/passive)
; => (#<addrinfo "[::]:80" af/inet6 sock/stream ipproto/tcp>
#<addrinfo "0.0.0.0:80" af/inet sock/stream ipproto/tcp>)
; As an example of inconsistent per-platform behavior, note that
; recent Ubuntu among others returns the above in reverse order.
(address-information "allie" 0 flags: ai/canonname)
; => (#<addrinfo "192.168.1.7" af/inet sock/stream ipproto/tcp
canonical: "allie.xorinia.dim">)
(address-information #f #f)
; => ()
<record>addrinfo</record>
<procedure>(addrinfo? ai)</procedure>
<procedure>(addrinfo-family ai)</procedure>
<procedure>(addrinfo-socktype ai)</procedure>
<procedure>(addrinfo-protocol ai)</procedure>
<procedure>(addrinfo-address ai)</procedure>
<procedure>(addrinfo-canonname ai)</procedure>
<procedure>(addrinfo-flags ai)</procedure>
Address information record returned by {{address-information}}.
* {{addrinfo-address}} is the {{sockaddr}} socket address object;
* {{addrinfo-family}}, {{addrinfo-socktype}} and {{addrinfo-protocol}} are numeric constants in the {{af/}}, {{sock/}} and {{ipproto/}} families respectively;
* {{addrinfo-canonname}} is the canonical (FQDN) name of this host and is present only if the {{ai/canonname}} flag was used; otherwise {{#f}};
* {{addrinfo-flags}} is the bitwise OR of {{ai/}} flags used when constructing this object. The system may set certain flags itself so this is probably not reliably useful.
<procedure>(name-information saddr #!optional (flags 0))</procedure><br>
<constant>ni/numerichost</constant><br>
<constant>ni/numericserv</constant><br>
<constant>ni/dgram</constant><br>
<constant>ni/namereqd</constant><br>
<constant>ni/nofqdn</constant><br>
Given a socket address object {{saddr}}, performs a reverse-lookup to
obtain the node and service names, returning them as the pair
{{("node" . "service")}}. If hostname lookup fails, the numeric
representation of the address is returned as a string. If service
number lookup fails, it is returned as an integer.
The socket address object is usually constructed with {{inet-address}}
or obtained from a socket call, e.g. {{socket-peer-name}}. As a
convenience in socket 0.2.1 and later, if {{saddr}} is a string, it is
converted to a socket address object with {{(inet-address saddr #f)}}.
The behavior of {{name-information}} can be influenced by FLAGS. FLAGS
may be the {{bitwise-ior}} (or simply {{+}}) of the following
constants:
; {{ni/numerichost}} : Do not resolve the node address to a name; instead, return the canonical string representation of the address, as in {{inet_ntop()}}. {{(sockaddr-address saddr)}} returns the same representation.
; {{ni/numericserv}} : Do not attempt to resolve the service number to a name.
; {{ni/namereqd}} : If hostname lookup fails, raise an error.
; {{ni/nofqdn}} : Return only the local part of the hostname for local hosts.
; {{ni/dgram}} : Look up the service as a datagram service. A few service names may differ between TCP and UDP.
Examples:
(name-information (inet-address "127.0.0.1" 80))
; => ("localhost" . "http")
(name-information (inet-address "::1" 80))
; => ("localhost" . "http")
(name-information (inet-address "::1" #f))
; => ("localhost" . 0)
(name-information "::1")
; => ("localhost" . 0)
(name-information (inet-address "::1" 80) ni/numerichost)
; => ("::1" . "http")
(name-information (inet-address "::1" 80) (+ ni/numerichost ni/numericserv))
; => ("::1" . 80)
(name-information (inet-address "127.0.0.2" 80))
; => ("127.0.0.2" . "http")
(name-information (inet-address "127.0.0.2" 80) ni/namereqd)
; => error: nodename nor servname provided, or not known
(name-information (inet-address "2001:470:0:64::2" 80) ni/numericserv)
; => ("ipv6.he.net" . 80)
(name-information (socket-peer-name s)) ;; s being an accept()ed socket
; => ("taco.universe12.dim" . 31828)
==== Setup and teardown
<procedure>(socket-connect so saddr)</procedure><br>
<procedure>(socket-connect/ai ais)</procedure><br>
{{socket-connect}} connects to the remote socket address {{saddr}} over
the socket {{so}}. Upon completion, {{so}} will be connected; on connection
failure an error is thrown. The return value is unspecified. This is
a non-blocking operation; other SRFI-18 threads can continue to run.
If connection fails due to refusal, network down, unreachable host or
system enforced timeout, it raises a "transient" error of type {{(exn
i/o net transient)}}, signalling the connection can be retried later
if desired. A connection may also raise an {{(exn i/o net timeout)}}
error after {{(socket-connect-timeout)}} milliseconds. If a fatal error
occurs, it raises an error of type {{(exn i/o net)}}, like all other
socket procedures.
{{socket-connect/ai}} connects to the addresses in the {{addrinfo}}
list {{ais}} sequentially until the connection succeeds or there are no
more addresses. If a fatal error occurs while connecting, it aborts
immediately; but transient or timeout errors cause it to try the next
address. If all attempts fail, the error raised is that of the last
attempt. On success, the return value is a fresh connected socket of
the appropriate family and type.
Examples:
;; Connect to localhost:22 over IPv4.
(define so (socket af/inet sock/stream))
(socket-connect so (inet-address "127.0.0.1" 22))
;; Connect to localhost:22 over IPv6.
(define so (socket af/inet6 sock/stream))
(socket-connect so (inet-address "::1" 22))
;; Connect to localhost:22 over IPv4 and return the connected socket.
(socket-connect/ai
(address-information "localhost" 22 family: af/inet))
; => #<socket fd:8 af/inet sock/stream>
;; Try to connect to localhost:ssh via any address family.
;; In this case, address-information may return the IPv6 loopback
;; address "::1" and perhaps "fe80::1", along with the usual
;; IPv4 "127.0.0.1". socket-connect/ai will try them all in order
;; and return a new connected socket. For illustrative purposes
;; we use socket-peer-name to show where we connected.
(socket-peer-name
(socket-connect/ai (address-information "localhost" "ssh")))
; => #<sockaddr "[::1]:22"> ;; If ssh listening on ::1
; => #<sockaddr "[fe80::1%lo0]:22"> ;; If listening on link-local loopback
; => #<sockaddr "127.0.0.1:22"> ;; If listening on 127.0.0.1 only
; => error: connection refused ;; If ssh isn't running
<procedure>(socket-bind so saddr)</procedure>
Binds socket {{so}} to socket address {{saddr}}. The return value is unspecified.
; Bind to the IPv4 unspecified address on port 8000.
(define so (socket af/inet sock/stream))
(socket-bind so (inet-address "0.0.0.0" 8000))
; Bind to the IPv6 unspecified address on port 8000. This may also
; allow IPv4 connections, depending on your system settings.
(define so (socket af/inet6 sock/stream))
(socket-bind so (inet-address "::" 8000))
; Bind to the IPv6 unspecified address on port 8000, limiting
; connections to IPv6 only.
(define so (socket af/inet6 sock/stream))
(set! (ipv6-v6-only? so) #t)
(socket-bind so (inet-address "::" 8000))
<procedure>(socket-listen so backlog)</procedure>
Listen for incoming connections on socket {{so}}, with a connection queue
of integer length {{backlog}}. This call is only valid for
connection-oriented (stream) sockets.
<procedure>(socket-accept so)</procedure>
<procedure>(socket-accept-ready? so)</procedure>
{{socket-accept}} accepts a connection on listening socket {{so}},
returning a new connected {{socket}} object. The address of the peer
can be obtained by calling {{socket-peer-name}} on the socket.
This is a non-blocking operation; other SRFI-18 threads can continue
to run in the meantime, although this one will block. If the accept
does not complete within {{socket-accept-timeout}} milliseconds, a
timeout error is raised.
{{socket-accept-ready?}} tests whether there is a connection waiting
to be accepted, so you can avoid blocking the current thread.
However, if a peer resets his connection between your testing and
accepting, the accept may block nonetheless.
<procedure>(socket-shutdown so how)</procedure>
<constant>shut/rd</constant>
<constant>shut/wr</constant>
<constant>shut/rdwr</constant>
Shutdown the full-duplex connection on socket {{so}} in the manner of {{how}}:
; {{shut/rd}} : disallow further receiving
; {{shut/wr}} : disallow further sending
; {{shut/rdwr}} : disallow further receiving and sending
The system normally shuts down the connection itself when closing,
so calling this manually is not often necessary. One usage example
is using {{shut/wr}} to inform a server you have finished transmitting,
while allowing it to continue sending to you.
<procedure>(socket-close so)</procedure>
Close socket {{so}}. If a connection was established,
the socket is shut down gracefully.
{{socket-close}} throws an error if the {{close()}} fails.
<procedure>(socket-close* so)</procedure>
Same as {{socket-close}}, except that {{socket-close*}} does ''not''
throw an error if the {{close()}} fails.
This could be useful in certain lowlevel code, such as after a network
error, but you should not use this unless you know what you're doing.
This might go away or change semantics in the future.
==== Status
<procedure>(socket-name so)</procedure>
Return a {{sockaddr}} object representing the address of the local
endpoint of socket {{so}}. If the socket has not been bound, it
returns {{#f}}.
The procedure name is derived from getsockname(), hence the use of "name"
to describe a socket address.
<procedure>(socket-peer-name so)</procedure>
Return a {{sockaddr}} object representing the address of the remote
endpoint of socket {{so}}. If no connection exists, returns {{#f}}. This
can be used after {{socket-connect}} or on a socket returned by
{{socket-accept}}. Note that this also works with UDP "connections"
made with {{socket-connect}}.
The procedure name is derived from getpeername(), hence the use of "name"
to describe a socket address.
==== Data transfer
===== Receiving data
<procedure>(socket-receive! so buf #!optional (start 0) (end #f) (flags 0))</procedure>
<procedure>(socket-receive-ready? so)</procedure>
Receives data from socket {{so}} and writes it into {{buf}}, which may be a
string or a blob. {{start}} and {{end}} are optional offsets into {{buf}}; the
call attempts to read {{end}} - {{start}} = {{len}} bytes. If {{end}} is {{#f}}, it
is interpreted as the end of the blob or string.
This call will block until data is available, but other threads can
proceed. If the receive does not complete within
{{socket-receive-timeout}} milliseconds, a timeout error is raised.
To avoid blocking the current thread, you can check if data is ready
via {{socket-receive-ready?}}.
Returns the number of bytes actually received (and updates {{buf}} as
a side effect).
For datagram sockets, if {{len}} is smaller than the amount of data in
the next datagram, the rest of the data is irrevocably lost.
<procedure>(socket-receive so len #!optional (flags 0))</procedure>
Receives up to {{len}} bytes from data from socket {{so}} and returns it
in a string (sized to fit the returned data). Otherwise, it behaves
like {{socket-receive!}}.
<procedure>(socket-receive-from! so buf #!optional (start 0) (end #f) (flags 0))</procedure>
Like {{socket-receive!}}, but receives data on a connectionless
datagram socket, returning 2 values: the number of bytes read, and a
{{sockaddr}} object representing the source.
<procedure>(socket-receive-from so len #!optional (flags 0))</procedure>
Receives up to {{len}} bytes from data from socket {{so}} and returns two
values: a string (sized to fit the returned data), and a {{sockaddr}}
object representing the source. Otherwise, it behaves like
{{socket-receive-from!}}.
===== Sending data
<procedure>(socket-send so buf #!optional (start 0) (end #f) (flags 0))</procedure>
Sends data to socket {{so}} from the buffer {{buf}}, which may be a
string or a blob. {{start}} and {{end}} are optional offsets into {{buf}}; the
call attempts to write {{end}} - {{start}} = {{len}} bytes. If {{end}} is {{#f}}, it
is interpreted as the end of the blob or string.
This call will block until at least some data is sent, but other
threads can proceed. If the send does not complete within
{{(socket-send-timeout)}} milliseconds, a timeout error is raised.
Returns the number of bytes actually sent.
<procedure>(socket-send-to so buf saddr #!optional (start 0) (end #f) (flags 0))</procedure>
Like {{socket-send}}, but sends data over a connectionless datagram
socket to {{sockaddr}} {{saddr}}, returning the number of bytes actually
sent.
<procedure>(socket-send-all so buf #!optional (start 0) (end #f) (flags 0))</procedure>
Sends all data between {{start}} and {{end}} in {{buf}} over connected socket {{so}}
by calling {{socket-send}} multiple times until all data is sent.
Data is sent in chunks of size {{(socket-send-size)}}; the last chunk
sent may be smaller than this. A {{#f}} value for
{{socket-send-size}} will attempt to send all remaining data with each
call to send(). Note that this chunking works for connected datagram
sockets as well as stream sockets; you can use it to send a large
buffer divided into, say, 512-byte datagrams.
==== I/O ports
<procedure>(socket-i/o-ports so)</procedure>
Constructs an input port I and an output port O associated with
the connected socket {{so}}, returning {{(values I O)}}. This procedure
works on both stream and datagram sockets.
To enable output buffering on stream socket ports, see the parameter
{{socket-send-buffer-size}}. Setting it to a value of 1024 bytes is
reasonable.
Below is a fairly involved explanation of input and output buffering
and chunking, as well as recommendations for use with datagrams.
{{socket-i/o-ports}} is normally used with stream sockets. Input data
is always buffered in a buffer of size {{(socket-receive-buffer-size)}}.
Whenever the buffer is empty, {{socket-receive!}} is called once to read
up to that many bytes. Output data is sent with {{socket-send-all}},
so it may be divided into chunks of size {{(socket-send-size)}}. If
output is unbuffered, {{socket-send-all}} is called as soon as data
is written to the port.
If output is buffered by setting {{(socket-send-buffer-size)}} to N,
then N characters are buffered before sending the data. Note that
only multiples of the buffer size are sent (any overage is kept in the
buffer). For example, if the buffer can hold 512 bytes and contains
500 bytes, writing 526 more bytes brings the total unsent size to 1026
bytes. 1024 bytes (2 blocks) are written out in a single call to
{{socket-send-all}} and the last 2 bytes are retained in the buffer.
When the output buffer size and chunk size are both set, it is
recommended to make the chunk size a multiple of the buffer size; for
example, buffer size = 1024, chunk size = 8192. If not aligned,
extraneous small packets may be sent. Buffer size is almost always
less than or equal to chunk size. If greater, it should be a multiple
of the chunk size. Using powers of 2 for both satisfies all cases.
Note that {{socket-i/o-ports}} can also be used to create ports on
connected datagram sockets. Input is always buffered and a single
chunk of up to size {{(socket-receive-buffer-size)}} is read into the
buffer whenever the buffer is empty. (If the datagram is smaller than
the buffer, repeated reads are not performed; rather, the buffer is
used until exhausted again. Any datagram exceeding the buffer size
will be truncated.) Output is divided into chunks of size
{{(socket-send-size)}}, as in {{socket-send-all}} -- this is useful
for placing a maximum cap on datagram size transmitted. Finally,
output buffering may be enabled, which behaves the same as with TCP
ports; characters are buffered and sent in blocks of
{{(socket-send-buffer-size)}} bytes. Again, to avoid excessive
transmission, the chunk size should be a multiple of the buffer
size or vice versa.
For example, to accept up to 4K datagrams, buffer 128 characters
at a time and send 128-, 256-, 384- or 512-byte datagrams at a time:
(parameterize ((socket-receive-buffer-size 4096)
(socket-send-buffer-size 128)
(socket-send-size 512))
(define so (socket-connect/ai
(address-information host port type: sock/dgram)))
(define-values (i o) (socket-i/o-ports so))
;; a useful example would be nice
...)
<procedure>(socket-i/o-port->socket port)</procedure>
Returns the socket object assocated with input or output port {{port}}. From
there you can obtain a file descriptor with {{socket-fileno}}.
Alternatively, {{port->fileno}} from [[/man/4/Unit posix|posix]] is
supported to obtain a file descriptor. (Also see [[#Bugs and limitations]].)
<procedure>(socket-abandon-port port)</procedure>
Marks the socket input or output port {{port}} as abandoned. Normally, when
an socket input port is closed the read side of the connection is shut
down; similarly closing the output port shuts down the write side.
Marking a port as abandoned skips this shutdown. This is useful to
ensure a connection stays open after the port is closed.
The socket is still closed after both ports are closed, regardless of
their abandoned status.
==== Parameters
<parameter>(socket-connect-timeout ms) [default: #f]</parameter><br>
<parameter>(socket-accept-timeout ms) [default: #f]</parameter><br>
<parameter>(socket-receive-timeout ms) [default: 1 minute]</parameter><br>
<parameter>(socket-send-timeout ms) [default: 1 minute]</parameter><br>
Timeouts in milliseconds for connect, receive, send and accept
operations. If these timeout are exceeded, the error {{(exn i/o net timeout)}}
is raised. If {{#f}}, the operation never times out (unless
the system forces it to).
<parameter>(socket-send-buffer-size n) [default: #f]</parameter>
<parameter>(socket-send-size n) [default: 16384]</parameter>
<parameter>(socket-receive-buffer-size n) [default: 4096]</parameter>
These parameters are used mostly to adjust the behavior of socket ports,
and take effect when the ports are created.
{{(socket-send-buffer-size)}} is the buffer size used by socket output
ports. If {{#f}}, no buffering is done. A power of 2, such as 1K or 4K,
is an appropriate value.
{{(socket-send-size)}} is used by socket output ports, and is the size
used in a single call to {{socket-send}} by {{socket-send-all}}. It
can be {{#f}}, meaning infinite, so that any remaining data is sent at
each call. When set, it should usually be a multiple of
{{(socket-send-buffer-size)}}, assuming buffering is also enabled. A
power of 2 is an appropriate value, such as 8K or 16K.
{{(socket-receive-buffer-size)}} is the size used for the input buffer
in socket input ports. A power of 2, such as 4K, is appropriate.
Input buffering can not be disabled.
=== Socket option interface
BSD socket option values are of substantially differing types: boolean flags
(TCP_NODELAY), integers (SO_SNDBUF), structures (SO_LINGER), and so on. Still, we want a
consistent interface and an element of type-safety as well. So for each option, we
provide a unique getter / setter procedure which does the type-checking and marshals
(or unmarshals) the data as needed.
Each getter / setter takes a socket argument. The "socket" is either
a file descriptor number, as returned by a socket() call, or a socket object
as provided in this extension. SRFI-17 generalized set! is used on the
getters to set socket options.
(tcp-no-delay? s) ; => #t or #f
(set! (tcp-no-delay s) #t)
An error is thrown if the socket call fails, if the value passed is of
incorrect type, or if you try to set a read-only option.
An error of {{(exn i/o net unsupported)}} is thrown if you try to use
a socket option that is not defined at all on the platform, or that is
defined but not supported by the operating system. Note that some
platforms, particularly Windows, may return a false positive for
"unsupported option" when it really indicates a usage error (wrong
socket type or option value).
==== Socket option accessors
Below is a list of option procedures and their value type. The procedure names are
verbose variants of their associated constant names. For example, {{SO_REUSEADDR}} becomes
{{so-reuse-address}}.
Only booleans and integers and their read-only variants are currently supported. The
intention is to additionally support timevals, linger, ip_mreq structs and ipoptions, at
least. There is an example of linger support in the low-level interface below.
<procedure>(so-reuse-address? s) [so/reuseaddr] </procedure><br>
<procedure>(so-debug? s) [so/debug] </procedure><br>
<procedure>(so-keep-alive? s) [so/keepalive] </procedure><br>
<procedure>(so-dont-route? s) [so/dontroute] </procedure><br>
<procedure>(so-broadcast? s) [so/broadcast] </procedure><br>
<procedure>(so-oob-inline? s) [so/oobinline] </procedure><br>
<procedure>(so-accept-connections? s) [so/acceptconn] (r/o)</procedure><br>
<procedure>(so-send-buffer s) [so/sndbuf] </procedure><br>
<procedure>(so-receive-buffer s) [so/rcvbuf] </procedure><br>
<procedure>(so-send-low-water s) [so/sndlowat] </procedure><br>
<procedure>(so-receive-low-water s) [so/rcvlowat] </procedure><br>
<procedure>(so-error s) [so/error] (r/o)</procedure><br>
<procedure>(so-type s) [so/type] (r/o)</procedure><br>
Getters / setters for boolean and integer socket-level options, where {{s}} is a
''socket'' object or an integer file descriptor. To set an option, use SRFI-17
generalized set:
(set! (so-reuse-address? s) #t)
"(r/o)" indicates the option is read-only; an error will be raised if
you attempt to set it.
As a special note on {{so-reuse-address?}}, on Windows platforms it
will first attempt to use option {{so/exclusiveaddruse}} because this
option matches UNIX semantics. If this fails it will fall back to
{{so/reuseaddr}}, which allows any processes to rebind a previously
bound address and port.
<procedure>(tcp-no-delay? s) [tcp/nodelay]</procedure><br>
<procedure>(tcp-no-push? s) [tcp/nopush]</procedure><br>
<procedure>(tcp-no-options? s) [tcp/noopt]</procedure><br>
<procedure>(tcp-keep-alive s) [tcp/keepalive]</procedure><br>
<procedure>(tcp-max-segment-size s) [tcp/maxseg]</procedure><br>
Getters / setters for TCP-level socket options ({{ipproto/tcp}}). {{s}} is
a ''socket'' object or an integer file descriptor.
<procedure>(ip-header-included? s) [ip/hdrincl]</procedure><br>
<procedure>(ip-type-of-service s) [ip/tos]</procedure><br>
<procedure>(ip-time-to-live s) [ip/ttl]</procedure><br>
Getters / setters for IP-level socket options ({{ipproto/ip}}). {{s}} is
a ''socket'' object or an integer file descriptor.
<procedure>(ipv6-v6-only? s) [ipv6/v6only] </procedure><br>
Getters / setters for IPv6-level socket options ({{ipproto/ipv6}}). {{s}} is
a ''socket'' object or an integer file descriptor.
==== Low-level socket option interface
A low-level socket option interface is also provided. This is
intended to let you use the constants defined above (or your own) when
there is no high-level interface implemented. This interface can get
or set arbitrary option contents; you're not limited to predefined
types such as integer or boolean. No checking is done that the passed
option value is appropriate, as that's the job of the high-level
interface.
<procedure>(set-socket-option s level name val)</procedure>
Set the value of option {{name}} at socket level {{level}} on socket {{s}} to {{val}}.
{{val}} may be a fixnum or a boolean. It may also be a blob or a string; if so, the raw
contents are passed to the option, which is useful when a structure is required. The return value is unspecified.
If an unsupported option or level is requested, a condition of type
{{(exn i/o net unsupported)}} is raised.
Note: due to the vagaries of structure member alignment (and 32 vs. 64-bit sizes), it's
not generally safe to pack raw data yourself into a blob or a SRFI-4 vector. Instead, you
should treat the blob contents as a C struct. See the longer example down the page
for more.
(set-socket-option S ipproto/tcp tcp/nodelay 1)
(set-socket-option S ipproto/tcp tcp/nodelay (make-string 4 #\x0))
(set-socket-option S sol/socket so/rcvlowat (u32vector->blob/shared (u32vector #x01020304)))
<procedure>(get-socket-option s level name #!optional (len #f))</procedure>
Get the value of option {{name}} at socket level {{level}} on socket
{{s}}. If {{len}} is {{#f}}, the default, we interpret the option
value as an integer and return that. Otherwise, temporary storage of
length {{len}} is allocated to receive the binary option data; after
the call it is resized to fit the data, and returned. If {{len}} is too
small to hold the returned data, the result is undefined.
If an unsupported option or level is requested, a condition of type
{{(exn i/o net unsupported)}} is raised.
This procedure does not convert integers to boolean values---if you
expect a boolean flag, assume zero means {{#f}} and non-zero means
{{#t}}. Don't be surprised if boolean flags return different non-zero
integer values from those you put in; that's an implementation detail.
You can only rely on true being some non-zero value.
(get-socket-option S ipproto/tcp tcp/nodelay) ; => 8, perhaps, meaning #t
(get-socket-option S ipproto/tcp tcp/nodelay 64) ; => #${08000000}
(get-socket-option S ipproto/tcp tcp/nodelay 4) ; => #${08000000}
==== Socket option constants
Integer constants are provided for socket levels, socket types, and
socket options. They are renamed slightly, and consistently, to
achieve a more Schemely appearance: for example, C's {{SO_REUSEADDR}}
becomes {{so/reuseaddr}}.
Some platforms do not define all the constants we provide. If a
constant is undefined, its value is {{#f}} and attempting to get or
set it will raise an error. Note that a platform may define a
constant but not support it (for example, {{ipv6/v6only}} on Windows
prior to Vista); this will not be known until you try to get or set
it.
===== Socket-level constants
<constant>so/reuseaddr</constant><br>
<constant>so/debug</constant><br>
<constant>so/acceptconn</constant><br>
<constant>so/keepalive</constant><br>
<constant>so/dontroute</constant><br>
<constant>so/broadcast</constant><br>
<constant>so/linger</constant><br>
<constant>so/oobinline</constant><br>
<constant>so/sndbuf</constant><br>
<constant>so/rcvbuf</constant><br>
<constant>so/sndlowat</constant><br>
<constant>so/rcvlowat</constant><br>
<constant>so/sndtimeo</constant><br>
<constant>so/rcvtimeo</constant><br>
<constant>so/error</constant><br>
<constant>so/type</constant><br>
<constant>so/useloopback</constant><br>
<constant>so/reuseport</constant><br>
<constant>so/timestamp</constant><br>
<constant>so/exclusiveaddruse</constant><br>
Socket-level socket options for use with {{set-socket-option}} and
{{get-socket-option}} at level {{sol/socket}}.
===== TCP-level constants
<constant>tcp/nodelay</constant><br>
<constant>tcp/nopush</constant><br>
<constant>tcp/noopt</constant><br>
<constant>tcp/keepalive</constant><br>
<constant>tcp/maxseg</constant><br>
TCP-level socket options for use with {{set-socket-option}} and
{{get-socket-option}} at level {{ipproto/tcp}}.
===== IP-level constants
<constant>ip/options</constant><br>
<constant>ip/hdrincl</constant><br>
<constant>ip/tos</constant><br>
<constant>ip/ttl</constant><br>
<constant>ip/mtu</constant><br>
<constant>ip/mtu-discover</constant><br>
<constant>ip/pktinfo</constant><br>
<constant>ip/recverr</constant><br>
<constant>ip/recvtos</constant><br>
<constant>ip/recvttl</constant><br>
<constant>ip/router-alert</constant><br>
<constant>ip/recvopts</constant><br>
<constant>ip/recvretopts</constant><br>
<constant>ip/retopts</constant><br>
<constant>ip/recvdstaddr</constant><br>
<constant>ip/multicast-if</constant><br>
<constant>ip/multicast-ttl</constant><br>
<constant>ip/multicast-loop</constant><br>
<constant>ip/add-membership</constant><br>
<constant>ip/drop-membership</constant><br>
IP-level socket options for use with {{set-socket-option}} and
{{get-socket-option}} at level {{ipproto/ip}}.
===== Socket and protocol levels
<constant>sol/socket</constant><br>
<constant>ipproto/ip</constant><br>
<constant>ipproto/ipv6</constant><br>
<constant>ipproto/tcp</constant><br>
<constant>ipproto/icmp</constant><br>
<constant>ipproto/udp</constant><br>
Socket level constants, for use with {{set-socket-option}} and {{get-socket-option}}.
==== SO_LINGER lowlevel example
This is a pretty hairy example of getting and setting the {{so/linger}} option, which does
not currently have a high-level equivalent. {{so/linger}} usually takes and returns a
{{struct linger}} value which consists of an on/off flag and a linger timeout in seconds.
(But not always!)
The approach below encases the {{struct linger}} in an
appropriately-sized blob and creates an encoder and decoder for this
structure. Any useful option taking a structure value should ideally
have a high-level interface created for it instead.
(define _linger_size (foreign-value "sizeof(struct linger)" int))
(define (encode-linger-option state time)
(let ((blob (make-blob _linger_size)))
((foreign-lambda* void ((scheme-pointer ptr) (bool onoff) (int linger))
"struct linger *p = ptr;"
"p->l_onoff = onoff; p->l_linger = linger;")
blob state time)
blob))
(define (decode-linger-option blob)
; sanity checking on parameter recommended here
(list ((foreign-lambda* bool ((scheme-pointer p))
"return(((struct linger *)p)->l_onoff);") blob)
((foreign-lambda* int ((scheme-pointer p))
"return(((struct linger *)p)->l_linger);") blob)))
(set-socket-option S sol/socket so/linger (encode-linger-option #t 100))
(decode-linger-option
(get-socket-option S sol/socket so/linger _linger_size))
; => (#t 100)
=== Examples
==== Client-server examples
For a simple example of client-server communication over a unix socket, see
[[/Communicating over a unix socket, using the socket egg|here]].
A TCP/IP example is still to be written.
==== Disable Nagle's Algorithm on TCP listener socket
The [[/man/4/Unit tcp|tcp unit]] does not support setting arbitrary socket
options on sockets it creates. However, you can obtain a listener's
socket file descriptor after the fact.
(define L (tcp-listen 8080))
(define S (tcp-listener-fileno L))
(set! (tcp-no-delay? S) #t)
==== Set socket options on HTTP server
This is similar to the above. HTTP servers may see some performance
gain when Nagle's algorithm is disabled. This is generally the
default on Linux, but not Solaris or OS X.
(This needs to be updated for spiffy / intarweb.)
(parameterize ((http:listen-procedure
(lambda (port backlog host)
(let ((L (tcp-listen port backlog host)))
(set! (tcp-no-delay? (tcp-listener-fileno L) #t))
L))))
((http:make-server ...)))
=== Bugs and limitations
* If IPv6 support is totally unavailable on your platform (not simply disabled)
then building this extension will fail. This should be fixed in a future
release.
* In order to support {{port->fileno}}, socket ports are
backwardly-compatible with core tcp ports to the extent that
port-related procedures from [[/man/4/Unit tcp|Unit tcp]] will
erroneously accept socket ports as well. These procedures are
{{tcp-abandon-port}}, {{tcp-addresses}}, and {{tcp-port-numbers}}.
Using them with socket ports has an undefined result. The other way
around is safe: procedures in this extension will throw an error if
used with core tcp ports.
=== About this egg
==== Source
[[https://github.com/ursetto/socket-egg]]
==== Author
[[http://3e8.org|Jim Ursetto]]
Some code was derived from the core [[/man/4/Unit tcp|tcp]]
unit by Felix Winkelmann and the rest of the Chicken team.
==== Version history
; 0.3.3 : Quote variables for Windows build (wasamasa)
; 0.3.2 : Remove debug print statements (kdltr)
; 0.3.1 : Add Chicken 5 support. Add simple unix socket test to {{tests/}}; not used by {{-test}}.
; 0.2.7 : remove some debug prints
; 0.2.6 : Handle {{##sys#scan-buffer-line}} signature change in CHICKEN 4.8.2. Bug reported by Jonathan Chan and David Krentzlin.
; 0.2.5 : socket-accept: Handle select failure. Patch by Jonathan Chan.
; 0.2.4 : {{SO_EXCLUSIVEADDR}} fix for Cygwin
; 0.2.3 : Set connectionless sockets to nonblocking
; 0.2.2 : Fix segfault in {{socket-receive}}, {{socket-send}} and friends when socket argument was not a socket (reported by hypnocat)
; 0.2.1 : Treat string {{addr}} arg to {{name-information}} as {{(inet-address addr #f)}}
; 0.2 : Add UNIX socket support; eliminate much dead code and runtime support checks.
; 0.1 : Initial release for Chicken 4.
==== Portability
* socket 0.1 has been successfully built and tested on Mac OS X 10.5,
Ubuntu 10.10, Windows XP SP3 and NetBSD 5.1.
==== License
Copyright (c) 2011-2019, Jim Ursetto. All rights reserved.
Copyright (c) 2008-2011, The Chicken Team
Copyright (c) 2000-2007, Felix L. Winkelmann
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.