文章目录
- 一、socket
- 1、创建socket
- 2、网络通信流程
- 3、accept()函数
- 4、signal()函数
- 5、recv()函数
- 6、connect()函数
- 二、I/O多路复用
- 1.select模型
- 2.poll模型
- 3.epoll模型
- 注
一、socket
1、创建socket
int socket(int domain,int type,int protocol);
//返回值:一个有效的socket,失败时返回-1,errno被设置。
(1)domain:
PF_INET IPv4互联网协议族
PF_INET6 IPv6互联网协议族
PF_LOCAL 本地通信的协议族
PF_PACKET 内核底层协议族
PF_IPX IPX Novell协议族
(2)type数据传输的类型
2.1 SOCK_STREAM
面向连接的socket:
1)数据不会丢失
2)数据顺序不会错乱
3)双向通道
2.2 SOCK_DGRAM
无连接的socket:
1)数据可能会丢失
2)数据顺序可能错乱
3)传输的效率更高
(3)protocol最终使用的协议
在IPv4中,数据传输方式为SOCK_STREAM的协议只有IPPROTO_TCP,数据传输方式为SOCK_DGRAM的协议只有IPPROTO_UDP,也可填0.
2、网络通信流程
3、accept()函数
accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
//是在一个套接口接受的一个连接。用于在服务器端接受客户端连接的系统调用。
//参数:
sockfd:套接字描述符,该套接口在listen()后监听连接。
addr:(可选)指针,指向一缓冲区,其中接收为通讯层所知的连接实体的地址。Addr参数的实际格式由套接口创建时所产生的地址族确定。
addrlen:(可选)指针,输入参数,配合addr一起使用,指向存有addr地址长度的整型数。
4、signal()函数
//设置某一信号的对应动作
#include<signal.h>
void ( *signal( int signum,void(* handler)(int)) ) (int);
或者:typedef void (*sig_t)( int );
sig_t signal(int signum,sig_t handler);
//参数:
1.signum:指明了所要处理的信号类型,它可以取除了SIGKILL和SIGSTOP外的任何一种信号。
2.handler:描述了与信号关联的动作,它可以取以下三种值:
(1)一个无返回值的函数地址
此函数必须在signal()被调用前申明,handler中为这个函数的名字。当接收到一个类型为signum的信号时,就执行handler 所指定的函数。这个函数应有如下形式的定义:
void func(int sig);
(2)SIG_IGN
这个符号表示忽略该信号,执行了相应的signal()调用后,进程会忽略类型为sig的信号。
(3)SIG_DFL
这个符号表示恢复系统对信号的默认处理。
5、recv()函数
int recv(SOCKET s, char FAR*buf, int len, int flags);
//是一个用于从套接字接收数据的函数。
//返回值:如果接收到的字节数大于0,表示实际复制到缓冲区的数据量。如果返回0,表示对方已关闭连接。如果返回SOCKET_ERROR,表示出现了错误。
1.参数:
s:套接字的描述符,指定接收数据的端点。
buf:一个指向缓冲区的指针,用于存放接收到的数据。
len:缓冲区buf的长度。
flags:通常设置为0,但可以用于控制函数的行为,如使用MSG_PEEK查看数据或使用MSG_OOB处理带外数据。
2.函数行为:
(1)如果套接字处于阻塞模式,recv将一直等待直到有数据可读或发生错误。
(2)如果套接字处于非阻塞模式,并且没有数据可读,recv将返回SOCKET_ERROR并设置WSAEWOULDBLOCK错误。
(3)在数据接收过程中,如果网络出现错误,recv将返回SOCKET_ERROR。
(4)如果远程端正常关闭连接,对于面向连接的套接字(如SOCK_STREAM),recv将立即返回,接收0个字节。
(5)如果连接被重置,对于面向连接的套接字,recv将失败并显示错误WSAECONNRESET。
6、connect()函数
#include <sys/socket.h>
int connect(int sockfd, struct sockaddr * serv_addr, int addrlen);
//用于建立与指定socket的连接。
//返回值:成功则返回0, 失败返回-1, 错误原因存于errno 中.
参数:
sockfd:标识一个套接字。
serv_addr:套接字s想要连接的主机地址和端口号。
addrlen:name缓冲区的长度。
二、I/O多路复用
1.select模型
int select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* errorfds, struct timeval* timeout);
//select函数用于检测一组socket中是否有事件就绪,它能够监视我们需要监视的文件描述符的变化情况——读写或是异常 。
//返回值:准备就绪的描述符数,若超时则返回0,若出错则返回-1。
1.nfds:select监视的文件句柄数,视进程中打开的文件数而定,一般设为你要监视各文件中的最大文件号加一。
2.readfds:select监视的可读文件句柄集合。
3.writefds: select监视的可写文件句柄集合。
4.exceptfds:select监视的异常文件句柄集合。
5.timeout:本次select()的超时结束时间。(见/usr/sys/select.h,可精确至百万分之一秒!)
FD_ZERO(fd_set *fdset):清空fdset与所有文件句柄的联系。
FD_SET(int fd, fd_set *fdset):建立文件句柄fd与fdset的联系。
FD_CLR(int fd, fd_set *fdset):清除文件句柄fd与fdset的联系。
FD_ISSET(int fd, fd_set *fdset):检查fdset联系的文件句柄fd是否可读写,当>0表示可读写。
存在的问题:
(1)采用轮询的方式扫描bitmap,性能会随着socket数量增多而下降。
(2)每次调用select(),需要拷贝bitmap。
(3)bitmap的大小(单个进/线程打开的socket数量)由FD_SETSIZE宏设置,默认是1024个,可以修改,但是效率将降低。
2.poll模型
int poll(struct pollfd fds[], nfds_t nfds, int timeout);
//用于监视多个文件描述符的状态变化。
/*返回值:
如果函数调用成功,则返回所有事件就绪的文件描述符个数。
如果timeout时间耗尽,返回0。
如果函数调用失败,返回-1,同时错误码会被设置。
*/
struct pollfd {
int fd; /*文件描述符*/
short events; /* 等待的需要测试事件 */
short revents; /* 实际发生了的事件,也就是返回结果 */
};
1.fds:指向一个结构体数组的指针,每个数组元素都是一个struct pollfd结构,用于指定测试某个给定的fd的条件。struct pollfd包含三个成员:fd(文件描述符),events(等待的事件),revents(实际发生的事件)。
2.nfds:表示fds数组的长度。
3.timeout:表示poll函数的超时时间,单位是毫秒(ms)。如果timeout的取值为-1,则poll调用后进行阻塞等待,直到被监视的某个文件描述符上的某个事件就绪;如果timeout的取值为0,则poll调用之后进行非阻塞等待,无论被监视的文件描述符上的事件是否就绪,poll检测之后都会立即返回;如果timeout的取值为特定的时间值,则poll调用后在指定的时间内进行阻塞等待,如果被监视的文件描述符上一直没有事件就绪,则在该时间后poll进行超时返回。
存在的问题:
(1)poll的数据结构是数组,转入内核后转换成了链表。
(2)每调用一次poll拷贝一次结构体数组(select()需要拷贝两次bitmap)。
(3)监视的连接数没有1024的限制。但是使用遍历的方法,监视越多,效率越低。
3.epoll模型
int epoll_create(int size)
//创建一个epoll的句柄,size用来告诉内核这个监听的数目一共有多大。成功时返回epoll文件描述符,失败时返回-1。
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
//epoll的事件注册函数,它不同与select()是在监听事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型。第一个参数是epoll_create()的返回值,第二个参数表示动作,用三个宏来表示:EPOLL_CTL_ADD:注册新的fd到epfd中;EPOLL_CTL_MOD:修改已经注册的fd的监听事件;EPOLL_CTL_DEL:从epfd中删除一个fd;第三个参数是需要监听的fd,第四个参数是告诉内核需要监听什么事。
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout)
//等待事件的产生,类似于select()调用。参数events用来存储内核得到的事件的集合,maxevents告知内核events的大小,timeout是超时时间。成功时返回有多少文件描述符就绪,时间到时返回0,出错时返回-1。
注
内容来自b站C++网络编程,从Socket基础到Epoll,百度。