📄 26.htm
字号:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>CTerm非常精华下载</title>
</head>
<body bgcolor="#FFFFFF">
<table border="0" width="100%" cellspacing="0" cellpadding="0" height="577">
<tr><td width="32%" rowspan="3" height="123"><img src="DDl_back.jpg" width="300" height="129" alt="DDl_back.jpg"></td><td width="30%" background="DDl_back2.jpg" height="35"><p align="center"><a href="http://apue.dhs.org"><font face="黑体"><big><big>123</big></big></font></a></td></tr>
<tr>
<td width="68%" background="DDl_back2.jpg" height="44"><big><big><font face="黑体"><p align="center"> ● UNIX网络编程 (BM: clown) </font></big></big></td></tr>
<tr>
<td width="68%" height="44" bgcolor="#000000"><font face="黑体"><big><big><p align="center"></big></big><a href="http://cterm.163.net"><img src="banner.gif" width="400" height="60" alt="banner.gif"border="0"></a></font></td>
</tr>
<tr><td width="100%" colspan="2" height="100" align="center" valign="top"><br><p align="center">[<a href="index.htm">回到开始</a>][<a href="16.htm">上一层</a>][<a href="27.htm">下一篇</a>]
<hr><p align="left"><small>发信人: fion (fion), 信区: UNP <br>
标 题: unix socket faq (5) <br>
发信站: UNIX编程 (2001年07月07日09:46:38 星期六), 站内信件 <br>
<br>
4. Writing Server Applications (TCP/SOCK_STREAM) <br>
<br>
4.1. How come I get "address already in use" from bind()? <br>
You get this when the address is already in use. (Oh, you figured <br>
that much out?) The most common reason for this is that you have <br>
stopped your server, and then re-started it right away. The sockets <br>
that were used by the first incarnation of the server are still <br>
active. This is further explained in ``2.7 Please explain the <br>
TIME_WAIT state.'', and ``2.5 How do I properly close a socket?''. <br>
<br>
4.2. Why don't my sockets close? <br>
When you issue the close() system call, you are closing your interface <br>
to the socket, not the socket itself. It is up to the kernel to close <br>
the socket. Sometimes, for really technical reasons, the socket is <br>
kept alive for a few minutes after you close it. It is normal, for <br>
example for the socket to go into a TIME_WAIT state, on the server <br>
side, for a few minutes. People have reported ranges from 20 seconds <br>
to 4 minutes to me. The official standard says that it should be 4 <br>
minutes. On my Linux system it is about 2 minutes. This is explained <br>
in great detail in ``2.7 Please explain the TIME_WAIT state.''. <br>
<br>
4.3. How can I make my server a daemon? <br>
There are two approaches you can take here. The first is to use inetd <br>
to do all the hard work for you. The second is to do all the hard <br>
work yourself. <br>
If you use inetd, you simply use stdin, stdout, or stderr for your <br>
socket. (These three are all created with dup() from the real socket) <br>
You can use these as you would a socket in your code. The inetd <br>
process will even close the socket for you when you are done. <br>
If you wish to write your own server, there is a detailed explanation <br>
in "Unix Network Programming" by Richard Stevens (see ``1.5 Where can <br>
I get source code for the book [book title]?''). I also picked up <br>
this posting from comp.unix.programmer, by Nikhil Nair <br>
(nn201@cus.cam.ac.uk). You may want to add code to ignore SIGPIPE, <br>
because if this signal is not dealt with, it will cause your <br>
application to exit. (Thanks to ingo@milan2.snafu.de for pointing <br>
this out). <br>
I worked all this lot out from the GNU C Library Manual (on-line <br>
documentation). Here's some code I wrote - you can adapt it as necessary: <br>
#include <stdio.h> <br>
#include <stdlib.h> <br>
#include <ctype.h> <br>
#include <unistd.h> <br>
#include <fcntl.h> <br>
#include <signal.h> <br>
#include <sys/wait.h> <br>
/* Global variables */ <br>
volatile sig_atomic_t keep_going = 1; /* controls program termination */ <br>
/* Function prototypes: */ <br>
void termination_handler (int signum); /* clean up before termination */ <br>
int <br>
main (void) <br>
{ <br>
... <br>
if (chdir (HOME_DIR)) /* change to directory containing data <br>
files */ <br>
{ <br>
fprintf (stderr, "`%s': ", HOME_DIR); <br>
perror (NULL); <br>
exit (1); <br>
} <br>
/* Become a daemon: */ <br>
switch (fork ()) <br>
{ <br>
case -1: /* can't fork */ <br>
perror ("fork()"); <br>
exit (3); <br>
case 0: /* child, process becomes a daemon: */ <br>
close (STDIN_FILENO); <br>
close (STDOUT_FILENO); <br>
close (STDERR_FILENO); <br>
if (setsid () == -1) /* request a new session (job control) */ <br>
{ <br>
exit (4); <br>
} <br>
break; <br>
default: /* parent returns to calling process: */ <br>
return 0; <br>
} <br>
/* Establish signal handler to clean up before termination: */ <br>
if (signal (SIGTERM, termination_handler) == SIG_IGN) <br>
signal (SIGTERM, SIG_IGN); <br>
signal (SIGINT, SIG_IGN); <br>
signal (SIGHUP, SIG_IGN); <br>
/* Main program loop */ <br>
while (keep_going) <br>
{ <br>
... <br>
} <br>
return 0; <br>
} <br>
void <br>
termination_handler (int signum) <br>
{ <br>
keep_going = 0; <br>
signal (signum, termination_handler); <br>
} <br>
<br>
4.4. How can I listen on more than one port at a time? <br>
The best way to do this is with the select() call. This tells the <br>
kernel to let you know when a socket is available for use. You can <br>
have one process do i/o with multiple sockets with this call. If you <br>
want to wait for a connect on sockets 4, 6 and 10 you might execute <br>
the following code snippet: <br>
fd_set socklist; <br>
FD_ZERO(&socklist); /* Always clear the structure first. */ <br>
FD_SET(4, &socklist); <br>
FD_SET(6, &socklist); <br>
FD_SET(10, &socklist); <br>
if (select(11, NULL, &socklist, NULL, NULL) < 0) <br>
perror("select"); <br>
The kernel will notify us as soon as a file descriptor which is less <br>
than 11 (the first parameter to select()), and is a member of our <br>
socklist becomes available for writing. See the man page on select() <br>
for more details. <br>
<br>
4.5. What exactly does SO_REUSEADDR do? <br>
This socket option tells the kernel that even if this port is busy (in <br>
the TIME_WAIT state), go ahead and reuse it anyway. If it is busy, <br>
but with another state, you will still get an address already in use <br>
error. It is useful if your server has been shut down, and then <br>
restarted right away while sockets are still active on its port. You <br>
should be aware that if any unexpected data comes in, it may confuse <br>
your server, but while this is possible, it is not likely. <br>
It has been pointed out that "A socket is a 5 tuple (proto, local <br>
addr, local port, remote addr, remote port). SO_REUSEADDR just says <br>
that you can reuse local addresses. The 5 tuple still must be <br>
unique!" by Michael Hunter (mphunter@qnx.com). This is true, and this <br>
is why it is very unlikely that unexpected data will ever be seen by <br>
your server. The danger is that such a 5 tuple is still floating <br>
around on the net, and while it is bouncing around, a new connection <br>
from the same client, on the same system, happens to get the same <br>
remote port. This is explained by Richard Stevens in ``2.7 Please <br>
explain the TIME_WAIT state.''. <br>
<br>
4.6. What exactly does SO_LINGER do? <br>
On some unixes this does nothing. On others, it instructs the kernel <br>
to abort tcp connections instead of closing them properly. This can <br>
be dangerous. If you are not clear on this, see ``2.7 Please explain <br>
the TIME_WAIT state.''. <br>
<br>
4.7. What exactly does SO_KEEPALIVE do? <br>
From Andrew Gierth (andrew@erlenstar.demon.co.uk): <br>
The SO_KEEPALIVE option causes a packet (called a 'keepalive probe') <br>
to be sent to the remote system if a long time (by default, more than <br>
2 hours) passes with no other data being sent or received. This packet <br>
is designed to provoke an ACK response from the peer. This enables <br>
detection of a peer which has become unreachable (e.g. powered off or <br>
disconnected from the net). See ``2.8 Why does it take so long to <br>
detect that the peer died?'' for further discussion. <br>
Note that the figure of 2 hours comes from RFC1122, "Requirements for <br>
Internet Hosts". The precise value should be configurable, but I've <br>
often found this to be difficult. The only implementation I know of <br>
that allows the keepalive interval to be set per-connection is SVR4.2. <br>
<br>
4.8. How can I bind() to a port number < 1024? <br>
From Andrew Gierth (andrew@erlenstar.demon.co.uk): <br>
The restriction on access to ports < 1024 is part of a (fairly weak) <br>
security scheme particular to UNIX. The intention is that servers (for <br>
example rlogind, rshd) can check the port number of the client, and if <br>
it is < 1024, assume the request has been properly authorised at the <br>
client end. <br>
The practical upshot of this, is that binding a port number < 1024 is <br>
reserved to processes having an effective UID == root. <br>
This can, occasionally, itself present a security problem, e.g. when a <br>
server process needs to bind a well-known port, but does not itself <br>
need root access (news servers, for example). This is often solved by <br>
creating a small program which simply binds the socket, then restores <br>
the real userid and exec()s the real server. This program can then be <br>
made setuid root. <br>
<br>
4.9. How do I get my server to find out the client's address / host- <br>
name? <br>
name? <br>
From Andrew Gierth (andrew@erlenstar.demon.co.uk): <br>
After accept()ing a connection, use getpeername() to get the address <br>
of the client. The client's address is of course, also returned on <br>
the accept(), but it is essential to initialise the address-length <br>
parameter before the accept call for this will work. <br>
Jari Kokko (jkokko@cc.hut.fi) has offered the following code to <br>
determine the client address: <br>
int t; <br>
int len; <br>
struct sockaddr_in sin; <br>
struct hostent *host; <br>
len = sizeof sin; <br>
if (getpeername(t, (struct sockaddr *) &sin, &len) < 0) <br>
perror("getpeername"); <br>
else { <br>
if ((host = gethostbyaddr((char *) &sin.sin_addr, <br>
sizeof sin.sin_addr, <br>
AF_INET)) == NULL) <br>
perror("gethostbyaddr"); <br>
else printf("remote host is '%s'\n", host->h_name); <br>
} <br>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -