socket accept后的fd是否占用新端口

今天小组讨论了下自动化部署的架构与实现。在讨论推送配置的时候,我发现了一个问题,这也让我知道了我以前一直忽略的一个东西。


很多人socket编程很熟悉,但是能知道下面细节的人就不是那么多了。以前就想读tcp ip协议详解,可惜那时候的功力还没到,看来等有时间还是得细细品读。


大家都很熟悉,在server端编程,首先得创建一个socket并bind一个地址,然后进行listen和accept,当客户端有连接的时候,accept会返回一个fd,代表了与客户端的连接,我们可以通过这个fd来跟对应的client端进行io通讯,也可以捕捉到断开的事件。


问题就在上面accept的fd上面。我以前的理解一直以为这个fd占用了一个新的端口号,并且这个占用的端口号与client一致,通过相同的端口号进行传输。当时也没有怀疑正确与否,现在想来,真是错误百出。


首先,socketlisten后,是肯定占用了对应的端口号的,任何别的尝试使用该端口的操作肯定会报错,这是毋庸置疑的。accept的新的fd,占不占用新的端口号呢?看到这里大家肯定也都明白了,是不占用的。那不占用,肯定和listen的端口一样啊?不是上面说了一个端口只能使用一次吗?


这里就引出了socket的底层结构。区别两个socket是否一致,需要5元组来区别,分别是本机的ip 本机的端口 目标机的ip 目标机的端口 还有一个是协议。通过这些可以唯一的区别socket,当这些一致,那么这两个socket是一样的。系统提供的api,比如bind listen connect,其实都隐藏了对socket元组信息的写入。


再谈谈上述的问题,在accept一个新的连接后,其实fd的确是新的fd,但是它的本机ip port是和listen的一样的,而不同的client的fd之间的区别,就是不同的目标机的ip port信息。


这个问题解决了,那么我们再梳理一下结构。为何系统要划分端口号?其实端口这个概念,是在tcp这一层,即传输层实现的东西,为上层应用层提供支持。当应用层通过socket send数据的时候,tcp层会根据目标主机的ip port等信息进行数据包的进一步封装,然后将其交给ip层来进行网络传输,ip层则根据目标ip地址来将其再次封装然后最终交给物理层进行传输。

而对于client来说,首先ip层会收到这个数据,然后根据头部信息中的协议来交给不同的协议来进行处理。向上交给tcp层之后,tcp层会从头部数据中获取到端口号信息,到这一步其实已经可以唯一确定是哪个进程的数据了,通过端口号查到进程,并通知进程进行处理。这样整个通讯就走通了。


上面也谈到了,每一个socket都有一个local address和remote address,而对应的远程client的socket,也有一个local address和remote address,有意思的是,他们是互相相反的关系。其实想想也是,tcp向下传输的时候,需要给ip层传入remote ip,用于路由传输,而remote port,则会附加到数据头部中,因为这个remote port是需要给client来区分进程的。client收到了这个数据包后,根据server传入的port,则可以唯一的区分是哪个进程的数据包。而client发送的数据,则会带入client这个socket的remote port,然后最终数据会传输给server listen 的那个socket。tcp数据头中还会有client的local address的信息,所以发送到server监听的socket上后,可以通过client的ip port来区分是哪一个client发送的数据,之后的就交由应用程序来处理了。


上面的总结也就是每个socket都是有着通过local address来与remote address相连的意思,也就是两个端口号是可以不一样的。


当然上面的只是记录下今天的想法,只是感觉有必要去读下tcp ip协议了,以前读着摸不着头脑,现在读着却有着原来是这样啊的感慨。

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
socketaccept函数是用于在服务器端接收客户端的连接请求。它会阻塞当前线程,直到有客户端连接成功或发生错误。 accept函数的语法如下: ```c #include <sys/types.h> #include <sys/socket.h> int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); ``` 参数说明: - `sockfd`:表示服务器端监听的socket文件描述符。 - `addr`:用于存储客户端的地址信息,可以为NULL。 - `addrlen`:`addr`结构体的长度,需要初始化为`sizeof(struct sockaddr)`。 返回值: - 如果连接成功,返回一个socket文件描述符,用于与客户端进行通信。 - 如果失败,返回-1,并设置errno来指示出错的原因。 使用示例: ```c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <arpa/inet.h> #define MAX_CLIENTS 10 int main() { int server_fd, new_socket, valread; struct sockaddr_in address; int addrlen = sizeof(address); char buffer[1024] = {0}; // 创建服务器socket if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) { perror("socket failed"); exit(EXIT_FAILURE); } address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons(8080); // 绑定地址和端口 if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) { perror("bind failed"); exit(EXIT_FAILURE); } // 监听连接请求 if (listen(server_fd, MAX_CLIENTS) < 0) { perror("listen failed"); exit(EXIT_FAILURE); } // 等待连接 if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) { perror("accept failed"); exit(EXIT_FAILURE); } // 接收数据 valread = read(new_socket, buffer, 1024); printf("%s\n", buffer); return 0; } ``` 在上面的示例中,我们创建了一个服务器socket,并绑定地址和端口。然后使用`listen`函数开始监听连接请求。当有客户端连接成功后,通过`accept`函数获取一个socket文件描述符,然后可以使用这个socket与客户端进行通信。在示例中,我们利用socket读取客户端发送的数据,并打印输出。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值