一个关于socket网络编程的奇怪问题

时间:2022-06-30 10:17:21
在服务器端,每accept一个客户连接,就产生一个新的套接字 ,同时fork()一个新的进程,在这个新的进城中使用这个新的套接字与客户通信;而原来的套接字则在原来的进程中继续监听。
那么,这里这个新的套接字与原有的套接字端口号是一样呢?还是不一样呢?
资料上说,端口号是用来区分进程的。这里产生了一个新的进程,照理来讲,两个套接字的端口号应该不同,可是我在客户端用getpeername(或者在客户端新进程里用getsockname)却看到服务器端的新套接字与监听套接字端口号是一样的。这该如何解释?

41 个解决方案

#1


是这样的,

表示一个不同的socket的5个要素(我自己这么叫)
本地端口号,本地ip,远端端口号,远端ip,还有就是类型(udp,tcp),
这5点一起决定了一个socket。
任意一点不同就可以区别一个socket或者连接或者之类。

关于你这个问题,产生的那个socket,
远端ip和远端端口号,至少有一个是不一样的。
至于本地...
你看《TCP-IP详解卷2:实现》中,讲的系统是如何实现的,
我一时也弄不清楚了。

#2


端口号不一样。accept后,系统分配一个临时的端口给通讯进程,通讯进程使用新的端口进行通讯。而原来的进程则使用老的端口继续监听。

#3


首先谢谢各位回复
to:wwwunix,你说的不对,在服务器端,新建的socket在通讯时所使用的端口和监听所使用的端口号是一样的

to:imquestion,你说的意思是不是这样,在服务器端新产生的通信socket,虽然它本地的iP和port与监听socket是一样的,但是通信socket中的远端IP和端口号与监听socket却是不同的,是这个意思么?还有,在这里,既有一个监听套接字在这个端口监听,又有多个通讯套接字在同一端口接受数据。是这样的么?同一个端口号可以被多个通讯套接字共享么?

#4


to:wwwunix
如果照你所说,那么每来一个连接,服务器端系统就要为之分配一个一个临时的端口,如果来1000个连接,就要为之分配1000个端口,怎么可能呢?来一万个连接不是要分配一万个端口了?

#5


看你是什么协议的通选,但你说道要fork()子进程,应该是tcp的吧,tcp的当然是要每连接一个端口了,你在客户端get到的就是你连接服务端时用的端口,所以返回确实是服务端指定监听的端口号,但fork()出的进程实际用来和客户端通讯的端口则是新分配的。socket本身并没有客户和服务的区别这你是知道的。
如果一万个连接,客户端确实需要1完个子进程来,启动1000个端口来完成对一万个客户的交互。
至于为什么客户取到的端口号是服务的监听端口号,我想这和socket的使用习惯有关,因为socket通选一旦建立,我们没有必要知道两端是通过什么端口在通讯。也就是说我们使用上实际只关心我们能从哪个端口开始建立socket连接,而通讯一旦建立到断开之前我们都可以在这个唯一的连接上处理数据。


#6


呵呵,确实是用的新端口,系统最大可以有65535个端口。

#7


to: aydge(阿吉)  &  wwwwunix(木易),就依两位所说,比如说有个一万个连接过来,系统为之分配了一万个端口号用于通信,这个时候,比如开始监听的端口号为A,一万个端口号中有一个为B。假设我此时又有一个新的服务要开启,我为这个新的服务指定一个固定的端口号为B,这时不就有问题了么?
因为我并不知道系统已经使用端口B来处理连接,我只知道端口A被占用了。10000个端口这么多,那我不是别的服务都启动不了了(假设这些服务的端口号冲突了的话)。

#8


to:aydge(阿吉) 
你说“如果一万个连接,客户端确实需要1完个子进程来,启动1000个端口来完成对一万个客户的交互”,这里的客户端应该是打错了,你的意思应该是说服务器端启动10000个端口来完成对一万个用户的交互吧?

那好,我又如何知道服务器端分配给这个新进程的端口号呢?
我在服务端这个新建子进程里调用getsockname(通信套接字),但是获取的端口号仍然是那个监听套接字的端口号
还有上面的问题,如果10000多个端口被占用了,比如说我另外有个服务程序还未启动,它占用端口号B,刚好这10000多个端口号中有一个号就是B了,那我这个服务程序不是启动不了了。这怎么可能呢??
我的邮箱:litowen@163.net
QQ:5735933

