1 socket()
socket() 创建用于通信的终结点,并返回引用该终结点的文件描述符。成功调用返回的文件描述符是当前未被调用socket()的进程占用的最小的文件描述符。
1.1 包含头文件
#include <sys/types.h>
#include <sys/socket.h> //包含域参数所需要的各种网络协议
1.2 函数主体
int socket(int domain, int type, int protocol);
//例如:
int clt = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
参数解释:
- int domain
域参数指定通信域,即选择用什么样的通信协议,包括 AF_UNIX,、AF_LOCAL、AF_INET、AF_INET6、AF_IPX、AF_ALG等等。目前主要使用的为**AF_LOCAL(用于本地进程间的通信)、AF_INET(用于IPv4协议)。**其余的有需要时在做补充。
- int type
套接字具有指示的类型,该类型指定通信语义,包括SOCK_STREAM、SOCK_DGRAM、SOCK_SEQPACKET、SOCK_RAW 、SOCK_RDM等。目前主要使用的为**SOCK_STREAM(实现TCP字节流通信。可能支持带外数据传输机制。)、SOCK_DGRAM(实现UDP数据报通信)。**其余的有需要时在做补充
从 Linux 2.6.27 开始,类型参数还可以实现对socket的进一步控制,通过按为OR操作实现以下操作:
SOCK_NONBLOCK : 设置socket为非阻塞类型,在此处设置可以节省对fcntl的额外调用,进一步减小开销
SOCK_CLOEXEC : 在进程调用exec函数族时,文件描述符就会自动关闭,无需手动关闭。
这里对SOCK_CLOEXEC和带外数据(out-of-band data)作进一步解释(新手可以不看):
在多进程的场景中,在fork()函数后,由于子进程会拷贝父进程几乎所有的内容,这其中就包括了父进程打开的文件描述符,因此子进程和父进程使用的是相同的文件描述符。但是,当子进程执行exec()函数族时,由于子进程会使用新的内容覆盖父进程所有的数据,这其中就包括打开的文件描述符,此时打开的文件描述符就无法关闭了,因此在调用exec函数族前需要手动关闭文件描述符。而设置此参数即可以避免这种情况
带外数据(out-of-band data)是指建立连接双方中的一方有重要的事情需要及时通知对方,这种数据成为带外数据,发送优先级要高于带内数据(在消息缓存队列中排队的数据成为“带内数据”),带外数据发送不需要建立新的连接。对带外数据的实现一般使用BSD的紧急数据指针方式,需要接收方通过判断read()函数的返回值来判断带外数据。
- int protocol
用来指定协议的特类型,一般设置为0,表示接收协议的任何类型,后续的处理由应用程序自己实现。
1.3 返回值
成功后,将返回新套接字的文件描述符。出错时,返回 -1,并正确设置 errno,错误类型如下:
错误类型 | 解释 |
---|---|
EACCES | 创建指定类型和/或协议的套接字的权限被拒绝。 |
EAFNOSUPPORT | 不支持指定的地址系列。 |
EINVAL | 未知的协议,或协议系列不可用。或类型中的标志无效。 |
EMFILE | 已达到每个进程对打开的文件描述符数的限制。 |
ENFILE | 已达到系统范围内对打开的文件总数的限制。 |
ENOBUFS or ENOMEM | 可用内存不足。 在释放足够的资源之前,无法创建套接字。 |
EPROTONOSUPPORT | 此域中不支持协议类型或指定的协议。 |
1.4 结束
socket()函数介绍至此结束,虽然这个函数的使用一般来说并不会出错,但是建议养成良好的编程习惯,每次调用都对器返回值进行判断。