Prev | Index | Next |
You can connect to a socket using tcpconnect
. The
example below illustrates how.
1: #include "async.h" 2: 3: 4: void 5: connected(int fd) 6: { 7: if(fd < 0) 8: fatal << "tcpconnect\n"; 9: 10: warn << "fd = " << fd << "\n"; 11: exit(0); 12: } 13: 14: 15: int 16: main(int argc, char *argv[]) 17: { 18: async_init(); 19: tcpconnect("www.mit.edu", 80, wrap(connected)); 20: amain(); 21: }
The prototype of tcpconnect
is
void tcpconnect(str hostname, int_16 port, callback<void, int>::ref cb);
tcpconnect
calls cb
when it is done and
passes to cb
the file descriptor for the socket that the
connection was made on. It passes -1 to cb
if something
went wrong, for example if the underlying call to
socket()
, bind()
, or
connect()
fails.
The prototype indicated that we need to pass
tcpconnect
a callback function that returns
void
and that still expects an int
parameter. Our connected
function fits the
description.
You can't just read from a socket or write to a socket. You need
to be sure that your program will not block. Using libasync's
fdcb
function, you will be notified when a certain file
descriptor is readable or writable.
The code below builds on the tcpconnect
example above
and implements a very simple HTTP client, but it makes the point
nonetheless.
1: #include "async.h" 2: 3: 4: void 5: http_read(int fd) 6: { 7: strbuf resp; 8: 9: int n = resp.tosuio()->input(fd); 10: if(n < 0) 11: fatal << "read\n"; 12: 13: if(n == 0) { 14: fdcb(fd, selread, 0); 15: close(fd); 16: exit(0); 17: }; 18: 19: warn << resp; 20: } 21: 22: void 23: http_write(int fd, strbuf req) 24: { 25: int n = req.tosuio()->output(fd); 26: 27: if(n < 0) 28: fatal << "write\n"; 29: 30: // still stuff to write: don't disable writability callback 31: if(req.tosuio()->resid()) 32: return; 33: 34: // done writing request. 35: fdcb(fd, selwrite, 0); 36: fdcb(fd, selread, wrap(http_read, fd)); 37: } 38: 39: void 40: connected(int fd) 41: { 42: if(fd < 0) 43: fatal << "connect\n"; 44: 45: strbuf req("GET / HTTP/1.0\r\n\r\n"); 46: fdcb(fd, selwrite, wrap(http_write, fd, req)); 47: } 48: 49: 50: int 51: main(int argc, char *argv[]) 52: { 53: async_init(); 54: if(argc < 2) 55: fatal << "usage: fdcb host\n"; 56: 57: tcpconnect(argv[1], 80, wrap(connected)); 58: amain(); 59: }
main
is pretty much the same as in the example above.
However, connected
uses fdcb
to set a
callback for writability of file descriptor fd
. The
prototype for fdcb
is:
void fdcb(int socket, char operation, callback<void>::ptr cb);
operation
can be either selread
(readability) or selwrite
(writability). The call to
fdcb
on line 46 instructs libasync to call
http_write
when file descriptor fd
is ready
for writing.
When http_write
executes, it tries to write (the
remaining bytes in) req
out to file descriptor
fd
. This is what line 25 does---you'll get the details
about suio
later. Don't worry about it for now. (If you
must know, consult Using
libasync.) If we didn't write all of the request
(if
-statement on line 31) then don't disable the
writability callback. When fd
is writable again,
http_write
gets invoked again---this goes on until the
contents of req
have been written or until an error
occurs. When we're done writing the request, we disable the
writability callback (line 35) by passing 0 to fdcb
.
(Hence the ::ptr
rather than ::ref
in the
fdcb
prototype.) Now we're interested in reading the
response so we set a callback function (http_read
) for
readability on line 36.
In http_read
we read data from the file descriptor
into a strbuf
object on line 9. Forget about the details
of it: it does a single read
and puts the bytes in
resp
. We handle the error case, print out the junk we
just read from the web server and quit the game if the remote party
closed the connection. Notice that if we don't do anything (i.e., when
the remote party did not close the connection), the readability
callback stays where it is and we can expect http_read
to
be called again.
Prev | Index | Next |