#9


有没有相关的书籍讲到这部分的具体实现阿?

#10


这个问题我也很困惑,希望更多人参与进来讨论

#11


如果端口已被占用,确实无法在使用这个端口了。

#12


因为我没有写过程序,所以不知道如何得到已用端口号。
我查查资料先

#13


如果端口已经被占用,则bind()时会报错。
还有一般端口的使用都有一些默认的规则(但不是强制的规则)。
例如:
1-1023为BSD保留端口
1024-5000为临时端口
5001-65535为BSD服务器(非特权)

#14


《TCP/IP详解》第一卷(译本)上有专门说到这个问题,书上说:服务器上的几个进程共用这一个端口。见译本第一卷193页。
相对来说我比较支持imquestion的观点;
各位如果还有支持意见、反对意见和依据,请继续关注,继续回复。

#15


接上,《TCP/IP详解》第一卷上讲:服务器上监听的进程和负责通信的进程使用的是同一个端口号,但是服务器如何区分到来的数据是哪个进程的呢?
就如imquestion老兄所说,服务器端的这几个socket虽然本地的IP和端口号相同,但是远端的IP和端口号肯定会有区别,要么是IP不同,要么是端口号不同,以此就能区分开该哪个进程接受数据了

请各位继续关注,继续回复。

#16


在linux的源代码中/net下面有个socket.c,自己仔细看看这段代码

#17


to   cai3995 :请说明你支持那种观点

#18


TCP有四元组,原、目的地址,原、目的端口号(也就是1楼那位所说的除开协议类型),每一条TCP链接都是依照这四个来区分的。所以你说的没错,但是你所问的是fork之后的socket,socket也是进程间通信,这个子进程是继承自父进程的,他的fid和父进程是一样的,所以出现了“我在客户端用getpeername(或者在客户端新进程里用getsockname)却看到服务器端的新套接字与监听套接字端口号是一样的”这种情况。

#19


这里才有点热闹:)
首先问清楚楼上:
   依阁下所说,那服务器端监听的端口号和处理连接的端口号应该是同一个了?
