socket的accept函数解析 stream socket accept

今天与同学争执一个话题:由于socket的accept函数在有客户端连接的时候产生了新的socket用于服务该客户端,那么,这个新的socket到底有没有占用一个新的端口?

讨论完后,才发现,自己虽然熟悉socket的编程套路,但是却并不是那么清楚socket的原理,今天就趁这个机会,把有关socket编程的几个疑问给搞清楚吧。

先给出一个典型的TCP/IP通信示意图。


问题一:socket结构体对象究竟是怎样定义的?

我们知道,在使用socket编程之前,需要调用socket函数创建一个socket对象,该函数返回该socket对象的描述符。

  1. 函数原型:intsocket(intdomain,inttype,intprotocol);

那么,这个socket对象究竟是怎么定义的呢?它记录了哪些信息呢?只记录了本机IP及端口、还是目的IP及端口、或者都记录了?

关于这个问题,大家可以在内核源码里面找,也可以参考这篇文章《struct socket 结构详解》,我们可以看到socket结构体的定义如下:

  1. structsocket
  2. {
  3. socket_statestate;
  4. unsignedlongflags;
  5. conststructproto_ops*ops;
  6. structfasync_struct*fasync_list;
  7. structfile*file;
  8. structsock*sk;
  9. wait_queue_head_twait;
  10. shorttype;
  11. };

其中,structsock 包含有一个 sock_common 结构体,而sock_common结构体又包含有struct inet_sock结构体,而struct inet_sock 结构体的部分定义如下:

  1. structinet_sock
  2. {
  3. structsocksk;
  4. #ifdefined(CONFIG_IPV6)||defined(CONFIG_IPV6_MODULE)
  5. structipv6_pinfo*pinet6;
  6. #endif
  7. __u32daddr;//IPv4的目的地址。
  8. __u32rcv_saddr;//IPv4的本地接收地址。
  9. __u16dport;//目的端口。
  10. __u16num;//本地端口(主机字节序)。
  11. …………
  12. }

由此,我们清楚了,socket结构体不仅仅记录了本地的IP和端口号,还记录了目的IP和端口。

问题二:connect函数究竟做了些什么操作?

在TCP客户端,首先调用一个socket()函数,得到一个socket描述符socketfd,然后通过connect函数对服务器进行连接,连接成功后,就可以利用这个socketfd描述符使用send/recv函数收发数据了。

关于connect函数和send函数的原型如下:

  1. intconnect(intsockfd,conststructsockaddr*server_addr,socklen_taddrlen)
  2. intsend(intsockfd,constvoid*msg,intlen,intflags);

那么,现在的困惑是,为什么send函数仅仅传入sockfd就可以知道服务器的ip和端口号?

其实,由“问题一”中的答案我们已经很清楚了,sockfd描述符所描述的socket对象不仅包含了本地IP和端口,同时也包含了服务器的IP和端口,这样,才能使得send函数只需要传入sockfd即可知道该把数据发向什么地方。而代码中,目的IP和端口只是在connect函数中出现过,因此,肯定是connect函数在成功建立连接后,将目的IP和端口写入了sockfd描述符所描述的socket对象中。

问题三:accept函数产生的socket有没有占用新的端口?

首先,回顾一下accept函数,原型如下:

  1. intaccept(intsockfd,structsockaddr*addr,socklen_t*len)

accept函数主要用于服务器端,一般位于listen函数之后,默认会阻塞进程,直到有一个客户请求连接,建立好连接后,它返回的一个新的套接字socketfd_new ,此后,服务器端即可使用这个新的套接字socketfd_new与该客户端进行通信,而sockfd则继续用于监听其他客户端的连接请求。

至此,我的困惑产生了,这个新的套接字socketfd_new 与监听套接字sockfd 是什么关系?它所代表的socket对象包含了哪些信息?socketfd_new是否占用了新的端口与客户端通信?

先简单分析一番,由于网站的服务器也是一种TCP服务器,使用的是80端口,并不会因客户端的连接而产生新的端口给客户端服务,该客户端依然是向服务器端的80端口发送数据,其他客户端依然向80端口申请连接。因此,可以判断,socketfd_new并没有占用新的端口与客户端通信,依然使用的是与监听套接字socketfd_new一样的端口号。

那这么说,难道一个端口可以被两个socket对象绑定?当客户端发送数据过来的时候,究竟是与哪一个socket对象通信呢?
socket的accept函数解析 stream socket accept

我是这么理解的(欢迎拍砖)。

首先,一个端口肯定只能绑定一个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交流。

本文出自 “对影成三人”博客,http://ticktick.blog.51cto.com/823160/779866

  

爱华网本文地址 » http://www.aihuau.com/a/25101017/352942.html

更多阅读

Crect的成员函数 类的静态成员函数

CRect类(矩形类)无基类【说 明】CRect类与Windows系统中表示矩形RECT结构相似,并且它还提供了操作RECT结构的成员函数。在传递LPRECT,LPCRECT或RECT结构作为参数的任何地方,都可以传递CRect类的对象。注意,这个类是从tagRECT结构派

模拟c++中的strstr函数 c语言中strstr函数

/************************************************************************//* 例子:请写出一个函数来模拟c++中的strstr函数:该函数的返回值是主传中字符子串的位置以后的所有字符,请不要使用任何c程序已有的函数函数名: strstr函

描述宇宙加速膨胀的简单函数 宇宙一直在加速膨胀

此日记引用地址:http://blog.people.com.cn/blog/trackback.do?wlog_id=1248387456086841描述宇宙加速膨胀的简单函数周 坚(广西柳州 545012 E-mail:zhzhjjjj@163.com)摘要 基于发现宇宙正在加速膨胀的高红移Ia超新星哈勃图,分析与高红

声明:《socket的accept函数解析 stream socket accept》为网友靈魂风尖上分享!如侵犯到您的合法权益请联系我们删除