网络Posix API

API

socket

int socket(int family, int type, int protocol);
// socket(AF_INET, SOCK_STREAM, 0); returnTCP套接字
// socket(AF_INET, SOCK_DGRAM, 0); return UCP套接字

bind

int bind(int sockfd, const struct sockaddr *myaddr, socklen_t addrlen);
// IP地址可以设置为通配地址0(宏定义是INADDR_ANY),端口也可以设置为0,这样,ip和端口则由内核选择。如果用户指明了ip和端口,则使用这个IP地址和端口,否则由内核选择。
// 当服务器绑定了通配地址,套接字会接收到达它绑定端口的任何TCP连接。

listen

int listen(int sockfd, int backlog);
// backlog可能是未完成队列和已完成队列总和的最大值

accept

int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);
// 参数cliaddr和addrlen用来返冋已连接的对端进程(客户〕的协议地址,其中addrlen是协议地址的长度,内核根据此长度向cliaddr指向的地址中存入相应的数据

recv

ssize_t recv(int sockfd, void *buf, size_t len, int flags);
// 参数buf指向一个缓冲区,该缓冲区用来存放接收到的数据,,参数len是指明buf的长度,第四个参数一般设置为0
// 接收网络数据的是内核的协议栈,recv只是负责把数据从协议栈拷贝到用户空间。
// 函数返回实际copy的字节数,如果出现错误,会返回-1,如果连接被关闭,会返回0.

send

ssize_t send(int sockfd, const void *buf, size_t len, int flags);
// 参数buf指明一个存放应用程序要发送数据的缓冲区,len指明实际要发送的数据的字节树,参数flags一般是0;
// 函数返回实际copy的字节数,如果出现错误,会返回-1,如果连接被关闭,会返回0

connect

int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);
// sockfd 是系统调用 socket() 返回的套接字文件描述符; serv_addr 是保存着目的地端口和 IP 地址的数据结构 struct sockaddr; addrlen 设置 为 sizeof(struct sockaddr)。

socket特性

socket分为阻塞模式和非阻塞模式,它们是指socket在进行输入输出操作时,是否会等待数据的到来或发送。默认情况下,socket都是阻塞模式,即如果没有数据可读或写,socket会一直等待,知道有数据或超时。非阻塞模式的socket不会等待,二十立即返回一个错误码,表示当前无法进行输入输出操作。

socket设置为非阻塞模式的方法:

// 创建socket的时候
int s = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP);
// 使用fcntl函数
fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL, 0) | O_NONBLOCK);
// 使用ioctl函数:
ioctl(sockfd, FIONBIO, 1);  //1:非阻塞 0:阻塞

阻塞模式下api的特点:

当内核发送缓冲区没有足够的空间时,继续使用send,阻塞模式下,会导致线程阻塞,直到内核中有足够的空间位置;非阻塞模式下,会直接返回错误码(-1);
当内核接收缓冲区中没有数据时,继续使用recv,阻塞模式下,会导致线程阻塞,直到内核中有数据;非阻塞模式下,会直接返回错误码(-1);
调用connect,会发起三次连接,阻塞模式下,会等到完成连接再返回,非阻塞模式下,会直接返回完成。
accept在非阻塞模式下,如果有连接,返回正确的fd,否则返回错误码.

Epoll

Epoll API

int epoll_create(int size); 

size参数表示所要监视文件描述符的最大值,不过在后来的Linux版本中已经被弃用,参数大于0即可,一般为1

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
// struct epoll_event结构:
// struct epoll_event
// {
//   uint32_t events;	/* Epoll events */
//   epoll_data_t data;	/* User data variable */
// }op参数说明:

op参数说明操作类型:

  • EPOLL_CTL_ADD:向interest list添加一个需要监视的描述符
  • EPOLL_CTL_DEL:从interest list中删除一个描述符
  • EPOLL_CTL_MOD:修改interest list中一个描述符

struct epoll_event结构描述一个文件描述符的epoll行为。在使用epoll_wait函数返回处于ready状态的描述符列表时:

  • data域是唯一能给出描述符信息的字段,所以在调用epoll_ctl加入一个需要监测的描述符时,一定要在此域写入描述符相关信息

  • events域是bit mask,描述一组epoll事件,在epoll_ctl调用中解释为:描述符所期望的epoll事件,可多选。

常用的epoll事件是什么:

  • EPOLLIN:描述符处于可读状态
  • EPOLLOUT:描述符处于可写状态
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

阻塞等待注册的事件发生,返回事件的数目,并将触发的事件写入events数组中。

events: 用来记录被触发的events,其大小应该和maxevents一致

maxevents: 返回的events的最大个数

参数timeout描述在函数调用中阻塞时间上限。在文件描述符没有进入ready状态时,-1表示调用将一直阻塞;0表示不管结果怎么样,调用都立即返回;大于0表示调用最多持续timeout时间,超时后,立即返回。

两种触发方式

epoll两种触发方式分别是:边缘触发(ET)和水平触发(LT)。epoll的默认的工作模式是LT模式。

水平触发:

  1. 对于读操作,只要缓冲内容不为空,LT模式返回读就绪。
  2. 对于写操作,只要缓冲区还不满,LT模式会返回写就绪。

边缘触发:
只有当socket的缓冲区状态变化时才触发事件。

  1. 当文件描述符关联的读内核缓冲区由空转化为非空的时候,则发出可读信号进行通知,
  2. 当文件描述符关联的内核写缓冲区由满转化为不满的时候,则发出可写信号进行通知

所以边缘触发需要一次性的把缓冲区的数据读完为止。

ET模式在很大程度上减少了epoll事件被重复触发的次数,因此效率要比LT模式高。epoll工作在ET模式的时候,必须使用非阻塞套接口,以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。

设置边缘触发的方法:

ev.events = EPOLLOUT | EPOLLET;

示例代码:

https://github.com/q962875152/mycode/blob/master/epolltcp.c

本专栏知识点是通过<零声教育>的系统学习,进行梳理总结写下文章,对c/c++linux课程感兴趣的读者,可以点击链接,详细查看详细的服务:链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值