原创 《TCP/IP详解,卷2:实现》读书笔记-SOCKET的结构和系统调用收藏

    插口,也就是socket,从事IT的地球人都知道,既然谈到了socket,也来简单介绍一下socket的由来。
80年代初,美国国防部高级研究计划署ARPA让California大学在UNIX操作系统下实现TCP/IP协议,Berkley提出了为UNIX操作系统开发的网络通信接口Socket,它是建立在传输层协议(主要是TCP和IP)上的一种套接字规范,因此人们也将Socket接口称为BerkeleySocket。Socket概念最早出现于1983年的4.2BSD版本中,它的主要目的是提供一个统一的访问网络和进程间通信协议的接口。
    Socket接口定义了许多函数例程,可以用它们来开发TCP/IP网络上的应用程序。它定义了两台计算机间的通信规范。如果两台计算机是利用一个“通道”进行通信,那么这个“通道”的两端就是套接字。Socket屏蔽了底层通信软件和具体操作系统的差异,使得任何两台安装了TCP协议软件和实现了Socket规范的计算机之间的通信成为可能。
    插口代表一条通信链路的一端,存储或指向与链路有关的所有信息。这些信息包括:使用的协议、协议的状态信息(包括源和目的地址)、到达的连接队列、数据缓存和可选标志。
struct socket { 
        
short        so_type;                /* generic type, see socket.h*/ 
        
short        so_options;                /* from socket call, see socket.h*/ 
        
short        so_linger;                /* time to linger while closing*/ 
        
short        so_state;                /* internal state flags SS_*, below*/ 
        caddr_t        so_pcb;                        
/* protocol control block*/ 
        
struct        protosw *so_proto;        /* protocol handle*/ 

/** Variables for connection queueing. 
 * Socket where accepts occur is so_head in all subsidiary sockets. 
 * If so_head is 0, socket is not related to an accept. 
 * For head socket so_q0 queues partially completed connections, 
 * while so_q is a queue of connections ready to be accepted. 
 * If a connection is aborted and it has so_head set, then 
 * it has to be pulled out of either so_q0 or so_q. 
 * We allow connections to queue up based on current queue lengths 
 * and limit on number of queued connections for this socket. 
 
*/ 
        
struct        socket *so_head;        /* back pointer to accept socket*/ 
        
struct        socket *so_q0;                /* queue of partial connections*/ 
        
struct        socket *so_q;                /* queue of incoming connections*/ 
        
short        so_q0len;                /* partials on so_q0*/ 
        
short        so_qlen;                /* number of connections on so_q*/ 
        
short        so_qlimit;                /* max number queued connections*/ 
        
short        so_timeo;                /* connection timeout*/ 
        u_short        so_error;                
/* error affecting connection*/ 
        pid_t        so_pgid;                
/* pgid for signals*/ 
        u_long        so_oobmark;                
/* chars to oob mark*/ 

/* 
 * Variables for socket buffering. 
 
*/ 
        
struct        sockbuf { 
                u_long        sb_cc;                
/* actual chars in buffer*/ 
                u_long        sb_hiwat;        
/* max actual char count*/ 
                u_long        sb_mbcnt;        
/* chars of mbufs used*/ 
                u_long        sb_mbmax;        
/* max chars of mbufs to use*/ 
                
long        sb_lowat;        /* low water mark*/ 
                
struct        mbuf *sb_mb;        /* the mbuf chain*/ 
                
struct        selinfo sb_sel;        /* process selecting read/write*/ 
                
short        sb_flags;        /* flags, see below*/ 
                
short        sb_timeo;        /* timeout for read/write*/ 
        } so_rcv, so_snd; 

