发布时间:2014-10-25 2:20:11

  尝试下原文对照的排版方式,不知道大家习惯于哪种       作者: Gordon McMillan  Abstract 摘要   Sockets are used nearly everywhere, but are one of the most severely misunderstood technologies around.  This is a 10,000 foot overview of sockets.  It’s not really a tutorial - you’ll still have work to do in getting things operational.  It doesn’t cover the fine points (and there are a lot of them),  but I hope it will give you enough background to begin using them decently.     Sockets几乎无处不在,但也是最让人误解的技术之一。这篇文章仅仅作为概览,他不是一篇真正的教程,因为要让他运行起来你还有其他工作要做。 它不会描述细节,但我希望文章能给你足够背景知识去使用它们。   Sockets (通常翻译为套接字) I’m only going to talk about INET sockets, but they account for at least 99% of the sockets in use. And I’ll only talk about STREAM sockets - unless you really know what you’re doing (in which case this HOWTO isn’t for you!), you’ll get better behavior and performance from a STREAM socket than anything else. I will try to clear up the mystery of what a socket is, as well as some hints on how to work with blocking and non-blocking sockets. But I’ll start by talking about blocking sockets. You’ll need to know how they work before dealing with non-blocking sockets.   我只想谈谈INET sockets,但仅此已经覆盖了将近99%的使用范围。 我只谈下STREAM sockets,你使用STREAM socket将比其他方式都好,其他方式除非你真的知道你在干什么(这篇文章没有涉及)。   我会尝试揭去socket的神秘面纱,所以将给出提示怎样使用好阻塞和非阻塞的socket。但我从阻塞socket谈起。你在进行非阻塞操作时需要知道它们是如何工作的。   Part of the trouble with understanding these things is that “socket” can mean a number of subtly different things, depending on context. So first, let’s make a distinction between a “client” socket - an endpoint of a conversation, and a “server” socket, which is more like a switchboard operator. The client application (your browser, for example) uses “client” sockets exclusively; the web server it’s talking to uses both “server” sockets and “client” sockets.   这些东西之所以理解起来有困难是因为"socket"可以代表不同的东西,需要联系上下文理解。所以我们首先把客户端socket(连接端点)和服务端socket(接线总机)区分开来。客户端应用程序(例如浏览器)只使用客户端socket("client"socket),服务端将既使用服务端socket也使用客户端socket。     History 历史 各种形式的IPC (进程间通信)中,sockets是目前最流行的。在任何特定的平台,有可能其他形式的IPC是最快的,但对跨平台的通信,sockets将是唯一的。   他们由Berkeley作为BSD的一部分而发明。在互联网中传播得如野火般迅速。这是因为结合socket和Inet使得连接世界各地的任意机器难以置信的容易(至少相对于其他方式) 。   Creating a Socket 创建一个socket Roughly speaking, when you clicked on the link that brought you to this page, your browser did something like the following:   总体来说,当你点了这篇文章的连接来到这个页面。你的浏览器做了如下工作:   #创建一个INET,steam socket s = socket.socket( socket.AF_INET, socket.SOCK_STREAM) #现在通过80端口(HTTP的标准端口)连接到服务器 s.connect(("www.mcmillan-inc.com", 80))     When the connect completes, the socket s can now be used to send in a request for the text of this page. The same socket will read the reply, and then be destroyed. That’s right - destroyed. Client sockets are normally only used for one exchange (or a small set of sequential exchanges).   What happens in the web server is a bit more complex. First, the web server creates a “server    ,socket”.   当连接完成,socket就可以用来发送请求给服务器,要求返回页面的文本。同样的socket会读到应答,然后销毁。是的,会销毁。客户端sockets通常仅用来做一个通信(或者是一小部分的通信)。   服务端则有点复杂。网站服务器创建了一个服务端socket。   #创建一个INET,steam socket serversocket = socket.socket( socket.AF_INET, socket.SOCK_STREAM) #绑定到公开服务器和端口, serversocket.bind((socket.gethostname(), 80)) #成为服务端socket serversocket.listen(5)      A couple things to notice: we used socket.gethostname() so that the socket would be visible to the outside world. If we had used s.bind(('', 80)) or s.bind(('localhost', 80)) or s.bind(('', 80)) we would still have a “server” socket, but one that was only visible within the same machine.   A second thing to note: low number ports are usually reserved for “well known” services (HTTP, SNMP etc). If you’re playing around, use a nice high number (4 digits).   Finally, the argument to listen tells the socket library that we want it to queue up as many as 5 connect requests (the normal max) before refusing outside connections. If the rest of the code is written properly, that should be plenty.   OK, now we have a “server” socket, listening on port 80. Now we enter the mainloop of the web server:   两点要注意:我们使用socket.gethostname(),所以那个socket会被外面的世界所知道。如果我们使用s.bind(('', 80)),s.bind(('localhost', 80)) 或者 s.bind(('', 80)),我们同样有一个服务端socket,但仅仅在本机能看见。   第二个注意的是,低端端口通常保留作为众所周知的服务(如HTTP,SNMP等)。如果你是其他使用,用比较大的号码(四位数以上)。 最后,listen的参数告诉socket库,我们只希望保留5个连接请求在队列(通常的最大值)。如果余下的代码都写上,将有很多代码。   好了,现在我们有了在80端口监听的服务端socket,我们将进入服务器主循环。 while 1: #接受外部请求 (clientsocket, address) = serversocket.accept() #对客户端clientsocket做些操作 #在这里我们假设我们在线程中使用 ct = client_thread(clientsocket) ct.run()      There’s actually 3 general ways in which this loop could work - dispatching a thread to handle clientsocket, create a new process to handle clientsocket, or restructure this app to use non-blocking sockets, and multiplex between our “server” socket and any active clientsockets using select. More about that later. The important thing to understand now is this: this is all a “server” socket does. It doesn’t send any data. It doesn’t receive any data. It just produces “client” sockets. Each clientsocket is created in response to some other “client” socket doing a connect() to the host and port we’re bound to. As soon as we’ve created that clientsocket, we go back to listening for more connections. The two “clients” are free to chat it up - they are using some dynamically allocated port which will be recycled when the conversation ends.   这个循环通常有三种工作方式,使用线程来处理clientsocket,创建新的进程来处理clientsocket,或者重新构建这个程序使用非阻塞socket,和使用select的多工方式。更多的以后再讲。现在重要的是理解:这是服务端socket所有要做的。他不发送任何数据。他不接收任何数据。他仅仅产生客户端socket。每个客户端clientsocket被创建来应答客户端对主机和端口的连接动作。当创建完成后我们就立即去监听其他连接。两个客户端就可以畅所欲言了。它们使用动态分配的端口在对话完成后将被回收。   IPC 进程间通信 如果您需要快速的IPC同一台机器两个进程之间的通信,你应该考虑他提供了什么形式的共享存储机制。一个简单的协议基于共享内存和锁或信号灯是目前速度最快的技术。    如果您决定使用socket,绑定服务器socket到“localhost” 。在大多数平台上,这将在两者的网络层间建立一条捷径,是会比较快的。   Using a Socket 使用socket The first thing to note, is that the web browser’s “client” socket and the web server’s “client” socket are identical beasts. That is, this is a “peer to peer” conversation. Or to put it another way, as the designer, you will have to decide what the rules of etiquette are for a conversation. Normally, the connecting socket starts the conversation, by sending in a request, or perhaps a signon. But that’s a design decision - it’s not a rule of sockets.   首先要注意的是,网页客户端的clientsocket和服务端的clientsocket是等同的。这是一个“端到端”连接。或者另外的说法是,作为设计者,你需要定义它们沟通的方式。通常,连接的socket开始通信,发送一个请求,或是一个登陆。但那是设计决定的,而非sockets的规则。     Now there are two sets of verbs to use for communication. You can use send and recv, or you can transform your client socket into a file-like beast and use read and write. The latter is the way Java presents their sockets. I’m not going to talk about it here, except to warn you that you need to use flush on sockets. These are buffered “files”, and a common mistake is to write something, and then read for a reply. Without a flush in there, you may wait forever for the reply, because the request may still be in your output buffer.   现在有两套动作使用通信。您可以使用发送和接收,或者您可以转换您的客户端套接字到一个文件,就像使用文件方式的读取和写入。后者是Java实现socket的方式。在这里我不想谈论它,除了提醒你,你需要在socket上使用flush。这些都是缓冲“文件” ,并且通常易犯的一个错误是写一些东西,然后立即读应答。没有使用flush,你将永远在等待答复,因为请求仍可能在您的输出缓冲器中。     Now we come the major stumbling block of sockets - send and recv operate on the network buffers. They do not necessarily handle all the bytes you hand them (or expect from them), because their major focus is handling the network buffers. In general, they return when the associated network buffers have been filled (send) or emptied (recv). They then tell you how many bytes they handled. It is your responsibility to call them again until your message has been completely dealt with.   现在,我们来到socket的主要难点。在网络上的运作缓冲器的发送和接收。他们不一定处理所有你发给他们的字节(或期望从他们读到的字节),因为他们的主要重点是处理网络缓冲器。一般情况下,他们在相关的网络缓冲器已填补(发送)或掏空(接收)时返回。然后他们告诉你处理了多少字节。重复调用它们,直到您的消息已被完全处理将是你的责任。   When a recv returns 0 bytes, it means the other side has closed (or is in the process of closing) the connection. You will not receive any more data on this connection. Ever. You may be able to send data successfully; I’ll talk about that some on the next page.   当recv返回0字节时,他表示对方已关闭(或者在关闭当中)连接。你不会在此连接上接收到数据,但仍可能可以成功发送。下一页将讨论这个内容呢。   A protocol like HTTP uses a socket for only one transfer. The client sends a request, the reads a reply. That’s it. The socket is discarded. This means that a client can detect the end of the reply by receiving 0 bytes.   一个协议像HTTP使用socket进行一次传送。客户端发送一个请求,服务端处理一个请求。就这样。套接字就丢弃。这意味着,客户端可以通过接收0字节检测到已经到了尾部。     But if you plan to reuse your socket for further transfers, you need to realize that there is no “EOT” (End of Transfer) on a socket. I repeat: if a socket send or recv returns after handling 0 bytes, the connection has been broken. If the connection has not been broken, you may wait on a recv forever, because the socket will not tell you that there’s nothing more to read (for now). Now if you think about that a bit, you’ll come to realize a fundamental truth of sockets: messages must either be fixed length (yuck), or be delimited (shrug), or indicate how long they are (much better), or end by shutting down the connection. The choice is entirely yours, (but some ways are righter than others).   但是,如果您计划重用套接字进一步传送,你必须认识到,socket上没有任何“ EOT” (End of Transfer)。我再重复一遍:如果套接字发送或接收后返回0字节,连接已损坏。如果连接没损坏,你将永远在recv上等待,因为socket不会告诉你,没有什么更多阅读(目前如此)。现在想想这一点,你将对socket有个基础认识:信息必须是固定长度(yuck),或使用分隔(变长),或指定它们长度(更好),最后,关闭连接。选择权完全取决于您,(但有些方法比其他的好)。   Assuming you don’t want to end the connection, the simplest solution is a fixed length message:   假设你不希望断开连接,最简单的方案是一个固定长度信息。   class mysocket: '''demonstration class only - coded for clarity, not efficiency ''' def __init__(self, sock=None): if sock is None: self.sock = socket.socket( socket.AF_INET, socket.SOCK_STREAM) else: self.sock = sock def connect(self, host, port): self.sock.connect((host, port)) def mysend(self, msg): totalsent = 0 while totalsent < MSGLEN: sent = self.sock.send(msg[totalsent:]) if sent == 0: raise RuntimeError, / "socket connection broken" totalsent = totalsent + sent def myreceive(self): msg = '' while len(msg) < MSGLEN: chunk = self.sock.recv(MSGLEN-len(msg)) if chunk == '': raise RuntimeError, / "socket connection broken" msg = msg + chunk return msg        The sending code here is usable for almost any messaging scheme - in Python you send strings, and you can use len() to determine its length (even if it has embedded /0 characters). It’s mostly the receiving code that gets more complex. (And in C, it’s not much worse, except you can’t use strlen if the message has embedded /0s.)   发送代码是可用于几乎任何通讯-在Python里您发送字符串,您可以使用len(),以确定其长度(即使包含/0字符)。接收代码比较复杂。(与C语言中,这不是非常坏,除了你不能使用strlen如果信息包含/0。)   The easiest enhancement is to make the first character of the message an indicator of message type, and have the type determine the length. Now you have two recvs - the first to get (at least) that first character so you can look up the length, and the second in a loop to get the rest. If you decide to go the delimited route, you’ll be receiving in some arbitrary chunk size, (4096 or 8192 is frequently a good match for network buffer sizes), and scanning what you’ve received for a delimiter.   最简单的增强是使信息的第1个字符的作为一个指标指明信息类型,并有确定类型的长度。现在你有两个recvs -第一次获得(至少)第一个字符,因此您可以了解长度,第二个在循环中得到剩余部分。如果您决定走分隔符路线,你会得到一些任意块大小块(为匹配网络缓冲区大小4096或8192常常是一个很好的大小)并扫描您所收到的分隔符。     One complication to be aware of: if your conversational protocol allows multiple messages to be sent back to back (without some kind of reply), and you pass recv an arbitrary chunk size, you may end up reading the start of a following message. You’ll need to put that aside and hold onto it, until it’s needed.   其中的一个难点需要意识到:如果您的会话协议允许多个信息发送连续发送(没有某种形式的应答),并且您通过接收任意块大小,您可能会读到连续的消息。您需要就把这些信息放到一边,并整理它,直到被采用。   Prefixing the message with it’s length (say, as 5 numeric characters) gets more complex, because (believe it or not), you may not get all 5 characters in one recv. In playing around, you’ll get away with it; but in high network loads, your code will very quickly break unless you use two recv loops - the first to determine the length, the second to get the data part of the message. Nasty. This is also when you’ll discover that send does not always manage to get rid of everything in one pass. And despite having read this, you will eventually get bit by it!   在信息前附带一个长度头(就像5个字符的数字)更加复杂,因为(信不信由你),你可能不会在一次recv中得到5个字符。为了持续运行,你或许会忽略它,但在高网络负载下,您的代码将很快崩溃,除非您使用两个接收循环-第一,以确定长度,第二次获得的数据部分的信息。讨厌的是,您会发现,发送并不总是在一次处理所有事。有了这章内容,您将知道如何应付它!   In the interests of space, building your character, (and preserving my competitive position), these enhancements are left as an exercise for the reader. Lets move on to cleaning up.   在你的兴趣范围内,为了建立你的特点,(保留我的竞争地位),这些留给读者作为练习。让我们继续清理。   Binary Data 二进制数据   It is perfectly possible to send binary data over a socket. The major problem is that not all machines use the same formats for binary data. For example, a Motorola chip will represent a 16 bit integer with the value 1 as the two hex bytes 00 01. Intel and DEC, however, are byte-reversed - that same 1 is 01 00. Socket libraries have calls for converting 16 and 32 bit integers - ntohl, htonl, ntohs, htons where “n” means network and “h” means host, “s” means short and “l” means long. Where network order is host order, these do nothing, but where the machine is byte-reversed, these swap the bytes around appropriately.   完全有可能在套接字上发送二进制数据。主要问题是,并非所有机器使用相同的格式的二进制数据。例如,摩托罗拉公司的芯片将是16位整数的值1用两个十六进制字节00 01表示。英特尔和DEC则是字节反序-同样的1 用01 00表示。socket库要求转换为16和32位整型- ntohl,htonl,ntohs,htons,字母“ N ”是指网络和“h”是指主机,“S”表示短型,“L”是指长整。如果网络字节序和主机字节序一致,什么也不做,但在机器字节反序的话,这些交换字节序到恰当顺序。   In these days of 32 bit machines, the ascii representation of binary data is frequently smaller than the binary representation. That’s because a surprising amount of the time, all those longs have the value 0, or maybe 1. The string “0” would be two bytes, while binary is four. Of course, this doesn’t fit well with fixed-length messages. Decisions, decisions.   在今天的32位机器里,在ASCII表示的二进制数据往往小于二进制表示法。这是因为大部分的时间,所有这些长度值为0,或者1。字符串“ 0 ”将是两个字节,而二进制是四个。当然,这并不十分符合固定长度的消息。要抉择啊。   Disconnecting 断开连接   Strictly speaking, you’re supposed to use shutdown on a socket before you close it. The shutdown is an advisory to the socket at the other end. Depending on the argument you pass it, it can mean “I’m not going to send anymore, but I’ll still listen”, or “I’m not listening, good riddance!”. Most socket libraries, however, are so used to programmers neglecting to use this piece of etiquette that normally a close is the same as shutdown(); close(). So in most situations, an explicit shutdown is not needed.   严格地说,在socket上使用shutdown,然后将其close。shutdown咨询另一端。根据给它的不同参数,它可能意味着“我不会发送了,但我还是会监听” ,或者“我不听了,可以卸掉了!”。尽管如此,大部分socket库,让程序员忽视使用这个函数,认为close和shutdown是一样的。因此,在大多数情况下,一个明确的关机是没有必要。   One way to use shutdown effectively is in an HTTP-like exchange. The client sends a request and then does a shutdown(1). This tells the server “This client is done sending, but can still receive.” The server can detect “EOF” by a receive of 0 bytes. It can assume it has the complete request. The server sends a reply. If the send completes successfully then, indeed, the client was still receiving.   其中HTTP这种通信是高效使用shutdown的一个方式。客户端发送请求,然后做了shutdown(1)。这就告诉服务器“这个客户端发送完毕,但仍然在接收。”服务器可以通过接收0字节检测到“EOF”。它可以认为它完成了请求。服务器发出了一个答复。实际上发送成功后,实际上,客户端仍然接受。   Python takes the automatic shutdown a step further, and says that when a socket is garbage collected, it will automatically do a close if it’s needed. But relying on this is a very bad habit. If your socket just disappears without doing a close, the socket at the other end may hang indefinitely, thinking you’re just being slow. Please close your sockets when you’re done.   Python使用自动shutdown又向前迈进了一步,并表示,当一个套接字是垃圾收集时,如果需要它会自动进行关闭。但是,依靠这个是一个很坏的习惯。如果您的socket不关闭而不用,socket的另一端可能会挂起,认为你仅仅是缓慢,当您完成时请关闭sockets。   When Sockets Die 当Sockets死亡时   Probably the worst thing about using blocking sockets is what happens when the other side comes down hard (without doing a close). Your socket is likely to hang. SOCKSTREAM is a reliable protocol, and it will wait a long, long time before giving up on a connection. If you’re using threads, the entire thread is essentially dead. There’s not much you can do about it. As long as you aren’t doing something dumb, like holding a lock while doing a blocking read, the thread isn’t really consuming much in the way of resources. Do not try to kill the thread - part of the reason that threads are more efficient than processes is that they avoid the overhead associated with the automatic recycling of resources. In other words, if you do manage to kill the thread, your whole process is likely to be screwed up.   也许是最坏的事情有关使用阻塞socket是当对方端硬性停下时(没有使用close)。您的socket有可能挂起。 SOCKSTREAM是一个可靠的协议,它将等待很久很久之后才放弃一个连接。如果您使用的是线程,整个线程基本上是死了。没有什么可以做的。只要你不是做一些愚蠢的事,如加锁,而做了阻塞阅读,线程是不会真正消耗大量的资源的。不要试图杀死线程-部分原因是线程比进程更有效的是,他们避免间接自动回收资源的相关开销。换句话说,如果你能杀死线程,您的整个进程很可能就如醉了一般。     Non-blocking Sockets 非阻塞socket   If you’ve understood the preceeding, you already know most of what you need to know about the mechanics of using sockets. You’ll still use the same calls, in much the same ways. It’s just that, if you do it right, your app will be almost inside-out.   如果您已经理解前面内容,你已经知道大部分你所需要了解关于使用socket的内容。您将可以以大致相同的方式使用同样的calls。这只是说,如果你是做的正确,您的应用程序将运行良好。   In Python, you use socket.setblocking(0) to make it non-blocking. In C, it’s more complex, (for one thing, you’ll need to choose between the BSD flavor O_NONBLOCK and the almost indistinguishable Posix flavor O_NDELAY, which is completely different from TCP_NODELAY), but it’s the exact same idea. You do this after creating the socket, but before using it. (Actually, if you’re nuts, you can switch back and forth.)   在Python中,您可以使用socket.setblocking(0),使其非阻塞。C语言中,它更复杂,(举例来说,您需要在BSD风格的O_NONBLOCK和Posix风格的O_NDELAY之间作出选择, 这又完全不同于TCP_NODELAY ),但一样的做法是。您在socket创建后使用它。(事实上,你可以在它们间来回切换。)     The major mechanical difference is that send, recv, connect and accept can return without having done anything. You have (of course) a number of choices. You can check return code and error codes and generally drive yourself crazy. If you don’t believe me, try it sometime. Your app will grow large, buggy and suck CPU. So let’s skip the brain-dead solutions and do it right.   主要不同的是send, recv, connect and accept可以立即返回,而不必做任何事。您(当然)有几个选择。您可以检查返回代码和错误代码,一般会让自己疯掉。如果你不相信我,试试看。您的应用程序将增大,易错并且消耗CPU。所以让我们不会脑死的解决办法就是方法正确。   Use select. 使用select   In C, coding select is fairly complex. In Python, it’s a piece of cake, but it’s close enough to the C version that if you understand select in Python, you’ll have little trouble with it in C.   在C语言里,用select编码是相当复杂。在Pyghon,这不成问题,但如果你掌握了Python里的select,将和C版本很接近,你在C中将不会有什么麻烦。   ready_to_read, ready_to_write, in_error = / select.select( potential_readers, potential_writers, potential_errs, timeout)      You pass select three lists: the first contains all sockets that you might want to try reading; the second all the sockets you might want to try writing to, and the last (normally left empty) those that you want to check for errors. You should note that a socket can go into more than one list. The select call is blocking, but you can give it a timeout. This is generally a sensible thing to do - give it a nice long timeout (say a minute) unless you have good reason to do otherwise.   您通过select三个列表:第一包含所有尝试阅读的socket;第二包含所有的尝试写socket,最后一个(通常是空的)包含你要检查错误的socket。你应该注意到,socket可以进入一个以上列表。select call是阻塞的,但是你可以给它一个超时。这通常是明智之举-给它一个好的长期超时(比如一分钟),除非你有很好的理由做其他选择。   In return, you will get three lists. They have the sockets that are actually readable, writable and in error. Each of these lists is a subset (possibly empty) of the corresponding list you passed in. And if you put a socket in more than one input list, it will only be (at most) in one output list.   作为返回,你会得到三个列表。他们包含具有可读,可写,有错的socket。所有这些列表是你传入相应的列表的一个子集(可能是空的)如果你把socket插在一个以上的输入列表中,它只会(最多)在一个输出列表里。     If a socket is in the output readable list, you can be as-close-to-certain-as-we-ever-get-in-this-business that a recv on that socket will return something. Same idea for the writable list. You’ll be able to send something. Maybe not all you want to, but something is better than nothing. (Actually, any reasonably healthy socket will return as writable - it just means outbound network buffer space is available.)     如果socket在可读列表,你可以确认recv有数据返回,对于可写列表,你可以发送了。不是所有的都是你要的,但有总比没有好。您可以发送。(其实,任何正确的套接字将返回可写的-它只是说明网络缓冲区空间可用。 )     If you have a “server” socket, put it in the potential_readers list. If it comes out in the readable list, your accept will (almost certainly) work. If you have created a new socket to connect to someone else, put it in the potential_writers list. If it shows up in the writable list, you have a decent chance that it has connected.   如果你有一个“服务器”socket,把它放在potential_readers列表。如果返回的可读列表,您accept的话将可以(几乎可以肯定)工作。如果您已经创建了一个新的套接字连接到别人,把它放在potential_writers列表。如果它出现在可写列表,可以确认它已经连接。     One very nasty problem with select: if somewhere in those input lists of sockets is one which has died a nasty death, the select will fail. You then need to loop through every single damn socket in all those lists and do a select([sock],[],[],0) until you find the bad one. That timeout of 0 means it won’t take long, but it’s ugly.     使用select有一个非常讨厌的问题:如果在输入列表的socket是一个已经销毁的,select将失败。然后,您需要循环的在所有列表使用select([sock],[],[],0),直到找到销毁的socket。超时为0意味着它不会需要很长时间,但它很蹩脚。     Actually, select can be handy even with blocking sockets. It’s one way of determining whether you will block - the socket returns as readable when there’s something in the buffers. However, this still doesn’t help with the problem of determining whether the other end is done, or just busy with something else.     其实,select甚至可以处理阻塞sockets。这是确定您是否将阻塞的方法之一,如果在缓冲器有内容,socket将返回可读。然而,这仍然无助于决定是否另一端已完成,还是忙于别的东西。     Portability alert: On Unix, select works both with the sockets and files. Don’t try this on Windows. On Windows, select works with sockets only. Also note that in C, many of the more advanced socket options are done differently on Windows. In fact, on Windows I usually use threads (which work very, very well) with my sockets. Face it, if you want any kind of performance, your code will look very different on Windows than on Unix.   可移植性提醒:在Unix,select在socket和文件中工作。不要尝试在Windows上以这种方式运行。在Windows中,select只用在socket上。在C语言还应注意到,许多socket高级选项在Windows操作系统上的实现有所不同。事实上,在Windows上我通常用线程(这工作得非常好)运行socket。必须面对的是,在Windows的很多实现都与Unix大不相同。     Performance 性能   There’s no question that the fastest sockets code uses non-blocking sockets and select to multiplex them. You can put together something that will saturate a LAN connection without putting any strain on the CPU. The trouble is that an app written this way can’t do much of anything else - it needs to be ready to shuffle bytes around at all times.   毫无疑问,速度最快的socket应用应该是非阻塞socket与select的配合使用。你可以把一些将占用网络资源的操作放到一起而不占用CPU资源。问题是,这样写的程序不能够做其他事,他必须一直在那交换数据。     Assuming that your app is actually supposed to do something more than that, threading is the optimal solution, (and using non-blocking sockets will be faster than using blocking sockets). Unfortunately, threading support in Unixes varies both in API and quality. So the normal Unix solution is to fork a subprocess to deal with each connection. The overhead for this is significant (and don’t do this on Windows - the overhead of process creation is enormous there). It also means that unless each subprocess is completely independent, you’ll need to use another form of IPC, say a pipe, or shared memory and semaphores, to communicate between the parent and child processes.     如果你的程序需要做其他事,线程是最佳的解决方案,(使用非阻塞套接字将快于使用阻塞套接字)。不幸的是,在Unix中多线程的支持的在API和质量上都有不同。因此,正常的Unix的解决方案是建立子进程来处理每个连接。这个开销很可观(不要在WINDOWS这样做,Windows进程开销更加巨大)。这也意味着,每个子进程是完全独立的,您需要使用的另一种形式的IPC--管道,共享内存或信号灯,来沟通与父进程与子进程。   Finally, remember that even though blocking sockets are somewhat slower than non-blocking, in many cases they are the “right” solution. After all, if your app is driven by the data it receives over a socket, there’s not much sense in complicating the logic just so your app can wait on select instead of recv.     最后,请记住,即使阻塞socket比非阻塞慢,在许多情况下他们是“正确”的解决办法。毕竟,如果您的应用程序是由来源于socket的数据驱动,现在并没有很多复杂逻辑的应用,需要您的应用程序可以在select上等待而不是接收。