网络通信之socket函数简述
socket函数原型:
/* Create a new socket of type TYPE in domain DOMAIN, using
protocol PROTOCOL. If PROTOCOL is zero, one is chosen automatically.
Returns a file descriptor for the new socket, or -1 for errors. */
extern int socket (int __domain, int __type, int __protocol) __THROW;
socket函数原型:
/* Create a new socket of type TYPE in domain DOMAIN, using protocol PROTOCOL. If PROTOCOL is zero, one is chosen automatically. Returns a file descriptor for the new socket, or -1 for errors. */ extern int socket (int __domain, int __type, int __protocol) __THROW;
参数一:协议族信息可选字段
AF_INET IPv4网络协议中采用的地址族
AF_INET6 IPv6网络协议中采用的地址族
AF_LOCAL 本地通信中采用的UNIX协议的地址族
参数二:套接字类型
SOCKET_RAW 原始套接字(SOCKET_RAW)允许对较低层次的协议直接访问,比如IP、 ICMP协议 SOCK_STREAM SOCK_STREAM是数据流,一般为TCP/IP协议的编程 SOCK_DGRAM SOCK_DGRAM是数据报,一般为UDP协议的网络编程
参数三:最终采用的协议
传输协议 IPPROTO_TCP 和 IPPTOTO_UDP,一般默认为0(由系统自动选择)
socket的返回值是一个描述符,它具有其他Unix描述符的所有特性:可以用这个描述符调用read和write;可以用dup复制它,在调用了fork后,父进程和子进程可以共享它;可以调用fcntl来改变它的属性,可以调用close来关闭它,等等。插口描述符是sendto和recvfrom等函数的第一个参数,也是进行通信所不可或缺的。当程序终止时 (通过调用exit),所有打开的描述符,包括插口描述符都会被内核关闭。
进程调用socket时,内核会创建一些数据结构。首先从进程的进程表表项开始。在每个进程的生存期内都会有一个对应的进程表表项存在。
看下图:
proc{ } 在此代表进程表项,在这个结构中的项p_fd 指向进程的filedesc结构。其中有两个我们现在关心的成员:一个是 fd_ofileflags ,它是一个字符数组指针 (每个描述符有一个描述符标志,此处不做过多介绍 );一个是fd_ofiles,它是一个指向文件表结构的指针数组的指针 。项fd_ofiles指向的数据结构用*file{ } [ ]来表示。它是一个指向file结构的指针数组。这个数组及描述符标志数组的下标就是描述符本身: 0、 1、 2(分别对应着标准输入、标准输出、标准错误,是由系统默认打开的三个描述符)等等,是非负整数。在上图中可以看到描述符 0、 1、 2对应的项指向图底部的同一个 f i l e结构(由于这三个描述符都对应终端设备)。描述符3对应的项指向另外一个 f i l e结构,是我们感兴趣的socket结构和它所指向的Internet专用数据结构。
结构file的成员f_type指示描述符的类型是DTYPE_SOCKET和DTYPE_VNODE。 v-node是一个通用机制,允许内核支持不同类型的文件系统—磁盘文件系统、网络文件系统(如NFS )、CD-ROM文件系统、基于存储器的文件系统等等,TCP/IP插口的类型总是DTYPE_SOCKET。
结构f i l e的成员f _ d a t a指向一个s o c k e t结构或者一个v n o d e结构,根据描述符类型而定。成员f _ o p s指向一个有 5个函数指针的向量。这些函数指针用在 r e a d、 r e a d v、w r i t e、 w r i t e v、 i o c t l、 s e l e c t和c l o s e系统调用中,这些系统调用需要一个插口描述符或非插口描述符。这些系统调用每次被调用时都要查看f _ t y p e的值, 然后做出相应的跳转,实现者选择了直接通过f i l e o p s结构的相应项来跳转的方式。
当描述符的类型是DTYPE_SOCKET时,结构file指向结构socket。在我们的例子中,socket的类型(数据报插口的类型是SOCK_DGRAM)保存在成员so_type中。还分配了一个Internet协议控制块(PCB):一个inpcb结构。结构socket的成员so_pcb指向inpcb,并且结构inpcb的成员inp_socket指向结构socket。对于一个给定插口的操作可能来自两个方向:“上”或“下”,因此需要有指针来互相指向。
1)当进程执行一个系统调用时,如sendto,内核从描述符值开始,使用fd_ofiles索引到file结构指针向量,直到描述符所对应的file结构。结构file指向socket结构,结构socket带有指向结构inpcb的指针。
2)当一个UDP数据报到达一个网络接口时,内核搜索所有UDP协议控制块,寻找一个合适的,至少要根据目标UDP端口号,可能还要根据目标IP地址、源IP地址和源端口号。一旦定位所找的inpcb,内核就能通过inp_socket指针来找到相应的socket结构。
成员inp_faddr和inp_laddr包含远地和本地IP地址,而成员inp_fport和inp_lport包含远地和本地端口号。IP地址和端口号的组合经常叫做一个插口。至此,我们申请到了最小的可用文件描述符,并且内核已经创建了相应的用来通信的数据结构,接下来进程将通过bind、listen、accept等系统调用来配置系统,完成通信。