socket编程深入了解

1. 使用socket建立套接字描述符时,内核实际上会注册一个socket结构体,可以在内核源码里找到,socket结构体定义如下:

struct socket

{

    socket_state state;

    unsigned long flags;

    const struct proto_ops *ops;

    struct fasync_struct *fasync_list;

    struct file *file;

    struct sock *sk;

    wait_queue_head_t wait;

    shrot type;

};

其中,struct sock包含一个sock_common结构体,sock_common结构体又包含struct inet_sock结构体:

struct inet_sock

{

    struct sock sk;

    #if defined(CONFIG_IPV6)||defined(CONFIG_IPV6_MODULE)

    struct ipv6_pinfo *pinet6;

    #endif

    _u32 daddr; //ipv4的目的地址

    _u32 rcv_saddr;//ipv4的本地接收地址

    _u16 dport;//目的端口

    _u16  num;//本地端口(主机字节序)

};

因此内核注册的socket结构体里,包含了本地ip和port以及目的ip和port.

2.bind为套接字描述符绑定ip和port,目的就是将主机暴露在网络中(服务端都使用bind,为客户端提供ip和端口),而在客户端可以不需要使用。

3.客户端使用connect连接服务端成功时,内核就会给socket结构体完善目的信息(目的ip和port)。

4.服务端listen函数将socket套接字由主动变为被动(使成为监听套接字),只用来连接客户端和设置允许同时最大连接数。因此监听套接字里的socket结构体里没有目的ip和端口。

5.服务端accept函数通过监听套接字等待客户端连接,和返回与客户端连接的连接套接字。内核里根据连接套接字找到的socket结构体有目的ip和端口。

问题:服务端有监听套接字和连接套接字(如果多个客户端相连,就会有多个连接套接字),当客户端发消息时,如何区分是发到哪个套接字?

显然,服务端所有套接字本地ip和port都是一个。我们都知道一个端口只能绑定一个套接字,这里是为什么呢?

我要说明一下,监听套接字是我们绑定的ip和port。而通过accept产生的连接套接字它的ip和port不是绑定的而是复制监听套接字里的。

当客户端(通过connect)发送连接请求时,服务端就会使用监听套接字,进行accept处理。建立一个连接套接字。监听套接字和连接套接字是互不影响,当close掉监听套接字后,连接套接字仍能使用,反之,一样。

当多个客户端连接服务端后(此时服务端有多个连接套接字),都给服务端发送数据,那么服务端怎么区分由哪个连接套接字接收数据的呢?

系统内核会为每个套接字注册一个socket结构体,当产生一个连接套接字时,内核会添加一个带有本地ip,port和目的ip,port的socket结构体。当协议将数据传到服务端时,会根据服务端内核注册的socket结构体找到与客户端相连接的套接字,并将数据放到该套接字的接受缓冲区中。

附:ip地址,网络中每个ip地址都是唯一的,可以找到主机。

        port(端口号):每个端口号只能被一个进程使用,每个进程能监听多个端口号。因此,端口号可以用来唯一标识一个进程。在通讯里,用来告知主机哪个进程来处理信息。

 

你可能感兴趣的:(socket深入了解,c++)