Linux网络编程——socket详解

1. socket数据报和流式套接字的工作过程

在这里插入图片描述

2.socket流程

2.1 创建socket

int socket (int domain, int type, int protocol);
  1. domain:表示此socket的通信域(协议族),决定此socket到底是用于哪个域中的,网络PC间通信还是本地PC不同进程的通信等
    在这里插入图片描述
    比如,AF_UNIX,表示本地通信,其具体的绑定形式为文件路径;而对于AF_INET,表示网络通信,其具体绑定形式为ip+port。

  2. type:套接字的类型,进一步确定通信特征。比如说报文、有连接、无连接等。不同的type对应的protocol也不同
    在这里插入图片描述

  3. protocol:表示为给定域和套接字类型选择默认协议,当对同一域和套接字类型支持多个协议时,可以通过该字段来选择一个特定协议,通常默认为0.上面设置的socket类型,在执行的时候也会有默认的协议类型提供,比如SOCK_STREAM就TCP协议
    在这里插入图片描述

2.2 创建socket时的具体内核调用——socket()

其具体的系统调用为 int server_sockfd = socket(AF_INET, SOCK_STREAM, 0);(这里以AF_INET域的TCP流式协议为例)。
首先它会在系统内部创建一个socket结构,然后将此socket结构与某个文件描述符fd绑定(Linux中一切皆文件),相当于是给这个socket起了一个名字,这样便可以通过fd来索引socket

回想一下I/O模型中的I/O多路复用中的epoll不正是通过监听fd来响应连接请求的吗。

2.3 socket地址绑定——bind()

int bind(int sockfd, const struct sockaddr *addr, socklen_t len);

前面只是创建了socket并与fd绑定,但是具体的socket地址还不清楚,比如说如果domain选择了AF_INET域,那必然需要指定本机ip+port。相当于前面只是给socket起了名字,但这个socket到底是哪个市(port),哪个省(ip)的还不知道,所以要想找到这个人(socket),必须通过ip+port+fd来确定。

  • sockfd:前一阶段生成的fd
  • addr:指向sockaddr 的指针,而sockaddr就表示socket的地址结构。
    对于AF_INET域来说,其地址结构就需要指定port,ip,比如:
    addr.sin_family = AF_INET;
    addr.sin_port = htons(MYPORT)
    addr.sin_addr.s_addr = inet_addr("192.168.101.101")
    
    对于AF_UNIX域来说,其地址结构就需要指定用于交互的文件绝对路径
    addr.sin_family = AF_UNIX;
    addr.sun_path = "/tmp/path"
    

总结一下,bind()是系统函数暴露给用户的api,针对不同的协议族,其底层sys_bind()函数有多种形式,比如上面我们介绍了的AF_INET协议族和AF_UNIX协议族。内核的socket结构和fd是在调用socket函数时根据协议生成的,他绑定了不同的bind()函数形式。

2.4 socket监听——listen()

int listen(int sockfd, int backlog);

前面我们完成了socket的创建和绑定,但是如何让server直到是否有其它程序的socket连接请求呢,那就需要server监听socket队列,让其它程序发出的socket连接请求首先放入到一个可连接队列中,然后server监听这个队列,每当server空闲的时候,就可以accept一个socket。

2.5 server建立socket连接——accept()

client_fd =  int accept(int sockfd, struct sockaddr *restric addr, socklen_t *restrict len);
  • sockfd:原始侦听socket队列的fd
  • addr:指向客户端sockaddr 信息的指针(客户端的sockaddr 由sys_accept()通过相应协议栈解析生成,比如ip等)

注意点是,accept返回的是客户端socket在server端分配的一个新的文件描述符fd,相当于server端是通过和这个新fd来与client通信。于是后面要传入到client的数据可以使用相应的I/O函数进行写入,比如write方式写到这个fd对应的文件中(socket本身都是文件描述符),然后由相应的系统调用去从这个fd中读取数据并返回给client。

注意这里只是建立连接,并未涉及到和客户端socket_fd的具体交互模式,比如当有多个客户端连接的时候,就会产生多个fd,而对于大量的fd,有些数据还没有传输完成,有些完成了,那server如何和这些fd进行交互即何种I/O模式(阻塞BIO、非阻塞NIO、异步AIO),其中最常见的就是I/O多路复用,这一块关于 服务器的I/O模式 后面会专门写一篇讲解。

2.6 client发起socket连接——connect()

int connect(int sockfd,struct sockaddr *,int addrlen)
  • sockfd:客户端创建socket生成的fd
  • sockaddr :指向server端的socket信息(比如IP等)的指针
发布了69 篇原创文章 · 获赞 10 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/JustKian/article/details/100976539