socket的疑问解析 阳光穿透心脏的1/2处 2022-08-21 06:40 112阅读 0赞 今天与同学争执一个话题:由于socket的accept函数在有客户端连接的时候产生了新的socket用于服务该客户端,那么,这个新的socket到底有没有占用一个新的端口? 讨论完后,才发现,自己虽然熟悉socket的编程套路,但是却并不是那么清楚socket的原理,今天就趁这个机会,把有关socket编程的几个疑问给搞清楚吧。 先给出一个典型的TCP/IP通信示意图。 [![socket的accept函数解析][socket_accept]][socket_accept_socket_accept] 问题一:socket结构体对象究竟是怎样定义的? 我们知道,在使用socket编程之前,需要调用socket函数创建一个socket对象,该函数返回该socket对象的描述符。 1. 函数原型:**int** socket(**int** domain, **int** type, **int** protocol); 那么,这个socket对象究竟是怎么定义的呢?它记录了哪些信息呢?只记录了本机IP及端口、还是目的IP及端口、或者都记录了? 关于这个问题,大家可以在内核源码里面找,也可以参考这篇文章《struct socket 结构详解》,我们可以看到 socket 结构体的定义如下: 1. **struct** socket 2. \{ 3. socket\_state state; 4. unsigned **long** flags; 5. **const** **struct** proto\_ops \*ops; 6. **struct** fasync\_struct \*fasync\_list; 7. **struct** file \*file; 8. **struct** sock \*sk; 9. wait\_queue\_head\_t wait; 10. **short** type; 11. \}; 其中,struct sock 包含有一个 sock\_common 结构体,而sock\_common结构体又包含有struct inet\_sock 结构体,而struct inet\_sock 结构体的部分定义如下: 1. **struct** inet\_sock 2. \{ 3. **struct** sock sk; 4. \#if defined(CONFIG\_IPV6) || defined(CONFIG\_IPV6\_MODULE) 5. **struct** ipv6\_pinfo \*pinet6; 6. \#endif 7. \_\_u32 daddr; //IPv4的目的地址。 8. \_\_u32 rcv\_saddr; //IPv4的本地接收地址。 9. \_\_u16 dport; //目的端口。 10. \_\_u16 num; //本地端口(主机字节序)。 11. 12. ………… 13. \} 由此,我们清楚了,socket结构体不仅仅记录了本地的IP和端口号,还记录了目的IP和端口。 问题二:connect函数究竟做了些什么操作? 在TCP客户端,首先调用一个socket()函数,得到一个socket描述符socketfd,然后通过connect函数对服务器进行连接,连接成功后,就可以利用这个socketfd描述符使用send/recv函数收发数据了。 关于connect函数和send函数的原型如下: 1. **int** connect( **int** sockfd, **const** **struct** sockaddr\* server\_addr, socklen\_t addrlen) 2. 3. **int** send( **int** sockfd, **const** **void** \*msg,**int** len,**int** flags); 那么,现在的困惑是,为什么send函数仅仅传入sockfd就可以知道服务器的ip和端口号? 其实,由“问题一”中的答案我们已经很清楚了,sockfd 描述符所描述的socket对象不仅包含了本地IP和端口,同时也包含了服务器的IP和端口,这样,才能使得send函数只需要传入sockfd 即可知道该把数据发向什么地方。而代码中,目的IP和端口只是在connect函数中出现过,因此,肯定是connect函数在成功建立连接后,将目的IP和端口写入了sockfd 描述符所描述的socket对象中。 问题三: accept函数产生的socket有没有占用新的端口? 首先,回顾一下accept函数,原型如下: 1. 2. **int** accept(**int** sockfd, **struct** sockaddr\* addr, socklen\_t\* len) 3. accept函数主要用于服务器端,一般位于listen函数之后,默认会阻塞进程,直到有一个客户请求连接,建立好连接后,它返回的一个新的套接字 socketfd\_new ,此后,服务器端即可使用这个新的套接字socketfd\_new与该客户端进行通信,而sockfd 则继续用于监听其他客户端的连接请求。 至此,我的困惑产生了,这个新的套接字 socketfd\_new 与监听套接字sockfd 是什么关系?它所代表的socket对象包含了哪些信息?socketfd\_new 是否占用了新的端口与客户端通信? 先简单分析一番,由于网站的服务器也是一种TCP服务器,使用的是80端口,并不会因客户端的连接而产生新的端口给客户端服务,该客户端依然是向服务器端的80端口发送数据,其他客户端依然向80端口申请连接。因此,可以判断,socketfd\_new 并没有占用新的端口与客户端通信,依然使用的是与监听套接字socketfd\_new一样的端口号。 那这么说,难道一个端口可以被两个socket对象绑定?当客户端发送数据过来的时候,究竟是与哪一个socket对象通信呢? 我是这么理解的(欢迎拍砖)。 首先,一个端口肯定只能绑定一个socket。我认为,服务器端的端口在bind的时候已经绑定到了监听套接字socetfd所描述的对象上,accept函数新创建的socket对象其实并没有进行端口的占有,而是复制了socetfd的本地IP和端口号,并且记录了连接过来的客户端的IP和端口号。 那么,当客户端发送数据过来的时候,究竟是与哪一个socket对象通信呢? 客户端发送过来的数据可以分为2种,一种是连接请求,一种是已经建立好连接后的数据传输。 由于TCP/IP协议栈是维护着一个接收和发送缓冲区的。在接收到来自客户端的数据包后,服务器端的TCP/IP协议栈应该会做如下处理:如果收到的是请求连接的数据包,则传给监听着连接请求端口的socetfd套接字,进行accept处理;如果是已经建立过连接后的客户端数据包,则将数据放入接收缓冲区。这样,当服务器端需要读取指定客户端的数据时,则可以利用socketfd\_new 套接字通过recv或者read函数到缓冲区里面去取指定的数据(因为socketfd\_new代表的socket对象记录了客户端IP和端口,因此可以鉴别)。 这就是我对socket编程的一些疑问的理解,有不正确的地方欢迎留言或者来信[lujun.hust@gmail.com][lujun.hust_gmail.com]交流。 本文出自 “[对影成三人][Link 1]” 博客,[http://ticktick.blog.51cto.com/823160/779866][http_ticktick.blog.51cto.com_823160_779866] [socket_accept]: http://s5.sinaimg.cn/mw690/0026RMyCzy6IQYpwaTWc4&690 [socket_accept_socket_accept]: /images/20220724/c635de8142484e0db7431bcecc00c321.png [lujun.hust_gmail.com]: mailto:lujun.hust@gmail.com [Link 1]: http://ticktick.blog.51cto.com/ [http_ticktick.blog.51cto.com_823160_779866]: http://ticktick.blog.51cto.com/823160/779866
相关 Java网络编程socket通信问题案例解析 在Java的网络编程中,Socket是一种主要的通信工具。以下是一些关于Socket通信问题以及案例解析: 1. **连接问题**: - 案例:客户端无法与服务器建立连 浅浅的花香味﹌/ 2024年09月19日 01:27/ 0 赞/ 11 阅读
相关 Java网络编程:客户端socket连接问题解析 在Java的网络编程中,客户端通过Socket进行连接。这里可能会遇到一些问题,以下是这些问题及解析: 1. **连接失败**:可能是服务器端未开启监听,或者客户端IP、端口 痛定思痛。/ 2024年09月17日 00:21/ 0 赞/ 15 阅读
相关 Java网络编程:socket连接异常解析 在Java的网络编程中,socket连接可能会出现各种异常。下面是对几种常见异常的解析: 1. **SocketException**: - `Socket.conne 墨蓝/ 2024年09月12日 12:33/ 0 赞/ 19 阅读
相关 Java设计模式: Singleton 实例疑问解析 Singleton(单例)是Java中一种常用的对象创建设计模式。它主要解决的问题是全局唯一对象的获取,以防止因多线程同时获取而导致混乱。 下面是Singleton模式的几种 布满荆棘的人生/ 2024年09月12日 04:24/ 0 赞/ 35 阅读
相关 Java网络编程:Socket连接问题实例解析 在Java网络编程中,Socket连接问题是常见的操作。以下是一些实例解析: 1. **创建Socket**: ```java ServerSocket serv 曾经终败给现在/ 2024年09月11日 15:45/ 0 赞/ 28 阅读
相关 Java网络编程:Socket通信问题解析 在Java的网络编程中,Socket是一种主要的通信工具。以下是一些常见的Socket通信问题及解析: 1. **连接失败**:这可能是由于服务器地址错误、端口异常或服务器已 r囧r小猫/ 2024年09月10日 07:18/ 0 赞/ 24 阅读
相关 关于Socket的简单解析 最近在研究Socket的通信,感觉坑很多,多,多。。所以今天就和大家来简单分享下Socket的使用方式,以及关于Socket的几个比较重要,容易被小伙伴们忽略且常用的 ゝ一世哀愁。/ 2022年08月21日 09:45/ 0 赞/ 150 阅读
相关 socket的疑问解析 今天与同学争执一个话题:由于socket的accept函数在有客户端连接的时候产生了新的socket用于服务该客户端,那么,这个新的socket到底有没有占用一个新的端口? 阳光穿透心脏的1/2处/ 2022年08月21日 06:40/ 0 赞/ 113 阅读
还没有评论,来说两句吧...