网络编程中,socket是基础,理解socket的常用接口及其参数也成为网络编程的入门课。常用接口包括:
创建 socket bind listen accept connect
收发 read/recv/recvfrom write/send/sendto
关闭 close shutdown
参数 getsockopt/setsockopt
地址 gethostbyaddr getaddrbyhost,结构体 sockaddr
获取本端或对端的地址 getsockname、getpeername
- socket
#include <sys/socket.h>
int socket(int domain, int type, int protocol)
若成功返回0,出错返回-1
domain 指定通信的特性。POSIX指定的域包括AF_NET(IPV4因特网)、AF_NET6(IPV6因特网)、AF_UNIX(UNIX域)、AF_UNSPEC(未指定)。多数系统还会定义AF_LOCAL域,这是AF_UNIX的别名。
type 指定套接字的类型。POSIX定义的套接字的类型包括SOCK_DGRAM(长期固定的、无连接的不可靠报文传递)、SOCK_RAW(IP协议的数据报接口)、SOCK_SEQPACKET(长度固定、有序、可靠的面向链接的协议)、SOCK_STREAM(有序、可靠、双向的面向链接字节流)。
protocol 通常为0,表示按照给定的域和套接字类型选择默认协议。在AF_NET协议域中,SOCK_DGREM默认协议是UDP协议(用户数据报协议),SOCK_STREAM默认协议是TCP协议(传输控制协议)。
注:UDP协议与对方通信是不需要逻辑链接的。只需要送出一个报文,其地址是一个对方进程所使用的套接字。
- bind
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr* addr, socklen_t len)
若成功返回0,出错返回-1
该函数用于服务器端绑定一个周所知的地址,便于客户端进行链接通信。
对于因特网域,如果对指定IP地址为INADDR_ANY,套接字端口可以绑定到服务器上的所有网络接口。一般对于多网卡侦听的服务器,可以设置器服务地址为INADDR_ANY,这样对于多网卡都可以接收客户端请求。多网卡在解决网络IO导致的服务器性能瓶颈问题中算是一个解决方案。
- getsockname
#include <sys/socket.h>
int getsockname(int sockfd, struct sockaddr* restrict addr, socklen_t* restrict alenp)
若成功返回0,出错返回-1
在调用getsockname之前,设置alenp为一个指向整数的指针,该整数指定缓冲区sockaddr的大小。返回时,该整数会被设置成返回地址的大小。如果该地址和提供的缓冲区的长度不匹配,则将其截断而不报错。如果当前没有绑定到该套接字的地址,结果未定义。
- getpeername
#include <sys/socket.h>
int getpeername(int sockfd, struct sockaddr* restrict addr, socklen_t*restict alenp)
若成功返回0,出错返回-1
用法同getsockname
- listen
#include <sys/socket.h>
int listen(int sockfd, int backlog)
若成功返回0,出错返回-1
backlog表示该进程要求入队列的连接请求数目。其实际值由系统决定,但是上限在<sys/socket.h>中SOMAXCONN指定。Linux下TCP为128。
一旦队列满,系统会拒绝多余的连接请求,所以backlog应该基于服务器期望负载和接收连接请求与启动服务的处理能力来选择。
- accept
#include <sys/scoket.h>
int accept(int sockfd, struct sockaddr* restrict addr, socklen_t* restrict alenp)
若成功返回文件(套接字)描述符,出错返回-1
sockfd参数为listen指定的侦听套接字,这个套接字不会关联到客户端connect的套接字,而是继续进行侦听接收其它连接请求。
如果不关心客户端标识,可以将参数addr和alenp设置为NULL;如果参数addr和alenp非NULL,则accept成功后,addr指向的缓冲区保存客户端的标识信息。
注:如果没有链接请求,accept会阻塞直到一个请求到来。如果sockfd处于非阻塞模式,accept会返回-1,error设置为EAGAIN或EWOULDBLOCK。accept成功后,如果客户端往连接套接字中写入数据,则服务器端对应的套接字的事件为可读。
- connect
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr* addr, socklen_t len)
若成功返回0,出错返回-1
如果sockfd没有绑定到一个地址,则connect会给调用者绑定一个地址。这是一般的客户端的用法。
connect可能的出错原因:服务没有启动,服务等待队列空间不足。因此需要对connect的返回错误进行处理。
如果套接字出于非阻塞模式下,链接不能马上建立时,connect将会返回-1,并且将error设置为EINPROGRESS。应用可以使用poll或select或epoll判断文件描述符何时可写。如果可写,连接完成。
注:connect成功后,sockfd的事件为可写。
connect还可以用于SOCK_DGRAM,实际上并未建立连接,只是在以后每次传送报文时不需要再提供地址。另外仅能接收来至指定地址的报文。仅此而已。
- send
#include <sys/socket.h>
ssize_t send(int sockfd, const void* buf, size_t nbytes, int flags)
若成功则返回发送的字节数,若出错则返回-1
send和write很像,但是可以指定标志来改变处理传输数据的方式。send支持四个标志:
MSG_DONTROUTE
MSG_DONTWAIT 允许非阻塞操作,相当于使用O_NONBLOCK
MSG_EOR
MSG_OOB 发送带外数据
注:send成功返回,并不表示对端进程接收数据,只表示本端将数据正确发到网络上。对于字节流协议,send会阻塞知道所有数据传输完毕。
- sendto
#include <sys/socket.h>
ssize_t sendto(int sockfd, cosnt void* buf, size_t nbytes, int flags, const struct sockaddr*destaddr, socklen_t destlen)
若成功则返回发送的字节数,若出错则返回-1
sendto和send类似,区别在域sendto允许在无连接的套接字上指定一个目标地址。
- sendmsg
#include <sys/socket.h>
ssize_t sendmsg(int sockfd, const struct msghdr*msg, int flags)
若成功返回发送的字节数,若出错则返回-1
- recv
#include <sys/socket.h>
ssize_t recv(int sockfd, void*buf, size_t nbytes, int flags)
返回值:以字节计数的消息长度,若无可用的消息或对方已经按序结束则返回0,若出错则返回-1
recv和read很像,但是允许指定选项来控制如何接收数据。
MSG_OOB 接收带外数据
MSG_PEEK 返回报文内容,只是查看,而不真正取走报文。再次调用read或者recv的时候才取走数据。
MSG_TRUNC 即使报文被截断,要求返回的是报文的实际长度
MSG_WAITALL 直到所有的数据可用(仅SOCK_STREAM)
注:如果发送者已经调用shutdown关闭,或者发送端已经关闭,那么当所有的数据接收完毕后,recv返回0。
- recvfrom
#include <sys/socket.h>
ssize_t recvfrom(int sockfd, void*buf, size_t nbytes, int flags, struct sockaddr* restrict addr, socklen_t* restrict alenp)
返回值:以字节计数的消息长度,若无可用的消息或对方已经按序结束则返回0,若出错则返回-1
recvfrom和recv很像,不同之处是可以用于定位发送者,如果addr和alenp不为NULL,则保存发送者地址信息。
- recvmsg
#include <sys/socket.h>
ssize_t recvmsg(int sockfd, void*buf, size_t nbytes, int flags, struct sockaddr* restrict addr, socklen_t* restrict alenp)
- read
#include <unistd.h>
ssize_t read(int filedes, void* buf, size_t nbyes)
返回值:若成功,返回读到的字节数,若已经到文件结尾则返回0,若出错则返回-1
当从网络读时,网络中的缓冲机制可能造成返回值小于所要求的字节数。
- write
#include <unistd.h>
ssize_t write(int filedes, const void*buf, size_t nbytes)
返回值:若成功,返回已写的字节数,若出错则返回-1
- setsockopt
#include <sys/socket.h>
int setsockopt(int sockfd, int level, int option, const void* val, socklen_t len)
返回值:若设置成功返回0,若出错则返回-1
level 标识选项应用的协议,如果选择通用的套接字层选项,level设置成SOL_SOCKET;否则,level是设置成控制这个选项的协议号。
val 根据选项不同指向一个数据结构或者一个整数。一些选项是on/off开关。
len 指定了val指向的对象的大小。
- getsockopt
#include <sys/socket.h>
int getsockopt(int sockfd, int level, int option, const void* val, socklen_t* lenp)
- shutdown
- close
#include <unistd.h>
int close(int filedes)
返回值:若成功则返回0,若出错则返回-1
注:关闭一个文件描述符,系统并不一定马上释放该文件描述符对应的资源,只有在关闭了最后一个引用后才释放。