Chinaunix首页 | 论坛 | 博客
  • 博客访问: 310617
  • 博文数量: 94
  • 博客积分: 2220
  • 博客等级: 大尉
  • 技术积分: 975
  • 用 户 组: 普通用户
  • 注册时间: 2004-12-17 21:17
文章分类

全部博文(94)

文章存档

2011年(5)

2010年(11)

2009年(1)

2008年(2)

2006年(1)

2005年(65)

2004年(9)

我的朋友

分类: 系统运维

2005-10-11 23:42:12


7.

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:

$ netstat

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.

  1. server reads data from file (or whereever)
  2. server encrypts data (you add this part)
  3. server send()s encrypted data

Now the other way around:

  1. client recv()s encrypted data
  2. client decrypts data (you add this part)
  3. 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:

  1. connect() to server
  2. send("/sbin/ls > /tmp/client.out")
  3. close() the connection

Meanwhile, the server is handling the data and executing it:

  1. accept() the connection from the client
  2. recv(str) the command string
  3. close() the connection
  4. 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 .

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. ;-)



8.

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 " instead of "66.94.230.35".

gethostbyname() takes a string like ", 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

he = gethostbyname(");
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


阅读(1746) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~