网络IO,第一件事情就是调用socket函数,指定期望的通信协议类型
socket(int family,int type,int protocol)
family值的是协议族,常值:AF_INET AF_INET6 AF_UNIX AF_ROUTE AF_KEY
type指的是套接口类型,常值:SOCK_STREAM SOCK_DGRAM SOCK_SEQPACKET SOCK_RAW
protocol指的是协议类别,常值:IPPROTO_TCP IPPROTO_UDP IPPROTO_SCTP
AFXXX PFXXX
AF 指的是addressfamily
PF指的是protocolfamily
AF和PF其实指的是不同的东东,曾经期望的是单个协议族可以支撑多种地址族,但是没有实现,现在一般认为AF==PF
connect函数
connect(int fd,struct sockaddr* server,socklen_t addrlen)
connect 的错误有以下几种情况:
1,发送syn没有ack或者根据预先设定重发规则,还是没有收到ack,则会返回ETIMEDOUT
2,发送syn收到rst,则会返回ECONNREFUSED
3, 如果某个路由器返回一个destination unreachable的ICMP报文,按照预定的重发策略还是没有收到回应,则会返回 EHOSTUNREACH或者ENETUNREACH错误
个人理解第三种情况是第一种情况的一个具体表现。
bind(int fd,struct sockaddr* addr,socklen_t addrlen)
可以不调用bind,直接connect或者listen,不过不bind的listen好像没有太大意义。
listen(int fd,int backlog)
backlog官方文档说的是pending queue不过queue在实现上有没有完成和完成两个队列,一般建议取512.
accept(int fd,struct sockaddr* addr,socklen_t addrlen)
如果对对端的信息不关心,后面两个参数可以选择传递NULL。
fork和exec
fork创建新的进程,exec在新fork创建的新进程上面加载新程序。
fork调用一次返回两次;之所以返回两次是因为fork之后就是两个进程了,父进程和子进程。需要给两个进程分别以返回做控制接下来的流程。
fork的子进程几乎是父进程的拷贝,不过还有一些不一样,比如pid;信号量,锁,cpu使用时间等等,具体的可以man fork。
close
close函数关闭套接字,close执行的操作是将套接字置为关闭,在当前进程不能再使用该文件描述符执行read或者write操作,立即返回,协议栈会发送队列里面剩余数据,完成之后发送FIN。
getsockname和getpeername
获取对于一个连接上的本端和对端的套接口信息
inetd就是使用fork来根据用户的连接类型调用exec加载适当的进程处理用户操作,由于父子进程共享打开的文件描述符,新创建的进程可以通过环境变量或者固定某个套接字来获取对端的信息,inetd使用的是第二种方式,即固定的套接字 0 1 2,不过0 1 2 是标准输入输出以及错误,通过dup2即可复制一个套接字,可能inetd不需要使用 0 1 2这种特殊的套接字吧。