Hello World

套接字:Socket

  在进行通信之前,一般要先建立Socket连接,Socket编程是端到端的通信,往往意识不到中间经过了多少局域网,多少路由器,他能操作的是端到端协议之上的网络层和传输层。

  在网络层,Socket函数可以指定使用IPV4(AF_INET)或者IPV6(AF_INET6),还可以指定传输层协议使用TCP(SOCK_STREAM)还是UDP(SOCK_DGRAM)。

1. 基于TCP的Socket函数的调用过程

  a. 调用 bind 函数,给这个Socket赋予一个IP和端口(绑定IP是因为机器可能有多个网卡,绑定端口是确定进程);

  b. 调用 listen 函数进行监听;

    内核中每个socket维护两个队列:一个已经建立连接的队列(三次握手完毕,处于established状态),一个还没有完全建立连接的队列(三次握手尚未完成,处于syn_rcvd状态)。

    服务端监听时,客户端可通过 connect 函数发起连接,三次握手时,内核会给客户端分配一个临时端口,握手成功后服务端的accept返回另一个socket。

    监听的socket和传输数据的socket是两个socket,前者叫做监听socket,后者叫做已连接socket。

    为什么需要两个socket?为了复用监听socket,提高性能。

  c. 服务端调用 accept 函数,拿出一个已经完成连接进行处理; 

 

  d. 连接建立成功后,双方通过read和write函数读写数据;

1.1 socket 结构

  TCP的socket就是一个文件流,Linux中socket就是以文件的形式存在的。初次之外还存在文件描述符,写入和读出也是通过文件描述符。

  每个进程都有有一个数据结构 task_struc,里面指向一个文件描述符数组(fds),列出这个进程打开的所有文件的文件描述符。文件描述符是一个整数,是这个数组的下标。

  数组中的内容是一个指针,指向内核中所有打开的文件的列表(file list);

  每个文件都有一个 inode,普通文件的inode保存在硬盘上,socket的inode保存在内存中;

  在这个inode中,指向了socket在内核中的 socket结构

  socket主要是一个发送队列和接收队列,两个队列里面保存的是一个缓存sk_buff。这个缓存里面可以看到完整的包结构。

  下面是一个整体的结构图。

  

 

2. 基于UDP的Socket函数的调用过程

  a. bind 一个IP和端口号;

  b. 不需要维护连接状态,一个socket就可以和多个客户端通信;

  c. 没有连接状态,所以每次通信都调用 sendto recvfrom,都可以传入IP和端口; 

  

3. 连接的处理方式

3.1 多进程方式

  Linux下通过fork子进程,让子进程去处理socket,在Linux内核中,会复制文件描述符列表,内存空间,当前程序执行到哪一行的记录;

  fork()子进程后会返回一个整数,代表子进程的ID;

  进程调用fork函数时,返回0代表为子进程,返回其他整数代表为父进程;

  

 

3.2 多线程方式

  在Linux下,通过pthread_create创建一个线程,也是调用do_fork。

  

 

3.3 IO多路复用,一个线程维护多个socket(轮询)

  一个线程监听全部的socket,通过select函数监听文件描述符集合,

  一旦有变化,就依次查看每个文件描述符

  将发生变化的文件描述符在fd_set对应的位设置为1 ,表示socket可读或者可写,

  然后继续监听;

3.4 IO多路复用,基于事件通知

  事件通知在Linux中通过epoll函数实现,通过注册callback函数的方式,某个文件描述符发生变化时主动通知。

  Linux中通过epoll_create创建epoll对象,它本身也是一个文件,它里面有一个红黑树结构,保存着监听的所有socket

  当epoll_ctl添加一个socket的时候,其实是加入这个红黑树,同时红黑树的节点指向一个结构,这个结构挂在被监听的socket事件列表中。

  当一个socket来了一个事件之后,可以在这个列表中得到epoll对象,并调用callback通知他。

  

 

  

posted @ 2018-12-11 22:19  小小忧愁米粒大  阅读(235)  评论(0编辑  收藏  举报
瞅啥瞅,好好看书