再,楼上开始肯定了litowen的说法,但后面的观点莫非是说:子进程的socket是从父进程中继承过来的,所以就会出现这种问题,但 如果现在假设这个服务器不是多进程的,只是单进程,处理一个连接时,服务器同样要创建一个新的socket,这个用于处理通信的socket和监听socket现在总该是在一个进程里了吧,我刚刚测试了一下,在客户端用getpeername(或者在服务器端读写时调用getsockname所得到的端口号仍然是一样的

#20


其实争论的焦点无非就是:

在服务器端多进程或单进程情况下,
服务器端负责监听的套接字和负责通信的套接字这两者的端口号是不是一样的?

#21


我发的第一篇帖子中,“我在客户端用getpeername(或者在客户端新进程里用getsockname)却看到服务器端的新套接字与监听套接字端口号是一样的”
应改为:
“我在客户端用getpeername(或者在服务器端新进程里用getsockname)却看到服务器端的新套接字与监听套接字端口号是一样的”

#22


但,如果现在假设这个服务器不是多进程的,只是单进程,处理一个连接时,服务器同样要创建一个新的socket,这个用于处理通信的socket和监听socket现在总该是在一个进程里了吧
--------------------------------------------------------------------
你这段话说得有问题,单进程的时候,他新创建一个socket干什么,C/S模式通信的原理就是服务器对客户端响应,所以在服务器端一直都是在listen,而不是“智能”的发现有客户要连接了,再去创建用于通信的socket。
服务器端监听的端口号和处理连接的端口号的确“不是”同一个!
与客户端建立连接的是监听的父进程,在accept之后才会creat一个新的进程来处理通信,所以看到的是父进程的端口号

#23


可否QQ交流?
5735933

#24


我仔细看了一下accept()的代码

主要是这样的,他会创建一个新的socket对象,这个socket对象所有的连接信息都是原有的监听socket和所连接到的client信息,所以这个socket的所有信息都是和原来的监听socket的是一样的。
开始误会了你的意思,不好意思

#25


把相关源码,和相关分析,整理一下贴出来吧。

#26


1、用getpeername得到的端口与监听端口一样并不表示数据通讯用的端口和监听端口一样。
2、其实分析这个问题要先分清楚服务器是迭代服务还是并发服务,如果是迭代服务则用的是同一个端口;如果是并发服务则用的是不同的端口(临时端口)。见《UNIX网络编程》第88页,很详细地分析了这个问题。

#27


同意wwwunix上面所说,我把accept的那段代码贴一下好了。
/*
 * For accept, we attempt to create a new socket, set up the link
 * with the client, wake up the client, then return the new
 * connected fd. We collect the address of the connector in kernel
 * space and move it to user at the very end. This is unclean because
 * we open the socket then return an error.
 *
 * 1003.1g adds the ability to recvmsg() to query connection pending
 * status to recvmsg. We need to add that support in a way thats
 * clean when we restucture accept also.
 */

asmlinkage long sys_accept(int fd, struct sockaddr *upeer_sockaddr, int *upeer_addrlen)
{
struct socket *sock, *newsock;
int err, len;
char address[MAX_SOCK_ADDR];

sock = sockfd_lookup(fd, &err);
if (!sock)
goto out;

err = -EMFILE;
if (!(newsock = sock_alloc())) 
goto out_put;

newsock->type = sock->type;
newsock->ops = sock->ops;

err = sock->ops->accept(sock, newsock, sock->file->f_flags);
if (err < 0)
goto out_release;

if (upeer_sockaddr) {
if(newsock->ops->getname(newsock, (struct sockaddr *)address, &len, 2)<0) {
err = -ECONNABORTED;
goto out_release;
}
err = move_addr_to_user(address, len, upeer_sockaddr, upeer_addrlen);
if (err < 0)
goto out_release;
}

/* File flags are not inherited via accept() unlike another OSes. */

if ((err = sock_map_fd(newsock)) < 0)
goto out_release;

out_put:
sockfd_put(sock);
out:
return err;

out_release:
sock_release(newsock);
goto out_put;
}
------------------------------------
accept之后创建的socket对象是和原来的一样的,但是fork之后新进程的就是别的了。

#28


写点分析啊,哪里是新的socket的本地端口号,本地ip的赋值,或者复制。
是总是这样呢,还是某些情况怎么样..

#29


to:wwwunix(木易) 
我一时间下载不到《UNIX网络编程》,不知道里面怎么说
你可不可以把相关的话贴上来

另外,我参考的是TCP/IP详解上的,两本书的作者都是Richard Stevens,
怎么会两本书有不同的提法?

#30


To litowen(litowen):
  其中分析的内容太多了,连图带解释有3页。呵呵,我想想看有没有其他办法。
  另:这两本书所说的的并不矛盾,只是基于的角度不同。

#31


我再去书店找找这本书吧

#32


收藏

#33


to:木易,我现在弄不懂你为什么反驳我的观点了

我去查看了《unix网络编程》(译本第88页,4.8 并发服务器),也说明我的观点是正确的阿,是不是你理解错了

#34


to litowen(litowen):
  你的观点是说fork()后,服务器用来通讯的socket和监听socket是同一个呀.但实际上不是.

#35


to:木易,书上好像并没有哪里说新建了一个临时端口号阿。
而且证明了我的观点是正确的,如有异意,请继续跟帖,或是加我QQ:5735933

#36


to:木易,你理解错了,我知道这两个socket不是同一个,
我一直想弄清楚的就是:这两个不同的socket是否使用同一个“端口号”?
我原先想如果使用同一个“端口号”,那么服务器端的两个进程不是使用的端口号是一样的么?,但是事实是他们使用的”端口号“是相同的

我一直说的是端口号,不是socket

#37


呵呵,可能是我理解错了。

#38


你把端口的意思理解错了,端口是端口,socket是socket(翻译做套接字),两者是不同的

#39


呵呵,你知道为了你这几句话,几个晚上没睡好呢,总想搞清楚这个问题
在网上找这本书找了几天,今天终于找到了,结果和我的观点是一样的

#40


Sorry,呵呵,其实我是与ftp协议弄混了。(正好在回答另一个关于ftp的问题。)

#41


有始有终,最后公布一下正确的结论:
服务器端这两个socket(用于监听的socket和用于通信的socket)的端口号是一样的,相关说明见《TCp/IP详解》(译本第一卷193页)和《unix网络编程》(译本第88页,4.8 并发服务器)。

#1


是这样的,

表示一个不同的socket的5个要素(我自己这么叫)
本地端口号,本地ip,远端端口号,远端ip,还有就是类型(udp,tcp),
这5点一起决定了一个socket。
任意一点不同就可以区别一个socket或者连接或者之类。

关于你这个问题,产生的那个socket,
远端ip和远端端口号,至少有一个是不一样的。
至于本地...
你看《TCP-IP详解卷2:实现》中,讲的系统是如何实现的,
我一时也弄不清楚了。

#2


端口号不一样。accept后,系统分配一个临时的端口给通讯进程,通讯进程使用新的端口进行通讯。而原来的进程则使用老的端口继续监听。

#3


首先谢谢各位回复
to:wwwunix,你说的不对,在服务器端,新建的socket在通讯时所使用的端口和监听所使用的端口号是一样的

to:imquestion,你说的意思是不是这样,在服务器端新产生的通信socket,虽然它本地的iP和port与监听socket是一样的,但是通信socket中的远端IP和端口号与监听socket却是不同的,是这个意思么?还有,在这里,既有一个监听套接字在这个端口监听,又有多个通讯套接字在同一端口接受数据。是这样的么?同一个端口号可以被多个通讯套接字共享么?

#4


to:wwwunix
如果照你所说,那么每来一个连接,服务器端系统就要为之分配一个一个临时的端口,如果来1000个连接,就要为之分配1000个端口,怎么可能呢?来一万个连接不是要分配一万个端口了?

#5


看你是什么协议的通选,但你说道要fork()子进程,应该是tcp的吧,tcp的当然是要每连接一个端口了,你在客户端get到的就是你连接服务端时用的端口,所以返回确实是服务端指定监听的端口号,但fork()出的进程实际用来和客户端通讯的端口则是新分配的。socket本身并没有客户和服务的区别这你是知道的。
如果一万个连接,客户端确实需要1完个子进程来,启动1000个端口来完成对一万个客户的交互。
至于为什么客户取到的端口号是服务的监听端口号,我想这和socket的使用习惯有关,因为socket通选一旦建立,我们没有必要知道两端是通过什么端口在通讯。也就是说我们使用上实际只关心我们能从哪个端口开始建立socket连接,而通讯一旦建立到断开之前我们都可以在这个唯一的连接上处理数据。


#6


呵呵,确实是用的新端口,系统最大可以有65535个端口。

#7


to: aydge(阿吉)  &  wwwwunix(木易),就依两位所说,比如说有个一万个连接过来,系统为之分配了一万个端口号用于通信,这个时候,比如开始监听的端口号为A,一万个端口号中有一个为B。假设我此时又有一个新的服务要开启,我为这个新的服务指定一个固定的端口号为B,这时不就有问题了么?
因为我并不知道系统已经使用端口B来处理连接,我只知道端口A被占用了。10000个端口这么多,那我不是别的服务都启动不了了(假设这些服务的端口号冲突了的话)。

#8


to:aydge(阿吉) 
你说“如果一万个连接,客户端确实需要1完个子进程来,启动1000个端口来完成对一万个客户的交互”,这里的客户端应该是打错了,你的意思应该是说服务器端启动10000个端口来完成对一万个用户的交互吧?

那好,我又如何知道服务器端分配给这个新进程的端口号呢?
我在服务端这个新建子进程里调用getsockname(通信套接字),但是获取的端口号仍然是那个监听套接字的端口号
还有上面的问题,如果10000多个端口被占用了,比如说我另外有个服务程序还未启动,它占用端口号B,刚好这10000多个端口号中有一个号就是B了,那我这个服务程序不是启动不了了。这怎么可能呢??
我的邮箱:litowen@163.net
QQ:5735933

#9


有没有相关的书籍讲到这部分的具体实现阿?

#10


这个问题我也很困惑,希望更多人参与进来讨论

#11


如果端口已被占用,确实无法在使用这个端口了。

#12


因为我没有写过程序,所以不知道如何得到已用端口号。
我查查资料先

#13


如果端口已经被占用,则bind()时会报错。
还有一般端口的使用都有一些默认的规则(但不是强制的规则)。
例如:
1-1023为BSD保留端口
1024-5000为临时端口
5001-65535为BSD服务器(非特权)

#14


《TCP/IP详解》第一卷(译本)上有专门说到这个问题,书上说:服务器上的几个进程共用这一个端口。见译本第一卷193页。
相对来说我比较支持imquestion的观点;
各位如果还有支持意见、反对意见和依据,请继续关注,继续回复。

#15


接上,《TCP/IP详解》第一卷上讲:服务器上监听的进程和负责通信的进程使用的是同一个端口号,但是服务器如何区分到来的数据是哪个进程的呢?
就如imquestion老兄所说,服务器端的这几个socket虽然本地的IP和端口号相同,但是远端的IP和端口号肯定会有区别,要么是IP不同,要么是端口号不同,以此就能区分开该哪个进程接受数据了

请各位继续关注,继续回复。

#16


在linux的源代码中/net下面有个socket.c,自己仔细看看这段代码

#17


to   cai3995 :请说明你支持那种观点

#18


TCP有四元组,原、目的地址,原、目的端口号(也就是1楼那位所说的除开协议类型),每一条TCP链接都是依照这四个来区分的。所以你说的没错,但是你所问的是fork之后的socket,socket也是进程间通信,这个子进程是继承自父进程的,他的fid和父进程是一样的,所以出现了“我在客户端用getpeername(或者在客户端新进程里用getsockname)却看到服务器端的新套接字与监听套接字端口号是一样的”这种情况。

#19


这里才有点热闹:)
首先问清楚楼上:
   依阁下所说,那服务器端监听的端口号和处理连接的端口号应该是同一个了?
