网络编程
心若向阳,何谓悲伤
这个作者很懒,什么都没留下…
展开
-
C/S模型UDP实现
一、处理模型由于UDP不需要维护连接,程序逻辑简单了很多,但是UDP协议是不可靠的,保证通讯可靠性的机制需要在应用层实现。二、实现服务器端:#include <string.h>#include <netinet/in.h>#include <stdio.h>#include <unistd.h>#include <strings.h>#include <arpa/inet.h>#include <ctype原创 2021-08-19 10:49:08 · 216 阅读 · 0 评论 -
UDP服务器
传输层主要应用的协议模型有两种,一种是TCP协议,另外一种则是UDP协议。TCP协议在网络通信中占主导地位,绝大多数的网络通信借助TCP协议完成数据传输。但UDP也是网络通信中不可或缺的重要通信手段。相较于TCP而言,UDP通信的形式更像是发短信。不需要在数据传输之前建立、维护连接。只专心获取数据就好。省去了三次握手的过程,通信速度可以大大提高,但与之伴随的通信的稳定性和正确率便得不到保证。因此,我们称UDP为“无连接的不可靠报文传递”。那么与我们熟知的TCP相比,UDP有哪些优点和不足呢?由于无需创建原创 2021-08-19 10:31:29 · 178 阅读 · 0 评论 -
线程池并发服务器
什么是线程池?线程池是一个抽象概念,可以简单的认为若干线程在一起运行,线程不退出,等待有任务处理。为什么要有线程池?(1)以网络编程服务器端为例,作为服务器端支持高并发,可以有多个客户端连接,发出请求,对于多个请求我们每次都去建立线程,这样线程会创建很多,而且线程执行完销毁也会有很大的系统开销,使用上效率很低。(2)之前文章的线程例子中,我们也知道创建线程并非多多益善,所以我们的思路是提前创建好若干个线程,不退出,等待任务的产生,去接收任务处理后等待下一个任务。线程池如何实现?需要思考2个问题原创 2021-08-19 09:39:31 · 192 阅读 · 0 评论 -
epoll反应堆
epoll还有一种更高级的使用方法,那就是借鉴封装的思想,简单的说就是当某个事情发生了,自动的去处理这个事情。这样的思想对我们的编码来说就是设置回调,将文件描述符,对应的事件,和事件产生时的处理函数封装到一起,这样当某个文件描述符的事件发生了,回调函数会自动被触发,这就是所谓的反应堆思想。从我们之前对epoll的使用上如何去支持反应堆呢?需要重新再认识一下struct epoll_event中的epoll_data_t结构体:typedef union epoll_data{void原创 2021-08-19 09:32:20 · 898 阅读 · 0 评论 -
epoll 边沿触发 非阻塞 IO 服务器
在之前的文章中提到过Readn 函数:ssize_t Readn(int fd, void *vptr, size_t n) 试想这样一种情况:1、server 循环使用 epoll_wait,监听 fd,fd 发生读事件,epoll_wait 通知 server。2、server 接到通知,调用 Readn 函数,读取 500 字节。3、但是,client 就发送了 200 字节,不足 500 字节。4、此时,server 会阻塞在 Readn 函数上。5、意味着 server 无法执行下原创 2021-08-15 10:29:29 · 168 阅读 · 0 评论 -
epoll 边沿触发(ET 模式)和水平触发(LT 模式)
(1)应用 ET 模式的目的:改变 epoll_wait 的默认属性,可以减少调用 epoll_wait 函数的调用次数。(2)思想由来:模拟电路的高低电频的思想。水平触发: 持续的 1 持续的 0边沿触发: 0 ->1 ;1 -> 0(3)场景:用 epoll 实现一个服务器,调用 epoll_wait 函数进行监听,此时 client 给 epoll 发送了 100 字节的数据,而 server 使用 read 函数读走 50 字节,剩余 50 字节,问题:epoll_wait原创 2021-08-13 14:31:57 · 894 阅读 · 0 评论 -
epoll 版 高并发服务器
#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <string.h>#include <arpa/inet.h>#include <sys/epoll.h>#include <errno.h>#include <ctype.h>int main(){ // 1 创建套接字 int lfd = socket(AF原创 2021-08-13 13:55:47 · 133 阅读 · 0 评论 -
多路 IO 转接 :epoll 函数
因为 select 和 poll 的返回值特性,所以想判断到底哪个文件描述符发生了事件,需要遍历文件描述符表,因此,在“高并发、少访问”情况下,比如 1000 个连接,就 3 个发了数据,select 和 poll 效率就很低。理想状态:内核直接告诉我“哪个文件描述符”发生了“什么事件”:此功能对应于epoll 模型。(1)头文件#include <sys/epoll.h>(2)函数1)创建一个 epoll 句柄:int epoll_create(int size)参数: 监听原创 2021-08-12 22:32:00 · 127 阅读 · 0 评论 -
poll 版 高并发服务器
#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <netinet/in.h>#include <arpa/inet.h>#include <poll.h>#include <ctype.h>#include <errno.h>#define MAXLINE 80原创 2021-08-12 16:51:47 · 162 阅读 · 0 评论 -
多路 IO 转接 :poll 函数
(1)头文件#include <poll.h>(2)函数原型int poll (struct pollfd *fds, nfds_t nfds, int timeout);(3)参数参 1:struct pollfd 类型结构体数组的首地址 。是传入传出参数struct pollfd {int fd; // 要监听的文件描述符:负值无效short events; // 要监听的事件宏: POLLIN (读) / POLLOUT(写) / POLLERRshort reve原创 2021-08-12 16:09:47 · 105 阅读 · 0 评论 -
select 版 高并发服务器
#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <arpa/inet.h>#include <ctype.h>#include <string.h>#include <sys/socket.h>#include <sys/select.h>int main(){ // 1 创建套接字 int lfd原创 2021-08-12 15:21:09 · 149 阅读 · 0 评论 -
多路 IO 转接 :select 函数
(1)头文件:#include <sys/select.h>(2)函数原型:int select( int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,struct timeval *timeout );(3)参数说明1)参数1:监听的所有文件描述符中,最大文件描述符的编号 + 1(即监听 fd 的总个数)。2)参数2、3、4:监听的文件描述符 “是否可读”、“是否可写”、“是否异常” 的事件;fd_set原创 2021-08-11 21:50:44 · 171 阅读 · 0 评论 -
多线程版 高并发服务器
#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <arpa/inet.h>#include <ctype.h>#include <strings.h>#include <pthread.h>typedef struct client_info{ int cfd; struct sockaddr_in client原创 2021-08-11 12:23:37 · 162 阅读 · 0 评论 -
链路层寻址和ARP
一、网络层地址和链路层地址每个节点有网络层地址和链路层地址。网络层地址:节点在网络中分配的一个唯一地址即IP地址,用于把分组送到目的IP网络。长度为32比特。链路层地址:又叫做MAC地址或物理地址、局域网地址。此地址用于把数据帧从一个节点传送到另一个节点中。MAC地址是节点“网卡”本身所带的地址,是唯一的。它的地址长度通常为6字节即48比特。这2字节的地址用16进制表示,每个字节表示为一对16进制数。注意网卡的MAC地址是永久的,它在生产时固化在其ROM里。下图为局域网的MAC地址:局域网中原创 2021-08-10 22:41:54 · 864 阅读 · 0 评论 -
多进程版 高并发服务器
#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <arpa/inet.h>#include <ctype.h>#include <strings.h>#include <sys/wait.h>#include <signal.h>void func(int sig) { while( (waitpid(-1原创 2021-08-10 15:29:41 · 126 阅读 · 0 评论 -
TCP 中粘包的产生和解决
当发送方的“发送速度”大于接收方的“处理速度”时,就会出现问题。比如:接收方一直没有处理,发送方第一次发送一个包大小为 100 字节,第二次发送 200 字节,则缓冲区内有数据 300 字节,当接收方从缓存区中取数据时,就无法决定该从何处将 2 个数据包分开,这种问题就是粘包问题。TCP 粘包:发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾。为了避免粘包现象,可采取以下几种措施:(1)添加结尾标记:比如 \n ,则需要用到 Readline 函数;原创 2021-08-09 10:57:52 · 102 阅读 · 0 评论 -
半关闭函数:高级版的 close 函数
半关闭函数:close() :只会将某文件的“描述符引用计数”减 1;shutdown ():会关掉连接在同一个套接字上的所有文件描述符,即计数置 0。代码为:#include <sys/socket.h> int shutdown(int sockfd, int how);参数how:(1)SHUT_RD (0):关闭读功能,不允许 sockfd 进行读操作。该套接字不再接受数据,任何当前在套接字接受缓冲区的数据将被丢弃掉。(2)SHUT_WR (1):关闭写功能,不允许 .原创 2021-08-09 10:42:31 · 198 阅读 · 0 评论 -
端口复用(解决C/S模型的BUG)
在上篇文章中介绍了TCP 的状态转换,现在看之前C/S通信代码的BUG:(1)如果服务器作为主动方,先调用 close,服务器会进入 FIN_WAIT_2(半关闭状态);(2)客户端调用 close 后,服务器会处于 TIME_WAIT 状态;(3)因为服务器需要等待 2MSL 才能进入 CLOSE,在 2MSL(大约 1min)期间,服务器并没有真正关闭,原端口被占用中,如果此时立即再启动服务器,会发现无法启动解决方法:在 server 代码的 socket()和 bind()调用之间插入如下代码原创 2021-08-09 10:23:41 · 193 阅读 · 0 评论 -
TCP 的状态转换
转换图如下:其中粗实线代表主动方动作;虚线代表被动方动作;细实线代表主动方和被动方同时的动作。1、主动方(1)主动建立连接过程:一般来讲:客户端主动、服务器被动(但不绝对)。主动方原始处于 CLOSE(关闭状态);然后向被动方发送 SYN 请求连接 ,这时 主动方处于 SYN_SENT(主动打开状态),此时等待被动方的 ACK 应答 ,得到应答的同时,被动方也会向主动方发送 SYN请求连接 ,主动方接收到这 2 个消息,同时回复被动方的请求 ,这时主动方处于 ESTABLISHED(数据传输原创 2021-08-09 10:02:58 · 218 阅读 · 0 评论 -
TCP 三次握手 / 四次挥手
一、为什么要有三次/四次握手(产生的背景)?网络 IP 层更贴近硬件,所以遇到的问题越多,比如路由器当机,断电,网线问题等,都会造成影响,因此网络层极其不稳定的,会导致数据有可能无法递达。网络层的上一层是传输层,针对这种情况,产生了 2 种机制,对应 2 种协议:(1)完全不弥补 :UDP,是无连接的不可靠的报文传输;(2)完全弥补 :TCP,是面向连接的可靠数的据包传递(丢包重传)。二、TCP 如何做到完全弥补?(关键在面向连接)先建立连接(成功连接就意味网络畅通),连接过程经过三次握手(2原创 2021-08-09 08:59:34 · 90 阅读 · 0 评论 -
套接字错误处理函数的封装思想及函数实现
一、套接字错误处理函数的封装思想在上篇文章中的 CS 模型,存在 bug:先关 server,再关 client,立即再启动 server,此时会发现无法启动。原因是:先关 server,再关 client,执行 shell 命令netstat -apn | grep 端口号,会发现服务器处于“TIME_WAIT”状态,即不是真正的退出状态,也就意味着,原先 server 端口号并没有被释放掉,再次启动 server,因为端口号是固定的,而且正在被占用中,所以无法启动,问题出现在 bind 函数调用原创 2021-08-08 23:06:50 · 715 阅读 · 0 评论 -
利用套接字实现 CS 模型
例子:大写转小写。注意:代码都是运行在Linux内核中。服务器端:#include <stdio.h>#include <unistd.h>#include <sys/types.h>#include <sys/socket.h>#include <stdio.h>#include <arpa/inet.h>#include <ctype.h>int main(){ // 1、创建套接字 int l原创 2021-08-08 17:33:35 · 151 阅读 · 0 评论 -
socket 相关函数
所有函数头文件:#include <sys/types.h>#include <sys/socket.h>一、基本函数(1)套接字创建函数:int socket(int domain, int type, int protocol);参数 :domain :IP 版本: AF_INET;type :socket 内部协议:SOCK_STREAM;protocol :协议号,选 0 表示采用默认协议即流式协议;返回值:成功返回指向新创建的socket 的文件描原创 2021-08-06 08:23:44 · 145 阅读 · 0 评论 -
sockaddr_in 结构体
strcut sockaddr 是用来描述 IPv4 地址协议,原始结构体 sockaddr 已经被废弃掉了。常使用 strcut sockaddr_in 类型。struct sockaddr_in { sa_family_t sin_family; // IP版本: AF_INET AF_INET6 in_port_t sin_port; //端口号(网络字节序) struct in_addr sin_addr; // IP 地址(网络字节序)只含有一个元素的结构体};s原创 2021-08-04 22:55:25 · 337 阅读 · 0 评论 -
本地字节序和网络字节序的转换
1、背景“端口号”或者“点分十进制的IP”必须先转化为“网络字节序”,才能在网络环境中传输。TCP/IP 规定,网络数据流采用大端字节序:高地址存低位数据,计算机一般采用小端存储。如下图所示:因此,从计算机到网络,需要一个“主机字节序”到“网络字节序”的转换。2、转换函数符号说明:32:32 位,代表转IP ;16:代表转端口号 ;h:host 主机 ;to:到;n:net 网络 ;l:长整形(标识 IP) ;s:短整形(标识端口号)。(1)端口号转换:#include <原创 2021-08-04 22:17:08 · 1843 阅读 · 0 评论 -
socket 套接字的基本概念
什么是套接字:socket套接字:一套网络通信的接口 (API), 一套函数, 本质是伪文件。在网络环境中唯一的标识一个进程需要 IP 和端口,这个进程就是 socket,因此 socket 需要捆绑 IP 和端口号。socket 一定是成对儿出现的。通信的两个角色:服务器是被动提供服务,所以服务器应该先于客户端启动。在服务器启动之后,它的IP地址和端口不能变化,所以服务器的IP和端口的设置是在启动之前即需要在启动之前绑定。客户端主动连接服务器,在当前服务器启动之后需要知道服务器程序对应的主机I原创 2021-08-04 21:40:14 · 326 阅读 · 0 评论 -
网络层:IP 协议(用于封装 IP)
格式:4 位版本号:IPv4,IPv6;4 位首部长度:IP 段头长度(20 字节) ;16 位总长度:IP 段总长度;8 位生存时间(TTL)(Time to live):最大值 256,数据每跳一次,TTL 减 1,意味着数据如果跳 256 次仍没有递达,则被丢弃(防止数据死循环跳跃,造成堵塞)。32 位的源 IP 地址: 数据包起始 IP,4 个字节;32 位的目的 IP 地址:数据包终止 IP(不是下一跳的 IP)。说明:点分十进制 192.168.1.1 本质是“字符串”, 而在 .原创 2021-08-01 17:04:04 · 540 阅读 · 0 评论 -
传输层 :TCP/UDP 协议(用于封装接口)
一、UDP协议16 位源端口号、目的端口号:用于定位进程 。端口号上限是 216^{16}16 - 1 = 65535。二、TCP协议TCP 数据报:除了拥有端口号,还拥有(1)32 位为序号、确定序号:用于校验是否投递成功以及递达顺序。(2)16 位窗口大小:滑动窗口。注意:IP 地址可以在网络环境中标识唯一的主机;端口号可以在主机中标识唯一的进程。网络层定义了源和目的 IP,传输层定义了源和目的的 端口,这样就可以指定由“源主机的某进程”到“目的主机的某进程”的通信。...原创 2021-08-01 16:52:09 · 291 阅读 · 0 评论 -
数据包通过分层模型实现通信的过程
传输前,由操作系统层层封装,然后经由网卡,路由器、交换机等进入网络。接收时,仍然由操作系统层层解封,露出数据。封装和解封顺序相反。每层的详细内容在我的计算机网络专栏有写,这个模块是放在网络编程里的,不再详细叙述。...原创 2021-07-30 23:05:12 · 172 阅读 · 0 评论 -
分层模型:OSI与TCP/IP
一、OSI 七层模型:物数网传会表应(1)物理层:物理设备的标准(如网线和光纤接口类型、各种传输介质的传输速率等)。(2)数据链路层:定义了如何让格式化数据以帧为单位进行传输,以及如何让控制对物理介质的访问。这一层通常还提供错误检测和纠正,以确保数据的可靠传输。(3)网络层:提供网络传输的路径。(4)传输层:定义了一些传输数据的协议和端口号。(5)会话层、表示层、应用层:分界不是很明显。二、TCP/IP 模型TCP/IP 模型:网络接口层(链路层)、网络层、传输层、应用层。...原创 2021-07-30 22:29:01 · 147 阅读 · 0 评论 -
网络应用程序设计模式
一、C/S 模式传统的网络应用设计模式,客户机(client) / 服务器(server)模式。需要在通讯两端各自部署客户机和服务器来完成数据通信。优点:协议可以自定义(灵活),数据可以提前缓存到本机上,后续运行快。缺点:客户端安装在主机电脑上,对用户的安全有一定威胁,需要分别开发客户端和服务器,而且需要联合调试,工作量大。使用场景:数据量访问比较大,要求稳定性较高。二、B/S 模式浏览器(browser) / 服务器(server)模式。只需在一端部署服务器,而另外一端使用每台 PC 都默认原创 2021-07-30 17:14:25 · 1623 阅读 · 0 评论 -
协议的概念
从应用的角度出发,协议可理解为“规则”,是数据传输和数据解释的规则。数据的发送方和接收方要严格遵照这些规则(这些规则肯定是之前就定好的)。例如:第一次发送文件名,第二次传输文件大小,第三次发送文件内容 ,这是FTP 协议的雏形。如果双方不遵照这个规则,就会出现数据混乱。TCP 协议注重数据的传输。 HTTP 协议着重于数据的解释。典型协议:应用层 :常见的协议有 HTTP 协议,FTP 协议。传输层 :常见协议有 TCP/UDP 协议。网络层 :常见协议有 IP 协议、ICMP 协议、IGMP原创 2021-07-30 16:44:10 · 375 阅读 · 0 评论