这里想要讲的就是几个概念,以及它们之间的关系。
概念:端口,进程,套接字描述符,TCP连接
1.TCP连接
TCP通信,是建立在TCP连接之上的。
可以把这个TCP连接,看成是一个管道(我这里对于进程间通信的那个pipe管道还不清楚概念,我这里说的管道就是一般的管道的意思),信息都是从管子的一端传到另一端的。
只要确定好了这个管道,通信就与其他东西无关了。
那么怎么唯一确定这个管道?
UNP1是给出了答案的。在P116,5.16里面有讲到。
TCP连接的套接字对:本地IP+本地端口+外地IP+外地端口
2.问题:套接字描述符相等是不是TCP连接就相同?
答案是否定的。
UNIX将套接字描述符处理为一个int的坏处之一吧,只能通过值来相区别。本来应该是唯一标识一个TCP连接的,却并没有达到这个目的。
我们可以编写一个这样的server:
在bind以后,accpet之前,fork一个子进程。
这个时候有两个server进程,但是它们拥有同一个IP,同一个监听port,并且共享同样的文件描述符。
这个时候,可以先后有两个client来到,都能够被accept连接。
请注意这个时候建立的是两个TCP连接,因为client的port是两个不同port。
但是两个server进程的connfd是相等的。所以套接字描述符相等却是不同的TCP连接。
两个TCP通信是互不影响的,就像两个不同的水管,随便你怎么弄,都不会影响。
3.问题:IP+port+协议能唯一地确定网络中的一个进程吗?
答案是否定的。
1个进程可以使用多个port
比如,编写这样一个client程序,声明许多个套接字描述符,然后分别connect同一个或不同的server,注意到connect会为每个套接字描述符绑定不同的port
那么这个时候这个client进程就是在使用多个port。
1个port可以被多个进程使用
比如,编写这样一个server程序,在accept以后,fork一个子进程,然后再阻塞于echo程序。这个时候,两个进程都使用同一个TCP连接。
因为这个时候本地IP+本地端口+外地IP+外地端口对于两个server进程来说是一样的。
如果你实验一下,你会发现client传过来的信息,一会儿会传给子进程,一会儿会传给父进程。
这是因为就信息传递而言,这两个进程都使用同一个水管,根本没有分别。
那么如何唯一地确定一个进程呢?
首先使用IP地址+域名地址先唯一确定一台主机,这个时候再用PID也就是进程ID,这样才能唯一确定一个网络中的进程。
4.问题:进程和TCP连接又是什么关系呢?
答案是没有绝对关系。
实际上问题3已经回答了。
一个进程可以拥有很多个TCP连接。
而一个TCP连接,也可以被多个进程共享使用。(只是这样明显引起了混乱)
5.问题:监听套接字描述符可以用来发送接收信息吗?
这个问题,我感觉我是想地最不清楚的了,不过我还是试着说下自己的想法。
其实这个问题的来源在于,我们知道,当调用listen返回,但是没有调用accept的时候,
实际上,listen会为我们维护两个队列:未完成连接队列,已完成连接队列。
其中已完成连接队列是已经完成三次握手连接的,而且这个连接是只在listenfd的参与下完成的。
既然连接established,而端口号以及IP都是和将要使用的connfd都是一样的啊。
但是当我们用read函数去read时,会出错。
书4.6节,P88有提到
listenfd是监听套接字描述符,而由accept返回的那个connfd叫做已连接套接字描述符。
这两个描述符是不一样的,而调用listen函数之后,就是将一个主动描述符,转化为了被动描述符,也就是监听套接字描述符。
而我们一般所说的TCP连接,是已经由accept返回了的,TCP连接,它可以用来发送进程给的消息,更具体的说是read,write消息。
确实在连接建立,三次握手时候,也发送了相应的消息,但这些完全是内核层面的,SYN,ACK,在进程层面是完全不可见的。
所以要发送接收,进程层面可见的内容,必须使用accept后的connfd套接字描述符。
当然这个根本原因,两种套接字描述符的区别在哪里,其实我还是没有找到答案,
不过遵循best practice即可。