        caddr_t        so_tpcb;                
/* Wisc. protocol control block XXX*/ 
        
void        (*so_upcall) __P((struct socket *so, caddr_t arg, int waitf)); 
        caddr_t        so_upcallarg;                
/* Arg for above*/ 
}; 
so_type由产生插口的进程来指定,它指明插口和相关协议支持的通信语义。对于UDPso_type等于SOCK_DGRAM,而对于TCPso_type则等于SOCK_STREAM
so_options是一组改变插口行为的标志。通过getsockoptsetsockopt系统调用进程能修改除SO_ACCEPTCONN外所有的插口选项。当在插口上发送listen系统调用时,SO_ACCEPTCONN被内核设置。 so_linger等于当关闭一条连接时插口继续发送数据的时间间隔。
so_state表示插口的内部状态和一些其他的特点。有这样两个标志需要重点注意一下:SS_NBIOSS_ASYNC标志。在默认情况下,进程在发出I/O请求后会等待资源。例如,对一个插口发read系统调用,如果当前没有网络上来的数据,则read系统调用就会被阻塞。同样,当一个进程调用write系统调用时,如果内核中没有缓存来存储发送的数据,则内核将阻塞进程。如果设置了SS_NBIO,在对插口执行I/O操作且请求的资源不能得到时,内核并不阻塞进程,而是返回EWOULDBLOCK。这就是我们在socket编程中通常要遇到的阻塞操作和非阻塞操作。
so_pcb指向协议控制块,协议控制块包含与协议有关的状态信息和插口参数。每一种协议都定义了自己的控制块结构,所以so_pcb被定义成一个通用的指针。
so_proto指向进程在socket系统调用中选择的协议的protosw结构。
so_q0队列中放着还没有完全建立的连接(TCP的三次握手还没完成),队列的长度为so_q0len
so_q队列中放着已经建立的连接或将被接受的连接(TCP的三次握手已完成),队列的长度为so_qlen
每一个socket都有自己的so_head,在每一个被排队的插口中,so_head指向设置了SO_ACCEPTCONN的源插口。
插口上可排队的连接数通过so_qlimit来控制,进程可以通过listen系统调用来设置so_qlimit
每一个插口包括两个数据缓存,so_rcvso_snd,分别用来缓存接收或发送的数据。
sb_mb指向mbuf链的第一个mbuf
sb_cc的值等于存储在mbuf链中的数据字节数。
sb_hiwatsb_lowat用来调整插口的流控算法。
sb_mbcnt等于分配给缓存中的所有mbuf的存储器数量。
sb_sel是一个用来实现select系统调用的selinfo结构。
sb_flags表示sockbuf的属性。
sb_timeo用来限制一个进程在读写调用中被阻塞的时间,默认值为0,表示进程无限期的等待。
socket系统调用
socket系统调用产生一个新的插口,并将插口同进程在参数domaintypeprotocol中指定的协议联系起来。该函数(如图15-14所示)分配一个新的描述符,用来在后续的系统调用中标识插口,并将描述符返回给进程。
bind系统调用将一个本地的网络运输层地址和插口联系起来。一般来说,作为客户的进程并不关心它的本地地址是什么。在这种情况下,进程在进行通信之前没有必要调用bind;内核会自动为其选择一个本地地址。服务器进程则总是需要绑定到一个已知的地址上。所以,进程在接受连接(TCP)或接收数据报(UDP)之前必须调用bind,因为客户进程需要同已知的地址建立连接或发送数据报到已知的地址。
listen系统调用的功能是通知协议进程准备接收插口上的连接请求。它同时也指定插口上可以排队等待的连接数的门限值。超过门限值时,插口层将拒绝进入的连接请求排队等待。当这种情况出现时,TCP将忽略进入的连接请求。
调用listen后,进程调用accept等待连接请求。accept返回一个新的描述符,指向一个连接到客户的新的插口。原来的插口仍然是未连接的,并准备接收下一个连接。如果name指向一个正确的缓存,accept就会返回对方的地址。
服务器进程调用listenaccept系统调用等待远程进程初始化连接。如果进程想自己初始化一条连接(即客户端),则调用connect。对于面向连接的协议如TCPconnect建立一条与指定的外部地址的连接。如果进程没有调用bind来绑定地址,则内核选择并且隐式地绑定一个地址到插口。对于无连接协议如UDPICMPconnect记录外部地址,以便发送数据报时使用。

发表于 @ 2007年04月16日 21:59:00|评论(loading...)

新一篇: 《TCP/IP详解,卷2:实现》读书笔记-协议控制块 | 旧一篇: 《TCP/IP详解,卷2:实现》读书笔记-IP分片和重组

Csdn Blog version 3.1a
Copyright © stevenmou