| 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 |