再,楼上开始肯定了litowen的说法,但后面的观点莫非是说:子进程的socket是从父进程中继承过来的,所以就会出现这种问题,但 如果现在假设这个服务器不是多进程的,只是单进程,处理一个连接时,服务器同样要创建一个新的socket,这个用于处理通信的socket和监听socket现在总该是在一个进程里了吧,我刚刚测试了一下,在客户端用getpeername(或者在服务器端读写时调用getsockname所得到的端口号仍然是一样的

#20


其实争论的焦点无非就是:

在服务器端多进程或单进程情况下,
服务器端负责监听的套接字和负责通信的套接字这两者的端口号是不是一样的?

#21


我发的第一篇帖子中,“我在客户端用getpeername(或者在客户端新进程里用getsockname)却看到服务器端的新套接字与监听套接字端口号是一样的”
应改为:
“我在客户端用getpeername(或者在服务器端新进程里用getsockname)却看到服务器端的新套接字与监听套接字端口号是一样的”

#22


但,如果现在假设这个服务器不是多进程的,只是单进程,处理一个连接时,服务器同样要创建一个新的socket,这个用于处理通信的socket和监听socket现在总该是在一个进程里了吧
--------------------------------------------------------------------
你这段话说得有问题,单进程的时候,他新创建一个socket干什么,C/S模式通信的原理就是服务器对客户端响应,所以在服务器端一直都是在listen,而不是“智能”的发现有客户要连接了,再去创建用于通信的socket。
服务器端监听的端口号和处理连接的端口号的确“不是”同一个!
与客户端建立连接的是监听的父进程,在accept之后才会creat一个新的进程来处理通信,所以看到的是父进程的端口号

#23


可否QQ交流?
5735933

#24


我仔细看了一下accept()的代码

主要是这样的,他会创建一个新的socket对象,这个socket对象所有的连接信息都是原有的监听socket和所连接到的client信息,所以这个socket的所有信息都是和原来的监听socket的是一样的。
开始误会了你的意思,不好意思

#25


把相关源码,和相关分析,整理一下贴出来吧。

#26


1、用getpeername得到的端口与监听端口一样并不表示数据通讯用的端口和监听端口一样。
2、其实分析这个问题要先分清楚服务器是迭代服务还是并发服务,如果是迭代服务则用的是同一个端口;如果是并发服务则用的是不同的端口(临时端口)。见《UNIX网络编程》第88页,很详细地分析了这个问题。

#27


同意wwwunix上面所说,我把accept的那段代码贴一下好了。
/*
 * For accept, we attempt to create a new socket, set up the link
 * with the client, wake up the client, then return the new
 * connected fd. We collect the address of the connector in kernel
 * space and move it to user at the very end. This is unclean because
 * we open the socket then return an error.
 *
 * 1003.1g adds the ability to recvmsg() to query connection pending
 * status to recvmsg. We need to add that support in a way thats
 * clean when we restucture accept also.
 */

asmlinkage long sys_accept(int fd, struct sockaddr *upeer_sockaddr, int *upeer_addrlen)
{
struct socket *sock, *newsock;
int err, len;
char address[MAX_SOCK_ADDR];

sock = sockfd_lookup(fd, &err);
if (!sock)
goto out;

err = -EMFILE;
if (!(newsock = sock_alloc())) 
goto out_put;

newsock->type = sock->type;
newsock->ops = sock->ops;

err = sock->ops->accept(sock, newsock, sock->file->f_flags);
if (err < 0)
goto out_release;

if (upeer_sockaddr) {
if(newsock->ops->getname(newsock, (struct sockaddr *)address, &len, 2)<0) {
err = -ECONNABORTED;
goto out_release;
}
err = move_addr_to_user(address, len, upeer_sockaddr, upeer_addrlen);
if (err < 0)
goto out_release;
}

/* File flags are not inherited via accept() unlike another OSes. */

if ((err = sock_map_fd(newsock)) < 0)
goto out_release;

out_put:
sockfd_put(sock);
out:
return err;

out_release:
sock_release(newsock);
goto out_put;
}
------------------------------------
accept之后创建的socket对象是和原来的一样的,但是fork之后新进程的就是别的了。

#28


写点分析啊,哪里是新的socket的本地端口号,本地ip的赋值,或者复制。
是总是这样呢,还是某些情况怎么样..

#29


to:wwwunix(木易) 
我一时间下载不到《UNIX网络编程》,不知道里面怎么说
你可不可以把相关的话贴上来

另外,我参考的是TCP/IP详解上的,两本书的作者都是Richard Stevens,
怎么会两本书有不同的提法?

#30


To litowen(litowen):
  其中分析的内容太多了,连图带解释有3页。呵呵,我想想看有没有其他办法。
  另:这两本书所说的的并不矛盾,只是基于的角度不同。

#31


我再去书店找找这本书吧

#32


收藏

#33


to:木易,我现在弄不懂你为什么反驳我的观点了

我去查看了《unix网络编程》(译本第88页,4.8 并发服务器),也说明我的观点是正确的阿,是不是你理解错了

#34


to litowen(litowen):
  你的观点是说fork()后,服务器用来通讯的socket和监听socket是同一个呀.但实际上不是.

#35


to:木易,书上好像并没有哪里说新建了一个临时端口号阿。
而且证明了我的观点是正确的,如有异意,请继续跟帖,或是加我QQ:5735933

#36


to:木易,你理解错了,我知道这两个socket不是同一个,
我一直想弄清楚的就是:这两个不同的socket是否使用同一个“端口号”?
我原先想如果使用同一个“端口号”,那么服务器端的两个进程不是使用的端口号是一样的么?,但是事实是他们使用的”端口号“是相同的

我一直说的是端口号,不是socket

#37


呵呵,可能是我理解错了。

#38


你把端口的意思理解错了,端口是端口,socket是socket(翻译做套接字),两者是不同的

#39


呵呵,你知道为了你这几句话,几个晚上没睡好呢,总想搞清楚这个问题
在网上找这本书找了几天,今天终于找到了,结果和我的观点是一样的

#40


Sorry,呵呵,其实我是与ftp协议弄混了。(正好在回答另一个关于ftp的问题。)

#41


有始有终,最后公布一下正确的结论:
服务器端这两个socket(用于监听的socket和用于通信的socket)的端口号是一样的,相关说明见《TCp/IP详解》(译本第一卷193页)和《unix网络编程》(译本第88页,4.8 并发服务器)。