本章详细介绍了套接字编程的几个接口:
1. socket
#include <sys/socket.h>
原型: int socket(int family, int type, int protocol);
成功返回套接字描述符,失败返回-1
family 可选参数:
AF_INET IPV4协议
AF_INET6 IPV6协议
AF_LOCAL UNIX域协议
AF_ROUTE 路由套接字
AF_KEY 密钥套接字
一般都选用AF_INET
type可选参数:
SOCK_STREAM 字节流套接字(默认TCP协议)
SOCK_DGRAM 数据报套接字(默认UDP协议)
SOCK_SEQPACKET 有序分组套接字
SOCK_RAW 原始套接字
protocol 可选参数:
IPPROTO_TCP
IPPROTO_UDP
IPPROTO_SCTP
通常可以填0,会选择有family 和 type组合的默认值
这个接口与OPEN类似,在文件IO之前也需要打开一个描述符。
2. bind
原型:
int bind(int sockfd, const struct sockaddr * &myaddr, socklen_t addrlen);
成功返回0, 失败返回-1
用于将IP地址,端口绑定到套接字上,IP和端口号都可以指定为0(表示由系统指定)通常服务器程序会调用这个函数指定一个周知的端口号。
客户程序一般不调用该接口。
3. connect
原型:
int connect(int sockfd, const struct scokaddr * servaddr, socklen_t addrlen);
成功返回0, 出错返回-1
由客户端调用这个函数来连接服务器,如果是TCP程序,调用后将发送SYN字节试图建立三次握手,在握手期间一直阻塞直到建立连接、错误或者超时。
4. listen
原型:
int listen(int sockfd, int backlog);
成功返回0, 出错返回-1
通常在服务端bind之后调用,接着调用accept,主要作用有2个:
1)将套接字修改为被动套接字(等待连接), 默认socket创建的套接字为主动套接字。如果不调用listen直接调用accept将出错。error = EINVAL
2) 由backlog指定最大的连接个数。
内核为任何一个监听套接字,维护2个队列, 一个未完成连接队列(开始握手单还没有成功),一个已完成连接队列(3次握手成功),当连接成功后就连接就自动有未完成队列转移到已完成链接队列中。
5. accept
原型:int accept(int sockfd , struct sockaddr * cliaddr, socklen_t* addrlen);
成功则返回已建立连接的套接字描述符, 失败则为-1
其中第二个参数用于获取客户的IP和端口,如果不关心可以设为NULL, addrlen 是一个输入输出参数,输入时指定套接字地址结构体的最大大小,输出时返回其实际大小,也可以设为NULL
调用这个函数后将在已完成队列中返回下一个连接,如果队列为空,就阻塞,等待客户连接。
并发服务器中通常是在一个循环中调用该函数,然后fork一个子进程进程处理,父进程则关闭该接口返回的描述符,继续等待下一个连接。
6. close , shutdown, getsockname, getpeername
close 用于关闭一个连接,不过只有当该连接指针计数为1时才能关闭,类似于OPEN打开一个文件,若打开的描述符被复制了,unlink也只是让其计数减1 一样
shutdown 则可以强制关闭该连接
getsockname 用于获取该连接自身的IP和端口号, 客户端可以用这个接口来获取内核指定的端口号和ip
getpeername 用于获取该连接对方的IP和端口号, 这可以用于fork之后exec程序中, 获取对方IP和端口