📄 26.htm
字号:
<br>
<br>
4.10. How should I choose a port number for my server? <br>
The list of registered port assignments can be found in STD 2 or RFC <br>
1700. Choose one that isn't already registered, and isn't in <br>
/etc/services on your system. It is also a good idea to let users <br>
customize the port number in case of conflicts with other un- <br>
registered port numbers in other servers. The best way of doing this <br>
is hardcoding a service name, and using getservbyname() to lookup the <br>
actual port number. This method allows users to change the port your <br>
server binds to by simply editing the /etc/services file. <br>
<br>
4.11. What is the difference between SO_REUSEADDR and SO_REUSEPORT? <br>
SO_REUSEADDR allows your server to bind to an address which is in a <br>
TIME_WAIT state. It does not allow more than one server to bind to <br>
the same address. It was mentioned that use of this flag can create a <br>
security risk because another server can bind to a the same port, by <br>
binding to a specific address as opposed to INADDR_ANY. The <br>
SO_REUSEPORT flag allows multiple processes to bind to the same <br>
address provided all of them use the SO_REUSEPORT option. <br>
From Richard Stevens (rstevens@noao.edu): <br>
This is a newer flag that appeared in the 4.4BSD multicasting code <br>
(although that code was from elsewhere, so I am not sure just who <br>
invented the new SO_REUSEPORT flag). <br>
What this flag lets you do is rebind a port that is already in use, <br>
but only if all users of the port specify the flag. I believe the <br>
intent is for multicasting apps, since if you're running the same app <br>
on a host, all need to bind the same port. But the flag may have <br>
other uses. For example the following is from a post in February: <br>
From Stu Friedberg (stuartf@sequent.com): <br>
SO_REUSEPORT is also useful for eliminating the <br>
try-10-times-to-bind hack in ftpd's data connection setup <br>
routine. Without SO_REUSEPORT, only one ftpd thread can <br>
bind to TCP (lhost, lport, INADDR_ANY, 0) in preparation for <br>
connecting back to the client. Under conditions of heavy <br>
load, there are more threads colliding here than the <br>
try-10-times hack can accomodate. With SO_REUSEPORT, things <br>
work nicely and the hack becomes unnecessary. <br>
I have also heard that DEC OSF supports the flag. Also note that <br>
under 4.4BSD, if you are binding a multicast address, then <br>
SO_REUSEADDR is condisered the same as SO_REUSEPORT (p. 731 of "TCP/IP <br>
Illustrated, Volume 2"). I think under Solaris you just replace <br>
SO_REUSEPORT with SO_REUSEADDR. <br>
From a later Stevens posting, with minor editing: <br>
Basically SO_REUSEPORT is a BSD'ism that arose when multicasting was <br>
added, even thought it was not used in the original Steve Deering <br>
code. I believe some BSD-derived systems may also include it (OSF, <br>
now Digital Unix, perhaps?). SO_REUSEPORT lets you bind the same <br>
address *and* port, but only if all the binders have specified it. <br>
But when binding a multicast address (its main use), SO_REUSEADDR is <br>
considered identical to SO_REUSEPORT (p. 731, "TCP/IP Illustrated, <br>
Volume 2"). So for portability of multicasting applications I always <br>
use SO_REUSEADDR. <br>
<br>
4.12. How can I write a multi-homed server? <br>
The original question was actually from Shankar Ramamoorthy <br>
(shankar@viman.com): <br>
I want to run a server on a multi-homed host. The host is <br>
part of two networks and has two ethernet cards. I want to <br>
run a server on this machine, binding to a pre-determined <br>
port number. I want clients on either subnet to be able to <br>
send broadcast packates to the port and have the server <br>
receive them. <br>
And answered by Andrew Gierth (andrew@erlenstar.demon.co.uk): <br>
Your first question in this scenario is, do you need to know which <br>
subnet the packet came from? I'm not at all sure that this can be <br>
reliably determined in all cases. <br>
If you don't really care, then all you need is one socket bound to <br>
INADDR_ANY. That simplifies things greatly. <br>
If you do care, then you have to bind multiple sockets. You are <br>
obviously attempting to do this in your code as posted, so I'll assume <br>
you do. <br>
I was hoping that something like the following would work. <br>
Will it? This is on Sparcs running Solaris 2.4/2.5. <br>
I don't have access to Solaris, but I'll comment based on my <br>
experience with other Unixes. <br>
[Shankar's original code omitted] <br>
What you are doing is attempting to bind all the current hosts unicast <br>
addresses as listed in hosts/NIS/DNS. This may or may not reflect <br>
reality, but much more importantly, neglects the broadcast addresses. <br>
It seems to be the case in the majority of implementations that a <br>
socket bound to a unicast address will not see incoming packets with <br>
broadcast addresses as their destinations. <br>
The approach I've taken is to use SIOCGIFCONF to retrieve the list of <br>
active network interfaces, and SIOCGIFFLAGS and SIOCGIFBRDADDR to <br>
identify broadcastable interfaces and get the broadcast addresses. <br>
Then I bind to each unicast address, each broadcast address, and to <br>
INADDR_ANY as well. That last is necessary to catch packets that are <br>
on the wire with INADDR_BROADCAST in the destination. (SO_REUSEADDR <br>
is necessary to bind INADDR_ANY as well as the specific addresses.) <br>
This gives me very nearly what I want. The wrinkles are: <br>
o I don't assume that getting a packet through a particular socket <br>
necessarily means that it actually arrived on that interface. <br>
o I can't tell anything about which subnet a packet originated on if <br>
its destination was INADDR_BROADCAST. <br>
o On some stacks, apparently only those with multicast support, I get <br>
duplicate incoming messages on the INADDR_ANY socket. <br>
<br>
4.13. How can I read only one character at a time? <br>
This question is usually asked by people who are testing their server <br>
with telnet, and want it to process their keystrokes one character at <br>
a time. The correct technique is to use a psuedo terminal (pty). <br>
More on that in a minute. <br>
According to Roger Espel Llima (espel@drakkar.ens.fr), you can have <br>
your server send a sequence of control characters: 0xff 0xfb 0x01 0xff <br>
0xfb 0x03 0xff 0xfd 0x0f3, which translates to IAC WILL ECHO IAC WILL <br>
SUPPRESS-GO-AHEAD IAC DO SUPPRESS-GO-AHEAD. For more information on <br>
what this means, check out std8, std28 and std29. Roger also gave the <br>
following tips: <br>
o This code will suppress echo, so you'll have to send the characters <br>
the user types back to the client if you want the user to see them. <br>
o Carriage returns will be followed by a null character, so you'll <br>
have to expect them. <br>
o If you get a 0xff, it will be followed by two more characters. <br>
These are telnet escapes. <br>
Use of a pty would also be the correct way to execute a child process <br>
and pass the i/o to a socket. <br>
I'll add pty stuff to the list of example source I'd like to add to <br>
the faq. If someone has some source they'd like to contribute <br>
(without copyright) to the faq which demonstrates use of pty's, please <br>
email me! <br>
<br>
4.14. I'm trying to exec() a program from my server, and attach my <br>
socket's IO to it, but I'm not getting all the data across. Why? <br>
If the program you are running uses printf(), etc (streams from <br>
stdio.h) you have to deal with two buffers. The kernel buffers all <br>
socket IO, and this is explained in ``section 2.11''. The second <br>
buffer is the one that is causing you grief. This is the stdio <br>
buffer, and the problem was well explained by Andrew: <br>
(The short answer to this question is that you want to use a pty <br>
rather than a socket; the remainder of this article is an attempt to <br>
explain why.) <br>
Firstly, the socket buffer controlled by setsockopt() has absolutly <br>
nothing to do with stdio buffering. Setting it to 1 is guaranteed to <br>
be the Wrong Thing(tm). <br>
Perhaps the following diagram might make things a little clearer: <br>
Process A Process B <br>
+---------------------+ +---------------------+ <br>
| | | | <br>
| mainline code | | mainline code | <br>
| | | | ^ | <br>
| v | | | | <br>
| fputc() | | fgetc() | <br>
| | | | ^ | <br>
| v | | | | <br>
| +-----------+ | | +-----------+ | <br>
| | stdio | | | | stdio | | <br>
| | buffer | | | | buffer | | <br>
| +-----------+ | | +-----------+ | <br>
| | | | ^ | <br>
| | | | | | <br>
| write() | | read() | <br>
| | | | | | <br>
+-------- | ----------+ +-------- | ----------+ <br>
| | User space <br>
------------|-------------------------- | --------------------------- <br>
| | Kernel space <br>
v | <br>
+-----------+ +-----------+ <br>
| socket | | socket | <br>
| buffer | | buffer | <br>
+-----------+ +-----------+ <br>
| ^ <br>
v | <br>
(AF- and protocol- (AF- and protocol- <br>
dependent code) dependent code) <br>
Assuming these two processes are communicating with each other (I've <br>
deliberately omitted the actual comms mechanisms, which aren't really <br>
relevent), you can see that data written by process A to its stdio <br>
buffer is completely inaccessible to process B. Only once the decision <br>
is made to flush that buffer to the kernel (via write()) can the data <br>
actually be delivered to the other process. <br>
The only guaranteed way to affect the buffering within process A is to <br>
change the code. However, the default buffering for stdout is <br>
controlled by whether the underlying FD refers to a terminal or not; <br>
generally, output to terminals is line-buffered, and output to non- <br>
terminals (including but not limited to files, pipes, sockets, non-tty <br>
devices, etc.) is fully buffered. So the desired effect can usually be <br>
achieved by using a pty device; this, for example, is what the <br>
'expect' program does. <br>
Since the stdio buffer (and the FILE structure, and everything else <br>
related to stdio) is user-level data, it is not preserved across an <br>
exec() call, hence trying to use setvbuf() before the exec is <br>
ineffective. <br>
A couple of alternate solutions were proposed by Roger Espel Llima <br>
(espel@drakkar.ens.fr): <br>
If it's an option, you can use some standalone program that will just <br>
run something inside a pty and buffer its input/output. I've seen a <br>
package by the name pty.tar.gz that did that; you could search around <br>
for it with archie or AltaVista. <br>
Another option (**warning, evil hack**) , if you're on a system that <br>
supports this (SunOS, Solaris, Linux ELF do; I don't know about <br>
others) is to, on your main program, putenv() the name of a shared <br>
executable (*.so) in LD_PRELOAD, and then in that .so redefine some <br>
commonly used libc function that the program you're exec'ing is known <br>
to use early. There you can 'get control' on the running program, and <br>
the first time you get it, do a setbuf(stdout, NULL) on the program's <br>
behalf, and then call the original libc function with a dlopen() + <br>
dlsym(). And you keep the dlsym() value on a static var, so you can <br>
just call that the following times. <br>
(Editors note: I still haven't done an expample for how to do pty's, <br>
but I hope I will be able to do one after I finish the non-blocking <br>
example code.) <br>
-- <br>
※ 来源:·UNIX编程 www.tiaozhan.com/unixbbs/·[FROM: 211.69.197.132] <br>
</small><hr>
<p align="center">[<a href="index.htm">回到开始</a>][<a href="16.htm">上一层</a>][<a href="27.htm">下一篇</a>]
<p align="center"><a href="http://cterm.163.net">欢迎访问Cterm主页</a></p>
</table>
</body>
</html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -