linux网络编程
文章平均质量分 55
城南花已开.jpg
这个作者很懒,什么都没留下…
展开
-
基础回顾----listen函数的backlog参数的本质
在初学网络编程这块时,对listen函数的第二个参数(backlog)简单理解为服务器允许同时连接的客户端个数,但后面总是感觉对这个参数的意义很模糊,在查阅资料后才发现这个参数远远没有表面上那么简单,因此写篇博客总结、记录、巩固一下。先提出两个概念半连接状态队列(syns queue)和完全连接状态队列(accept queue),通过下图可以看到这两个队列的作用:图片来源:关于TCP 半连接队列和全连接队列可以看到linux会将处于SYN_RCVD状态的socket放在半连接队列中,将已经建议好连原创 2020-08-12 00:43:09 · 2133 阅读 · 1 评论 -
进程池实现----高效的半同步/半异步并发模式的进程池
为何要用进程池?当我们在写并发服务器时,若每当一个客户端连接请求到来时,我们就为其创建一个进程来服务。这样动态的创建进程,无疑是比较耗时的,这将导致较慢的客户端相应。进程池的概念在服务器启动前我们就创建一组进程,我们将这组进程在逻辑抽象成一个池子。当我们需要一个工作进程来处理新到来的客户端请求时,我们可以直接从进程池中取得一个执行实体(进程),而无需动态的调用fork函数来创建进程。相比于动态创建子进程,选择一个已经存在的子进程代价显然要小得多。至于主进程如何选择子进程,有两种方式:主进程使用某原创 2020-08-09 19:18:37 · 406 阅读 · 0 评论 -
sendmsg和recvmsg的应用----在进程之间传递描述符
在进程之间传递描述符,我们通常会想到fork一个子进程,通过子进程继承父进程打开的描述符,来实现进程之间传递描述符。但是有时候我们需要子进程向父进程传递描述符,或者在没有血缘关系的两个进程之间传递文件描述符,比如客户端向服务端请求打开某个文件或者设备,服务端进行打开操作并把描述符传递给客户端,这样对客户端屏蔽了打开文件或者设备的细节。我们应当注意的是这里传递的描述符不是数值,而是发送描述符进程的PCB中文件描述符表里以该文件描述符为索引的元素----指向File结构体的指针对于上面描述的需求,Linux原创 2020-07-30 00:16:02 · 992 阅读 · 0 评论 -
最小堆的实现及应用-----定时器(3):时间堆
基于升序链表的定时器和时间轮都是以固定的频率来调用心搏函数,并在其中依次检测到期的定时器,然后执行到期定时器上的回调函数,这样的定时并不准确,因为可能已有定时器到期了,但是因为心搏函数此时还未调用而无法处理定时任务。设计定时器的另一种思路是:将所有定时器中超时时间最小的一个定时器的超时值作为心搏间隔。这样,一旦心博函数tick被调用,超时时间最小的定时器必然到期,我们就可以在tick函数中处理该定时器。然后,再次从剩余的定时器中找出超时值最小的一个,并将这个设置为下一心博间隔。如此反复,就可以实现较为精准原创 2020-07-20 21:40:04 · 932 阅读 · 0 评论 -
定时器(2)---时间轮
上一文所说的基于排序链表的定时器在添加定时器时效率很低,而时间轮则解决了这个问题,下图为一个简单的时间轮:指针指向一个槽(slot),其以恒定的速度顺时针转动,每转动一下就指向下一个槽,每次转动称为一个滴答(tick)。一个滴答的时间称为时间轮的槽间隔si(slot interval),它实际为心搏时间。该时间轮有N个槽,其运转一周的时间为N×siN\times siN×si。每个槽指向一条定时链表,每条链表上的定时器具有一个相同的特征:即他们的定时时间相差N×siN\times siN×si的整数倍。原创 2020-07-18 20:58:25 · 268 阅读 · 1 评论 -
定时器(1)---基于升序双向链表定时器处理非活动连接
一个高性能服务器通常需要处理非活动连接,来提升性能和释放计算机资源,我们可以为每个客户端设置一个定时器,其中包含超时时间,处理函数以及函数的参数。通过升序双向链表将所有客户端的定时器组织起来,通过alarm函数定时,当SIGALRM信号到来时,遍历链表来调用超时客户端的回调函数来关闭非活动连接。下面通过代码来展示这一方法。定时器代码:list_time.h:#ifndef _LIST_TIME_H#define _LIST_TIME_H#include<time.h>#inclu原创 2020-07-18 16:44:53 · 571 阅读 · 0 评论 -
收不到SIGURG信号?
在写利用SIGURG信号处理带外数据时,程序老是收不到该信号,找了很长时间BUG,在网上也搜不到相关内容,所以在此记录一下,防止后面的人跟我踩的是一样的坑,当然这种错误可能就我一个人会犯。我收不到的原因是,SIGURG信号捕捉函数是在accept之后注册的,而客户端发送数据是建立连接后立即开始发送数据的,并且发完数据就直接关闭连接退出了,而服务端检测到对端关闭后也会释放资源退出,这会导致两种情况:1.当带外数据到达时,SIGURG信号的捕捉函数还没注册上,服务端采用的是默认处理动作,即忽略。2.带外数原创 2020-07-02 20:21:43 · 551 阅读 · 6 评论 -
带外数据
带外数据什么是带外数据TCP的带外数据TCP带外数据的发送过程TCP带外数据的接受过程默认接受方式另一种接收方式应用程序如何接受和发送带外数据应用程序检查带外数据是否到达的方法I/O复用系统调用的异常事件SIGURG信号什么是带外数据带外数据用于迅速告诉对方本端发生的重要事情。它比普通数据有更高的优先级,它应该总是立即被发送,不论发送缓冲区中是否有排队等待发送的普通数据。带外数据的传输可以使用一条独立的传输层连接,也可以映射到传输普通数据的连接中。udp没有实现带外数据传输,TCP也没有真正的带外数据,原创 2020-07-02 19:15:53 · 963 阅读 · 0 评论 -
EINTR
当程序在执行处于阻塞状态的系统调用时接收到信号,并且我们为该信号设置了信号处理函数,在信号处理函数返回后,程序将面临继续执行或不执行慢速系统调用两种选择,默认情况下是系统调用将被中断,并且errno被设置为EINTR。我们可以选择继续执行,有以下两种方法:1.在设置信号处理函数的时候,为信号设置SA_RESTART标志以自动重启被该信号中断的系统调用,但是该方法对某些慢速系统调用无效,比如epoll_wait,poll,seletc等慢速系统调用,即使给信号设置了该选项,也会被中断。具体对那些慢速系统调用原创 2020-06-30 17:38:52 · 1558 阅读 · 0 评论 -
信号的一种处理模式----统一事件源
信号是一种异步事件:信号处理函数和程序的主循环是两条不同的执行路线。我们希望信号处理函数尽快地执行完毕,以确保该信号不会屏蔽(为了避免一些竟态条件,信号在处理期间,系统不会再次触发它)太久。(由于信号集采用位图这种数据结构,导致在我们屏蔽该信号期间 ,无论该信号到达多少次,我们只能记录一次,所以在当前信号处理函数执行完后,该信号只能触发一次,这当然不是我们希望的,所以信号处理函数执行的越快,越能避免这种问题。)为了使信号处理函数执行的速度变快,我们可以将处理函数的主逻辑放在程序的主循环中,当信号处理函数被原创 2020-06-30 14:51:53 · 242 阅读 · 0 评论 -
用poll来实现群聊功能
客户端:#include<stdio.h>#include<sys/socket.h>#include<sys/poll.h>#include<sys/types.h>#include<arpa/inet.h>#include<stdlib.h>#include<string.h>#include<errno.h>#include<unistd.h>#include<fcnt原创 2020-06-29 22:54:01 · 157 阅读 · 0 评论 -
UDP SOCKET 读数据的注意事项
UDP是数据报协议,它的数据单位与TCP不同,是以一个报文为单位,所以我们读取数据时应当注意应用缓冲区的大小,若不能接受一个报文中全部的数据,那么该报文没有被读取到的数据会丢失。...原创 2020-06-28 15:42:24 · 362 阅读 · 0 评论 -
I/O复用的高级应用三:同时处理TCP和UDP服务
对于同一个端口,如果服务器要同时处理该端口上的TCP和UDP请求,则需要建立两个不同的socket,一个是流socket,另一个是数据报socket,并将它们都绑定到该端口上。下例回射服务器就能同时处理一个端口上的TCP和UDP请求:#include<stdio.h>#include<sys/socket.h>#include<sys/epoll.h>#include<sys/types.h>#include<unistd.h>#in原创 2020-06-28 13:01:03 · 294 阅读 · 0 评论 -
使用POLLRDHUP和EPOLLRDHUP事件的坑
这两个事件其实是一个东西,分别对应poll和epoll,通常用来判断对端是否关闭,但是当你对某个socket注册POLLIN和POLLRDHUP(EPOLLIN和EPOLLRDHUP)时,在对端关闭时,对于poll来说会一直触发POLLIN + POLLRDHUP事件,epoll也会触发EPOLLIN + EPOLLRDHUP事件,是否一直触发要看epoll是工作在LT模式下还是ET模式下。所以,当我们使用POLLRDHUP(EPOLLRDHUP)事件来判断对端是否关闭时,POLLRDHUP(EPOLLR原创 2020-06-27 11:22:33 · 3132 阅读 · 0 评论 -
I/O复用的高级应用----非阻塞connect
在使用非阻塞connect时,常常会发生EINPROGRESS错误。这是在对非阻塞desocket调用connect,而连接又没有立即建立的情况下发生的错误,在这种情况下,我们可以调用select、poll等函数来监听这个连接失败的socket上的可写事件。当select、poll等函数返回后,再利用getsockopt来读取错误码并清楚该socket上的错误。如果错误码是0,表示连接成功建立,否则连接失败。非阻塞connect的实现:#include<stdio.h>#include&l原创 2020-06-26 16:45:36 · 207 阅读 · 0 评论 -
epoll在并发编程中同步问题的解决-----EPOLLONESHOT事件
无论epoll处于LT模式还是ET模式下,一个socket上的某个事件都可能会被触发多次。这在并发编程中会引起一个问题,比如一个线程在读取完某个socket上的数据后开始处理数据,而在处理数据的过程中又触发可读事件,这样会有另外一个线程读取新的数据,这样就会出现两个线程同时操作一个socket的问题。上述问题可以通过epoll中的EPOLLONESHOT事件来解决,对于注册EPOLLONESHOT事件的文件描述符来说,只会触发一次事件,除非使用epoll_ctl函数重置该文件描述符上注册的EPOLLONE原创 2020-06-25 20:33:54 · 685 阅读 · 0 评论 -
用有限状态机处理http请求
#include<stdio.h>#include<sys/socket.h>#include<sys/types.h>#include<stdlib.h>#include<string.h>#include<unistd.h>#include<arpa/inet.h>//主状态机有两种状态,当前正在分析请求行,当前正在分析头部字段enum CHECK_STATE{CHECK_STATE_REQUESTLIN原创 2020-06-24 22:35:17 · 587 阅读 · 0 评论 -
splice函数
该函数用于在两个文件描述符之间移动数据,为零拷贝操作,其函数原型如下:ssize_t splice(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags);splice()在两个文件描述符之间移动数据,而无需在内核地址空间和用户地址空间之间进行...原创 2020-04-21 20:14:46 · 4993 阅读 · 2 评论 -
sendfile函数
这个函数顾名思义就是传输文件,其函数原型为:#include <sys/sendfile.h>ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);out_fd参数:为写打开的文件描述符,在2.6.33之前的Linux内核中,out_fd必须引用套接字。从Linux 2.6.33开始,它可以是任...原创 2020-04-21 17:32:26 · 1155 阅读 · 0 评论 -
网络API----getaddrinfo和getnameinfo函数
该函数可通过知己名获取IP地址(内部使用的是gethostbyname函数),也能通过服务名获得端口号(内部使用的是getservbyname函数)。其是否可重入取决于其内部的gethostbyname和getservbyname函数是否是它们的可重入版本,该函数的定义如下: int getaddrinfo(const char *hostname, const char *serv...原创 2020-04-20 22:11:54 · 1024 阅读 · 0 评论 -
网络API之gethostbyname和getservbyname
这两个函数的头文件为#include<netdb.h>gethostbyname函数原型为:struct hostent * gethostbyname(const char *name);函数作用为:通过主机名来获取主机完整信息,参数指定目标主机的主机名。返回的结构体类型定义如下:struct hostent{ char* h_name; /* 主机名...原创 2020-04-20 15:00:38 · 381 阅读 · 1 评论 -
Linux网络编程----本地套接字
本地套接字与网络套接字的区别:1.网络套接字是通过绑定ip和端口使网络中的进程之间进行通信,而本地套接字是绑定套接字文件的路径名来使单机中的进程间通信,是一种IPC机制,其存储地址结构的数据类型为struct sockaddr_un,其头文件为#include<sys/un.h> #define UNIX_PATH_MAX 108 struct sockaddr_un {...原创 2020-03-16 17:08:48 · 473 阅读 · 2 评论 -
基于UDP协议的网络通信----C/S
UPD协议是:无连接,不可靠的数据报传输,对不稳定的网络层采取完全不弥补的策略它是直接向指定ip和端口的进程发送数据,并且不管对方是否收到。所以它不稳定,但是速度快,时效性高UDP比TCP网络通信模型的代码更简单,我们直接看代码就懂了。server.c#include<stdio.h>#include<sys/types.h>#include<sys/s...原创 2020-03-15 21:50:03 · 413 阅读 · 0 评论 -
epoll为何比select和poll快----select poll epoll之间的对比
select: int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);通过位图的数据结构来设置监听的文件描述符,对应位为1表示该文件描述符需要监听。受fd_set的限制最多只能监听1024个文件描述符...原创 2020-03-10 22:29:03 · 391 阅读 · 0 评论 -
epoll----反应堆模型
这个名听起来挺唬人,其实就是利用struct epoll_event这个数据类型里data中的void* ptr而已。由于data是一个共用体可以将我们之前使用的fd换成ptr使用,由于ptr是一个泛型指针,可以指向任意的数据类型。我们可以自己写一个结构体包含文件描述符,关心的事件类型,函数指针等,用函数指针指向一个函数,这样我们就把文件描述符和想做的操作捆绑到了一起。并且用ptr指向这个结构体,...原创 2020-03-10 20:19:46 · 156 阅读 · 0 评论 -
epoll ET与LT模式
LT模式:水平触发,epoll的默认模式,只要有数据可读或者有空间可写epoll_wait()函数就会返回。(对于一次事件,若没有处理完,epoll_wait会一直通知,直到事件处理完毕)ET模式:边缘触发,有读事件或写事件到来才会触发epoll_wait()函数返回,换言之,当有一次读事件触发epoll_wait函数返回后,不论你这次数据有没有读完,之后并不会触发epoll_wait函数,除非...原创 2020-03-10 17:35:12 · 230 阅读 · 0 评论 -
Linux网络编程多路IO复用----epoll
前面博客讲了select和poll,但是每次调用时都要将fd集合从用户空间拷贝到内核,并且为了检查哪些fd满足事件,需要遍历所有的fd,其效率是低下的。这样就有了epoll,epoll只需要注意我们所关心的那些fd。epoll的系统调用有三个函数:1.创建epoll fd函数int epoll_create(int size);创建一个epoll的文件描述符并在内核创建epoll的数据结...原创 2020-03-10 11:50:50 · 157 阅读 · 0 评论 -
linux网络编程多路IO复用----poll
poll函数其实和select函数没有太大区别,与select不同的是函数接口,poll不为每种状态设置对应的文件描述符集,而是设置一个struct pollfd类型的结构体数组,对其数组元素设置文件描述符和对其所关心的状态。优点:自带数组结构,并且将监听事件集合和返回事件集合分离,可以扩展监听上限,突破1024上限。缺点:不能跨平台函数原型为:int poll(struct pollfd ...原创 2020-02-29 23:31:48 · 162 阅读 · 0 评论 -
linux网络编程之多路IO转接----select
上一篇博客我们通过多进程和多线程可以为多个客户端提供服务,但是开多个进程或者线程消耗也增多了。其实我们只要一个进程就可以实现对多个客户端的监听,那就是多路IO复用,今天介绍其中的select函数(因为poll和epoll我还没学到哈哈)。函数原型int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, st...原创 2020-02-29 17:59:24 · 181 阅读 · 0 评论 -
多进程与多线程基础并发服务器
功能:客户端向服务端发送字符串,服务端转化为大写发送给客户端若想将服务端放在服务器上,客户端在本地访问,将服务端的监听套接字的ip绑定INADDR_ANY,而不能用127.0.0.1,客户端用服务器公网ip访问.多进程版服务端: 1 #include<stdio.h> 2 #include<string.h> 3 #include<signal.h&g...原创 2020-02-27 20:38:12 · 178 阅读 · 0 评论