- Where can I get those header files?
-
If you don't have them on your system already, you probably don't need them.
Check the manual for your particular platform. If you're building for Windows,
you only need to #include .
- What do I do when bind() reports "Address already in use"?
-
You have to use setsockopt() with the SO_REUSEADDR
option on the listening socket. Check out the section on
bind() and the section on select()
for an example.
- How do I get a list of open sockets on the system?
-
Use the netstat. Check the man page for full
details, but you should get some good output just typing:
The only trick is determining which socket is associated with which program.
:-)
- How can I view the routing table?
-
Run the route command (in /sbin on most
Linuxes) or the command netstat -r.
- How can I run the client and server programs if I only have one computer?
Don't I need a network to write network program?
-
Fortunately for you, virtually all machines implement a loopback network
"device" that sits in the kernel and pretends to be a network card. (This is the
interface listed as "lo" in the routing table.)
Pretend you're logged into a machine named "goat". Run the client in
one window and the server in another. Or start the server in the background
("server &") and run the client in the same window. The
upshot of the loopback device is that you can either client goat
or client localhost (since "localhost" is likely
defined in your /etc/hosts file) and you'll have the client
talking to the server without a network!
In short, no changes are necessary to any of the code to make it run on a
single non-networked machine! Huzzah!
- How can I tell if the remote side has closed connection?
-
You can tell because recv() will return 0.
- How do I implement a "ping" utility? What is ICMP? Where can I find out
more about raw sockets and SOCK_RAW?
-
All your raw sockets questions will be answered in W. Richard Stevens' UNIX
Network Programming books. See the books section of this
guide.
- How do I build for Windows?
-
First, delete Windows and install Linux or BSD. };-). No, actually,
just see the section on building for Windows in the
introduction.
- How do I build for Solaris/SunOS? I keep getting linker errors when I try
to compile!
-
The linker errors happen because Sun boxes don't automatically compile in the
socket libraries. See the section on building for
Solaris/SunOS in the introduction for an example of how to do this.
- Why does select() keep falling out on a signal?
-
Signals tend to cause blocked system calls to return -1 with
errno set to EINTR. When you set up a signal
handler with sigaction(), you can set the flag
SA_RESTART, which is supposed to restart the system call after
it was interrupted.
Naturally, this doesn't always work.
My favorite solution to this involves a goto statement. You know
this irritates your professors to no end, so go for it!
select_restart: if ((err = select(fdmax+1, &readfds, NULL, NULL, NULL)) == -1) { if (errno == EINTR) { // some signal just interrupted us, so restart goto select_restart; } // handle the real error here: perror("select"); }
|
Sure, you don't need to use goto in this case; you can use
other structures to control it. But I think the goto statement is
actually cleaner.
- How can I implement a timeout on a call to recv()?
-
Use select()! It allows you to specify a
timeout parameter for socket descriptors that you're looking to read from. Or,
you could wrap the entire functionality in a single function, like this:
#include #include #include #include
int recvtimeout(int s, char *buf, int len, int timeout) { fd_set fds; int n; struct timeval tv;
// set up the file descriptor set FD_ZERO(&fds); FD_SET(s, &fds);
// set up the struct timeval for the timeout tv.tv_sec = timeout; tv.tv_usec = 0;
// wait until timeout or data received n = select(s+1, &fds, NULL, NULL, &tv); if (n == 0) return -2; // timeout! if (n == -1) return -1; // error
// data must be here, so do a normal recv() return recv(s, buf, len, 0); } . . . // Sample call to recvtimeout(): n = recvtimeout(s, buf, sizeof(buf), 10); // 10 second timeout
if (n == -1) { // error occurred perror("recvtimeout"); } else if (n == -2) { // timeout occurred } else { // got some data in buf } . . .
|
Notice that recvtimeout() returns -2 in case of a
timeout. Why not return 0? Well, if you recall, a return value
of 0 on a call to recv() means that the remote side
closed the connection. So that return value is already spoken for, and
-1 means "error", so I chose -2 as my timeout
indicator.
- How do I encrypt or compress the data before sending it through the
socket?
-
One easy way to do encryption is to use SSL (secure sockets layer), but
that's beyond the scope of this guide.
But assuming you want to plug in or implement your own compressor or
encryption system, it's just a matter of thinking of your data as running
through a sequence of steps between both ends. Each step changes the data in
some way.
- server reads data from file (or whereever)
- server encrypts data (you add this part)
- server send()s encrypted data
Now the other way around:
- client recv()s encrypted data
- client decrypts data (you add this part)
- client writes data to file (or whereever)
You can also do compression at the same point that you do the
encryption/decryption, above. Or you could do both! Just remember to compress
before you encrypt. :)
Just as long as the client properly undoes what the server does, the data
will be fine in the end no matter how many intermediate steps you add.
So all you need to do to use my code is to find the place between where the
data is read and the data is sent (using send()) over the network, and
stick some code in there that does the encryption.
- What is this "PF_INET" I keep seeing? Is it related to
AF_INET?
-
Yes, yes it is. See the section on socket()
for details.
- How can I write a server that accepts shell commands from a client and
executes them?
-
For simplicity, lets say the client connect()s, send()s,
and close()s the connection (that is, there are no subsequent system
calls without the client connecting again.)
The process the client follows is this:
- connect() to server
- send("/sbin/ls > /tmp/client.out")
- close() the connection
Meanwhile, the server is handling the data and executing it:
- accept() the connection from the client
- recv(str) the command string
- close() the connection
- system(str) to run the command
Beware! Having the server execute what the client says is like giving
remote shell access and people can do things to your account when they connect
to the server. For instance, in the above example, what if the client sends
"rm -rf ~"? It deletes everything in your account, that's
what!
So you get wise, and you prevent the client from using any except for a
couple utilities that you know are safe, like the foobar
utility:
if (!strcmp(str, "foobar")) { sprintf(sysstr, "%s > /tmp/server.out", str); system(sysstr); }
|
But you're still unsafe, unfortunately: what if the client enters
"foobar; rm -rf ~"? The safest thing to do is to write a little
routine that puts an escape ("") character in front of all
non-alphanumeric characters (including spaces, if appropriate) in the arguments
for the command.
As you can see, security is a pretty big issue when the server starts
executing things the client sends.
- I'm sending a slew of data, but when I recv(), it only receives
536 bytes or 1460 bytes at a time. But if I run it on my local machine, it
receives all the data at the same time. What's going on?
-
You're hitting the MTU--the maximum size the physical medium can handle. On
the local machine, you're using the loopback device which can handle 8K or more
no problem. But on ethernet, which can only handle 1500 bytes with a header, you
hit that limit. Over a modem, with 576 MTU (again, with header), you hit the
even lower limit.
You have to make sure all the data is being sent, first of all. (See the sendall() function implementation for details.)
Once you're sure of that, then you need to call recv() in a loop until
all your data is read.
Read the section Son of Data Encapsulation for
details on receiving complete packets of data using multiple calls to
recv().
- I'm on a Windows box and I don't have the fork() system call or
any kind of struct sigaction. What to do?
-
If they're anywhere, they'll be in POSIX libraries that may have shipped with
your compiler. Since I don't have a Windows box, I really can't tell you the
answer, but I seem to remember that Microsoft has a POSIX compatibility layer
and that's where fork() would be. (And maybe even
sigaction.)
Search the help that came with VC++ for "fork" or "POSIX" and see if it gives
you any clues.
If that doesn't work at all, ditch the fork()/sigaction
stuff and replace it with the Win32 equivalent: CreateProcess(). I
don't know how to use CreateProcess()--it takes a bazillion arguments,
but it should be covered in the docs that came with VC++.
- How do I send data securely with TCP/IP using encryption?
-
Check out the OpenSSL project.
- I'm behind a firewall--how do I let people outside the firewall know my
IP address so they can connect to my machine?
-
Unfortunately, the purpose of a firewall is to prevent people outside the
firewall from connecting to machines inside the firewall, so allowing them to do
so is basically considered a breach of security.
This isn't to say that all is lost. For one thing, you can still often
connect() through the firewall if it's doing some kind of masquerading
or NAT or something like that. Just design your programs so that you're always
the one initiating the connection, and you'll be fine.
If that's not satisfactory, you can ask your sysadmins to poke a hole in the
firewall so that people can connect to you. The firewall can forward to you
either through it's NAT software, or through a proxy or something like that.
Be aware that a hole in the firewall is nothing to be taken lightly. You have
to make sure you don't give bad people access to the internal network; if you're
a beginner, it's a lot harder to make software secure than you might
imagine.
Don't make your sysadmin mad at me. ;-)
In the Unix world, there are a lot of manuals. They have little sections that
describe individual functions that you have at your disposal.
Of course, manual would be too much of a thing to type. I
mean, no one in the Unix world, including myself, likes to type that much.
Indeed I could go on and on at great length about how much I prefer to be terse
but instead I shall be brief and not bore you with long-winded diatribes about
how utterly amazingly brief I prefer to be in virtually all circumstances in
their entirety.
[Applause]
Thank you. What I am getting at is that these pages are called "man pages" in
the Unix world, and I have included my own personal truncated variant here for
your reading enjoyment. The thing is, many of these functions are way more
general purpose than I'm letting on, but I'm only going to present the parts
that are relevant for Internet Sockets Programming.
And, now for your reading pleasure, I have included some basic home-grown man
pages right here in this guide. And here is what is wrong with them:
- They are incomplete and only show the basics from the guide.
- There are many more man pages than this in the real world.
- They are different than the ones on your system.
- The header files might be different for certain functions.
- The function parameters be different for certain functions.
If you want the real information, check your local Unix man pages by typing
man whatever, where "whatever" is something that you're
incredibly interested in, such as "accept".
So why even include these at all in the Guide? Well, there are a few reasons,
but the best are that (a) these versions are geared specifically toward network
programming and are easier to digest than the real ones, and (b) these versions
contain examples!
Oh! And speaking of the examples, I don't tend to put in all the error
checking because it really increases the length of the code. But you should
absolutely do error checking pretty much any time you make any of the system
calls unless you're totally 100% sure it's not going to fail, and you should
probably do it even then!
Accept an incoming connection on a listening socket
- Prototypes
-
#include #include
int accept(int s, struct sockaddr *addr, socklen_t
*addrlen);
- Description
-
Once you've gone through the trouble of getting a SOCK_STREAM
socket and setting it up for incoming connections with listen(), then
you call accept() to actually get yourself a new socket descriptor to
use for subsequent communication with the newly connected client.
The old socket that you are using for listening is still there, and will be
used for further accept() calls as they come in.
|
s |
The listen()ing socket descriptor. |
addr
|
This is filled in with the address of the site that's connecting to
you. |
addrlen
|
This is filled in with the sizeof() the structure returned in the
addr parameter. You can safely ignore it if you assume you're
getting a struct sockaddr_in back, which you know you are, because
that's the type you passed in for
addr. |
accept() will normally block, and you can use select() to
peek on the listening socket descriptor ahead of time to see if it's "ready to
read". If so, then there's a new connection waiting to be accept()ed!
Yay! Alternatively, you could set the O_NONBLOCK flag on the
listening socket using fcntl(), and then it will never block, choosing
instead to return -1 with errno set to
EWOULDBLOCK.
The socket descriptor returned by accept() is a bona fide socket
descriptor, open and connected to the remote host. You have to close()
it when you're done with it.
- Return Value
-
accept() returns the newly connected socket descriptor, or
-1 on error, with errno set
appropriately.
- Example
-
int s, s2; struct sockaddr_in myaddr, remoteaddr; socklen_t remoteaddr_len;
myaddr.sin_family = AF_INET; myaddr.sin_port = htons(3490); // clients connect to this port myaddr.sin_addr.s_addr = INADDR_ANY; // autoselect IP address
s = socket(PF_INET, SOCK_STREAM, 0); bind(s, (struct sockaddr*)myaddr, sizeof(myaddr));
listen(s, 10); // set s up to be a server (listening) socket
for(;;) { s2 = accept(s, &remoteaddr, &remoteaddr_len);
// now you can send() and recv() with the // connected client via socket s2 }
|
- See Also
-
socket(), listen(), struct
sockaddr_in
Associate a socket with an IP address and port number
- Prototypes
-
#include #include
int bind(int sockfd, struct sockaddr *my_addr, socklen_t
addrlen);
- Description
-
When a remote machine wants to connect to your server program, it needs two
pieces of information: the IP address and the port number. The bind()
call allows you to do just that.
First, you call socket() to get a socket descriptor, and then you
load up a struct sockaddr_in with the IP address and port number
information, and then you pass both of those into bind(), and the IP
address and port are magically (using actual magic) bound to the socket!
If you don't know your IP address, or you know you only have one IP address
on the machine, or you don't care which of the machine's IP addresses is used,
you can simply set the s_addr field in your struct sockaddr_in
to INADDR_ANY and it will fill in the IP address for you.
Lastly, the addrlen parameter should be set to
sizeof(my_addr).
- Return Value
-
Returns zero on success, or -1 on error (and errno
will be set accordingly.)
- Example
-
struct sockaddr_in myaddr; int s;
myaddr.sin_family = AF_INET; myaddr.sin_port = htons(3490);
// you can specify an IP address: inet_aton("63.161.169.137", &myaddr.sin_addr.s_addr);
// or you can let it automatically select one: myaddr.sin_addr.s_addr = INADDR_ANY;
s = socket(PF_INET, SOCK_STREAM, 0); bind(s, (struct sockaddr*)myaddr, sizeof(myaddr));
|
- See Also
-
socket(), struct sockaddr_in, struct in_addr
Connect a socket to a server
- Prototypes
-
#include #include
int connect(int sockfd, const struct sockaddr
*serv_addr, socklen_t addrlen);
- Description
-
Once you've built a socket descriptor with the socket() call, you
can connect() that socket to a remote server using the well-named
connect() system call. All you need to do is pass it the socket
descriptor and the address of the server you're interested in getting to know
better. (Oh, and the length of the address, which is commonly passed to
functions like this.)
If you haven't yet called bind() on the socket descriptor, it is
automatically bound to your IP address and a random local port. This is usually
just fine with you, since you really don't care what your local port is; you
only care what the remote port is so you can put it in the
serv_addr parameter. You can call bind() if you
really want your client socket to be on a specific IP address and port, but this
is pretty rare.
Once the socket is connect()ed, you're free to send() and
recv() data on it to your heart's content.
Special note: if you connect() a SOCK_DGRAM UDP
socket to a remote host, you can use send() and recv() as well
as sendto() and recvfrom(). If you want.
- Return Value
-
Returns zero on success, or -1 on error (and errno
will be set accordingly.)
- Example
-
int s; struct sockaddr_in serv_addr;
// pretend the server is at 63.161.169.137 listening on port 80:
myaddr.sin_family = AF_INET; myaddr.sin_port = htons(80); inet_aton("63.161.169.137", &myaddr.sin_addr.s_addr);
s = socket(PF_INET, SOCK_STREAM, 0); connect(s, (struct sockaddr*)myaddr, sizeof(myaddr));
// now we're ready to send() and recv()
|
- See Also
-
socket(), bind()
Close a socket descriptor
- Prototypes
-
#include
int close(int s);
- Description
-
After you've finished using the socket for whatever demented scheme you have
concocted and you don't want to send() or recv() or, indeed,
do anything else at all with the socket, you can close() it, and
it'll be freed up, never to be used again.
The remote side can tell if this happens one of two ways. One: if the remote
side calls recv(), it will return 0. Two: if the remote
side calls send(), it'll recieve a signal SIGPIPE and
send() will return -1 and errno will be set to
EPIPE.
Windows users: the function you need to use is called
closesocket(), not close(). If you try to use close()
on a socket descriptor, it's possible Windows will get angry... And you wouldn't
like it when it's angry.
- Return Value
-
Returns zero on success, or -1 on error (and errno
will be set accordingly.)
- Example
-
s = socket(PF_INET, SOCK_DGRAM, 0); . . . // a whole lotta stuff...*BRRRONNNN!* . . . close(s); // not much to it, really.
|
- See Also
-
socket(), shutdown()
Returns the name of the system
- Prototypes
-
#include
int gethostname(char *name, size_t len);
- Description
-
Your system has a name. They all do. This is a slightly more Unixy thing than
the rest of the networky stuff we've been talking about, but it still has its
uses.
For instance, you can get your host name, and then call
gethostbyname() to find out your IP address.
The parameter name should point to a buffer that will hold
the host name, and len is the size of that buffer in bytes.
gethostname() won't overwrite the end of the buffer (it might return an
error, or it might just stop writing), and it will NUL-terminate
the string if there's room for it in the buffer.
- Return Value
-
Returns zero on success, or -1 on error (and errno
will be set accordingly.)
- Example
-
char hostname[128];
gethostname(hostname, sizeof(hostname)); printf("My hostname: %s
", hostname);
|
- See Also
-
gethostbyname()
Get an IP address for a hostname, or vice-versa
- Prototypes
-
#include #include
struct hostent *gethostbyname(const char *name); struct
hostent *gethostbyaddr(const char *addr, int len, int type);
- Description
-
These functions map back and forth between host names and IP addresses. After
all, you want an IP address to pass to connect(), right? But no one
wants to remember an IP address. So you let your users type in things like
"www.yahoo.com" instead of "66.94.230.35".
gethostbyname() takes a string like "www.yahoo.com", and returns a
struct hostent which contains tons of information, including the IP
address. (Other information is the official host name, a list of aliases, the
address type, the length of the addresses, and the list of addresses--it's a
general-purpose structure that's pretty easy to use for our specific purposes
once you see how.)
gethostbyaddr() takes a struct in_addr and brings you up a
corresponding host name (if there is one), so it's sort of the reverse of
gethostbyname(). As for parameters, even though addr is
a char*, you actually want to pass in a pointer to a struct
in_addr. len should be sizeof(struct in_addr), and
type should be AF_INET.
So what is this struct hostent that gets returned? It has a number
of fields that contain information about the host in question.
|
char *h_name |
The real canonical host name. |
|
char **h_aliases |
A list of aliases that can be accessed with arrays--the last element is
NULL |
|
int h_addrtype |
The result's address type, which really should be AF_INET for
our purposes.. |
|
int length |
The length of the addresses in bytes, which is 4 for IP (version 4)
addresses. |
|
char **h_addr_list |
A list of IP addresses for this host. Although this is a char**,
it's really an array of struct in_addr*s in disguise. The last array
element is NULL. |
|
h_addr |
A commonly defined alias for h_addr_list[0]. If you just want
any old IP address for this host (yeah, they can have more than one) just use
this field. |
- Return Value
-
Returns a pointer to a resultant struct hostent or success, or
NULL on error.
Instead of the normal perror() and all that stuff you'd normally use
for error reporting, these functions have parallel results in the variable
h_errno, which can be printed using the functions
herror() or hstrerror(). These work just like the classic
errno, perror(), and strerror() functions
you're used to.
- Example
-
int i; struct hostent *he; struct in_addr **addr_list; struct in_addr addr;
// get the addresses of www.yahoo.com:
he = gethostbyname("www.yahoo.com"); if (he == NULL) { // do some error checking herror("gethostbyname"); // herror(), NOT perror() exit(1); }
// print information about this host: printf("Official name is: %s
", he->h_name); printf("IP address: %s
", inet_ntoa(*(struct in_addr*)he->h_addr)); printf("All addresses: "); addr_list = (struct in_addr **)he->h_addr_list; for(i = 0; addr_list[i] != NULL; i++) { printf("%s ", inet_ntoa(*addr_list[i])); } printf("
");
// get the host name of 66.94.230.32:
inet_aton("66.94.230.32", &addr); he = gethostbyaddr(&addr, sizeof(addr), AF_INET);
printf("Host name: %s
", he->h_name);
|
- See Also
-
gethostname(), errno, perror(), strerror(), struct
in_addr
Return address info about the remote side of the connection
- Prototypes
-
#include
int getpeername(int s, struct sockaddr *addr, socklen_t
*len);
- Description
-
Once you have either accept()ed a remote connection, or
connect()ed to a server, you now have what is known as a peer.
Your peer is simply the computer you're connected to, identified by an IP
address and a port. So...
getpeername() simply returns a struct sockaddr_in filled
with information about the machine you're connected to.
Why is it called a "name"? Well, there are a lot of different kinds of
sockets, not just Internet Sockets like we're using in this guide, and so "name"
was a nice generic term that covered all cases. In our case, though, the peer's
"name" is it's IP address and port.
Although the function returns the size of the resultant address in
len, you must preload len with the size of
addr.
- Return Value
-
Returns zero on success, or -1 on error (and errno
will be set accordingly.)
- Example
-
int s; struct sockaddr_in server, addr; socklen_t len;
// make a socket s = socket(PF_INET, SOCK_STREAM, 0);
// connect to a server server.sin_family = AF_INET; inet_aton("63.161.169.137", &server.sin_addr); server.sin_port = htons(80);
connect(s, (struct sockaddr*)&server, sizeof(server));
// get the peer name // we know we just connected to 63.161.169.137:80, so this should print: // Peer IP address: 63.161.169.137 // Peer port : 80
len = sizeof(addr); getpeername(s, (struct sockaddr*)&addr, &len); printf("Peer IP address: %s
", inet_ntoa(addr.sin_addr)); printf("Peer port : %d
", ntohs(addr.sin_port));
|
- See Also
-
gethostname(), gethostbyname(), gethostbyaddr()
Holds the error code for the last system call
- Prototypes
-
#include
int errno;
- Description
-
This is the variable that holds error information for a lot of system calls.
If you'll recall, things like socket() and listen() return
-1 on error, and they set the exact value of
errno to let you know specifically which error occurred.
The header file errno.h lists a bunch of constant symbolic
names for errors, such as EADDRINUSE, EPIPE,
ECONNREFUSED, etc. Your local man pages will tell you what codes
can be returned as an error, and you can use these at run time to handle
different errors in different ways.
Or, more commonly, you can call perror() or strerror() to
get a human-readable version of the error.
- Return Value
-
The value of the variable is the latest error to have transpired, which might
be the code for "success" if the last action succeeded.
- Example
-
s = socket(PF_INET, SOCK_STREAM, 0); if (s == -1) { perror("socket"); // or use strerror() }
tryagain: if (select(n, &readfds, NULL, NULL) == -1) { // an error has occurred!!
// if we were only interrupted, just restart the select() call: if (errno == EINTR) goto tryagain; // AAAA! goto!!!
// otherwise it's a more serious error: perror("select"); exit(1); }
|
- See Also
-
perror(), strerror()
Control socket descriptors
- Prototypes
-
#include #include
int fcntl(int s, int cmd, long arg);
- Description
-
This function is typically used to do file locking and other file-oriented
stuff, but it also has a couple socket-related functions that you might see or
use from time to time.
Parameter s is the socket descriptor you wish to operate on,
cmd should be set to F_SETFL, and
arg can be one of the following commands. (Like I said, there's
more to fcntl() than I'm letting on here, but I'm trying to stay
socket-oriented.)
|
O_NONBLOCK |
Set the socket to be non-blocking. See the section on blocking for more details. |
|
O_ASYNC |
Set the socket to do asynchronous I/O. When data is ready to be
recv()'d on the socket, the signal SIGIO will be
raised. This is rare to see, and beyond the scope of the guide. And I think it's
only available on certain systems. |
- Return Value
-
Returns zero on success, or -1 on error (and errno
will be set accordingly.)
Different uses of the fcntl() actually have different return values,
but I haven't covered them here because they're not socket-related. See your
local fcntl() man page for more information.
- Example
-
int s = socket(PF_INET, SOCK_STREAM, 0);
fcntl(s, F_SETFL, O_NONBLOCK); // set to non-blocking fcntl(s, F_SETFL, O_ASYNC); // set to asynchronous I/O
|
- See Also
-
Blocking, send()
Convert multi-byte integer types from host byte order to network byte
order
- Prototypes
-
#include
uint32_t htonl(uint32_t hostlong); uint16_t htons(uint16_t
hostshort); uint32_t ntohl(uint32_t netlong); uint16_t
ntohs(uint16_t netshort);
- Description
-
Just to make you really unhappy, different computers use different byte
orderings internally for their multibyte integers (i.e. any interger that's
larger than a char.) The upshot of this is that if you send()
a two-byte short int from an Intel box to a Mac (before they became
Intel boxes, too, I mean), what one computer thinks is the number
1, the other will think is the number 256, and
vice-versa.
The way to get around this problem is for everyone to put aside their
differences and agree that Motorola and IBM had it right, and Intel did it the
weird way, and so we all convert our byte orderings to "big-endian" before
sending them out. Since Intel is a "little-endian" machine, it's far more
politically correct to call our preferred byte ordering "Network Byte Order". So
these functions convert from your native byte order to network byte order and
back again.
(This means on Intel these functions swap all the bytes around, and on
PowerPC they do nothing because the bytes are already in Network Byte Order. But
you should always use them in your code anyway, since someone might want to
build it on an Intel machine and still have things work properly.)
Note that the types involved are 32-bit (4 byte, probably int) and
16-bit (2 byte, very likely short) numbers. 64-bit machines might have
a htonll() for 64-bit ints, but I've not seen it. You'll just have to
write your own.
Anyway, the way these functions work is that you first decide if you're
converting from host (your machine's) byte order or from network byte
order. If "host", the the first letter of the function you're going to call is
"h". Otherwise it's "n" for "network". The middle of the function name is always
"to" because you're converting from one "to" another, and the penultimate letter
shows what you're converting to. The last letter is the size of the data,
"s" for short, or "l" for long. Thus:
|
htons() |
host to network short |
|
htonl() |
host to network long |
|
ntohs() |
network to host short |
|
ntohl() |
network to host
long |
- Return Value
-
Each function returns the converted value.
- Example
-
uint32_t some_long = 10; uint16_t some_short = 20;
uint32_t network_byte_order;
// convert and send network_byte_order = htonl(some_long); send(s, &network_byte_order, sizeof(uint32_t), 0);
some_short == ntohs(htons(some_short)); // this expression is true
|
Convert IP addresses from a dots-and-number string to a struct
in_addr and back
- Prototypes
-
#include #include
#include
char *inet_ntoa(struct in_addr in); int inet_aton(const char
*cp, struct in_addr *inp); in_addr_t inet_addr(const char
*cp);
- Description
-
All of these functions convert from a struct in_addr (part of your
struct sockaddr_in, most likely) to a string in dots-and-numbers format
(e.g. "192.168.5.10") and vice-versa. If you have an IP address passed on the
command line or something, this is the easiest way to get a struct
in_addr to connect() to, or whatever. If you need more power, try
some of the DNS functions like gethostbyname() or attempt a coup-de-tat
in your local country.
The function inet_ntoa() converts a network address in a struct
in_addr to a dots-and-numbers format string. The "n" in "ntoa" stands for
network, and the "a" stands for ASCII for historical reasons (so it's "Network
To ASCII"--the "toa" suffix has an analogous friend in the C library called
atoi() which converts an ASCII string to an integer.)
The function inet_aton() is the opposite, converting from a
dots-and-numbers string into a in_addr_t (which is the type of the
field s_addr in your struct in_addr.)
Finally, the function inet_addr() is an older function that does
basically the same thing as inet_aton(). It's theoretically deprecated,
but you'll see it alot and the police won't come get you if you use
it.
- Return Value
-
inet_aton() returns non-zero if the address is a valid one, and it
returns zero if the address is invalid.
inet_ntoa() returns the dots-and-numbers string in a static buffer
that is overwritten with each call to the function.
inet_addr() returns the address as an in_addr_t, or
-1 if there's an error. (That is the same result as if you tried
to convert the string "255.255.255.255", which is a valid IP address. This is
why inet_aton() is better.)
- Example
-
struct sockaddr_in antelope; char *some_addr;
inet_aton("10.0.0.1", &antelope.sin_addr); // store IP in antelope
some_addr = inet_ntoa(antelope.sin_addr); // return the IP printf("%s
", some_addr); // prints "10.0.0.1"
// and this call is the same as the inet_aton() call, above: antelope.sin_addr.s_addr = inet_addr("10.0.0.1");
|
- See Also
-
gethostbyname(), gethostbyaddr()
Tell a socket to listen for incoming connections
- Prototypes
-
#include
int listen(int s, int backlog);
- Description
-
You can take your socket descriptor (made with the socket() system
call) and tell it to listen for incoming connections. This is what
differentiates the servers from the clients, guys.
The backlog parameter can mean a couple different things
depending on the system you on, but loosely it is how many pending connections
you can have before the kernel starts rejecting new ones. So as the new
connections come in, you should be quick to accept() them so that the
backlog doesn't fill. Try setting it to 10 or so, and if your clients start
getting "Connection refused" under heavy load, set it higher.
Before calling listen(), your server should call bind() to
attach itself to a specific port number. That port number (on the server's IP
address) will be the one that clients connect to.
- Return Value
-
Returns zero on success, or -1 on error (and errno
will be set accordingly.)
- Example
-
int s; struct sockaddr_in myaddr;
myaddr.sin_family = AF_INET; myaddr.sin_port = htons(3490); // clients connect to this port myaddr.sin_addr.s_addr = INADDR_ANY; // autoselect IP address
s = socket(PF_INET, SOCK_STREAM, 0); bind(s, (struct sockaddr*)myaddr, sizeof(myaddr));
listen(s, 10); // set s up to be a server (listening) socket
// then have an accept() loop down here somewhere
|
- See Also
-
accept(), bind(), socket()
Print an error as a human-readable string
- Prototypes
-
#include
void perror(const char *s);
#include
char *strerror(int errnum);
- Description
-
Since so many functions return -1 on error and set the value
of the variable errno to be some number, it would sure be nice
if you could easily print that in a form that made sense to you.
Mercifully, perror() does that. If you want more description to be
printed before the error, you can point the parameter s to it
(or you can leave s as NULL and nothing
additional will be printed.)
In a nutshell, this function takes errno values, like
ECONNRESET, and prints them nicely, like "Connection reset by
peer."
The function strerror() is very similar to perror(), except
it returns a pointer to the error message string for a given value (you usually
pass in the variable errno.)
- Return Value
-
strerror() returns a pointer to the error message
string.
- Example
-
int s;
s = socket(PF_INET, SOCK_STREAM, 0);
if (s == -1) { // some error has occurred // prints "socket error: " + the error message: perror("socket error"); }
// similarly: if (listen(s, 10) == -1) { // this prints "an error: " + the error message from errno: printf("an error: %s
", strerror(errno)); }
|
- See Also
-
errno
|
|