网络编程主要关注的一些问题

主要关注3个方面的问题

  1. 连接的建立
  2. 连接的断开
  3. 消息的发送和到达

连接的建立

主要分为两种情况:服务器处理接受客户端的连接;服务端作为客户端的连接第三方服务;

//这是服务端接受客户端连接的时候;(三次握手完毕)
int clientfd=accept(listenfd,addr,sz);
//服务端作为客户端连接第三方服务
//这里又分为阻塞IO和非阻塞IO
int connectfd=soket(AF_INET,SOCK_STREAM,0);
int ret=connect(connectfd,(struct sockaddr*)&addr,sizeof(addr))
//阻塞情况:
//直接return 0;
//非阻塞情况:
//ret==-1 && errno=EINPROGRESS正在建立连接
//ret==-1 && errno=EISCONN 连接建立成功

连接的断开

分为两种:一种主动断开和被动断开

//主动关闭
close(fd);
//主动关闭本地读写端
shutdown(fd,SHUT_RDWR);
//主动关闭本地读端,对端写端关闭
shutdown(fd,SHUT_RD);
//主动关闭本地写端,对端读端关闭
shutdown(fd,SHUT_WR); // 被动:读端关闭
int n =read(fd,buf,sz);
if(n==0){
close_read(fd);
} //被动:写端关闭
int n = write(fd,buf,sz);
if(n==-1&&errno==EPIPE){
close_write(fd);
}

消息的到达

从缓冲区中读取数据:

int n= read(fd,buf,sz);
if(n<0){
if(errno==EINTR || errno == EWOULDBLOCK)
{
break;
}
close(fd);
}else if(n ==0 ){
close(fd);
}else{
//处理buf
}

消息的发送完毕

往写缓冲区中写数据:

int n =write(fd,buf,dz);
if (n == -1) {
if (errno == EINTR || errno == EWOULDBLOCK) {
return;
}
close(fd);
}

网络IO的职责

检测IO

io 函数本身可以检测 io 的状态;但是只能检测一个 fd 对应的状态;io 多路复用可以同时检测多个io的状态;区别是:io函数可以检测具体状态;io 多路复用只能检测出可读、可写、错误、断开等笼统的事件

操作IO

只能使用 io 函数来进行操作;分为两种操作方式:阻塞 io 和非阻塞 io;

阻塞 IO 和 非阻塞 IO

阻塞在网络线程;

连接的 fd 阻塞属性决定了 io 函数是否阻塞;

具体差异在:io 函数在数据未到达时是否立刻返回;

// 默认情况下,fd 是阻塞的,设置非阻塞的方法如下;
int flag = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, flag | O_NONBLOCK);

IO多路复用

io 多路复用只负责检测io,不负责操作io;

int n = epoll_wait(epfd, evs, sz, timeout);

timeout = -1 一直阻塞直到网络事件到达;

imeout = 0 不管是否有事件就绪立刻返回;

timeout = 1000 最多等待 1 s,如果1 s内没有事件触发则返回

EPoll

结构以及接口

struct eventpoll {
// ...
struct rb_root rbr; // 管理 epoll 监听的事件
struct list_head rdllist; // 保存着 epoll_wait 返回满⾜条件的事件
// ...
};
struct epitem {
// ...
struct rb_node rbn; // 红⿊树节点
struct list_head rdllist; // 双向链表节点
struct epoll_filefd ffd; // 事件句柄信息
struct eventpoll *ep; // 指向所属的eventpoll对象
struct epoll_event event; // 注册的事件类型
// ...
};
struct epoll_event {
__uint32_t events; // epollin epollout epollel(边缘触发)
epoll_data_t data; // 保存 关联数据
};
typedef union epoll_data {
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
}epoll_data_t;
int epoll_create(int size);
/**
op:
EPOLL_CTL_ADD
EPOLL_CTL_MOD
EPOLL_CTL_DEL
event.events:
EPOLLIN 注册读事件
EPOLLOUT 注册写事件
EPOLLET 注册边缘触发模式,默认是水平触发
*/
int epoll_ctl(int epfd, int op, int fd, struct epoll_event* event);
/**
events[i].events:
EPOLLIN 触发读事件
EPOLLOUT 触发写事件
EPOLLERR 连接发生错误
EPOLLRDHUP 连接读端关闭
EPOLLHUP 连接双端关闭
*/
int epoll_wait(int epfd, struct epoll_event* events, int maxevents, int
timeout);

调用 epoll_create 会创建一个 epoll 对象;调用 epoll_ctl 添加到 epoll 中的事件都会与网卡驱动程序建立回调关系,相应事件触发时会调用回调函数(ep_poll_callback ),将触发的事件拷贝到 rdlist 双向链表中;调用 epoll_wait 将会把 rdlist 中就绪事件拷贝到用户态中;

epoll 编程

连接建立

// 一、处理客户端的连接
// 1. 注册监听 listenfd 的读事件
struct epoll_event ev;
ev.events |= EPOLLIN;
epoll_ctl(efd, EPOLL_CTL_ADD, listenfd, &ev);
// 2. 当触发 listenfd 的读事件,调用 accept 接收新的连接
int clientfd = accept(listenfd, addr, sz);
struct epoll_event ev;
ev.events |= EPOLLIN;
epoll_ctl(efd, EPOLL_CTL_ADD, clientfd, &ev);
// 二、处理连接第三方服务
// 1. 创建 socket 建立连接
int connectfd = socket(AF_INET, SOCK_STREAM, 0);
connect(connectfd, (struct sockaddr *)&addr, sizeof(addr));
// 2. 注册监听 connectfd 的写事件
struct epoll_event ev;
ev.events |= EPOLLOUT;
epoll_ctl(efd, EPOLL_CTL_ADD, connectfd, &ev);
// 3. 当 connectfd 写事件被触发,连接建立成功
if (status == e_connecting && e->events & EPOLLOUT) {
status == e_connected;
// 这里需要把写事件关闭
epoll_ctl(epfd, EPOLL_CTL_DEL, connectfd, NULL);
}

连接断开

if (e->events & EPOLLRDHUP) {
// 读端关闭
close_read(fd);
close(fd);
}
if (e->events & EPOLLHUP) {
// 读写端都关闭
close(fd);
}

数据到达

// reactor 要用非阻塞io
// select
if (e->events & EPOLLIN) {
while (1) {
int n = read(fd, buf, sz);
if (n < 0) {
if (errno == EINTR)
continue;
if (errno == EWOULDBLOCK)
break;
close(fd);
} else if (n == 0) {
close_read(fd);
// close(fd);
}
// 业务逻辑了
}
}

reactor应用

下面就要开始介绍一下redis,memcached,nginx网络组件了。

这是我们自己的理解。

The reactor design pattern is an event handling pattern (事件处理模式)for handling service requests delivered concurrently to a service handler by one or more inputs(处理一个或多个并发传递到服务端的服务请求). The service handler then demultiplexes the incoming requests and dispatches them synchronously (同步)to the associated request handlers.

Redis :

Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。

Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。

Redis支持数据的备份,即master-slave模式的数据备份。

redis主要使用的是单reactor的模式

  • 单线程业务逻辑
  • 命令处理是单线程的

具体的模型如下:

memcached:

Memcached,简单来说就是一个免费开源并且高性能的分布式内存对象缓存系统,主要用于加速动态 Web 程序,减轻数据库负载。

他也是key和value的内存数据库

命令处理是多线程的。

memcached为什么使用多reactor?

  • KV数据操作简单
  • 更高程度并发处理业务

    具体的模型如下:

nginx:

nginx采用多进程模型,含一个master进程和多个worker进程,worker进程数目可配置,一般与机器CPU核心数目一致,master进程主要职责是:接收外界信号,如star,stop,restart,监控worker进程状态。worker进程主要职责:负责处理客户端请求。

使用反向代理,多进程处理业务的模式

nginx为什么要使用多进程?

  • 业务类型复杂
  • 通过进程隔离避免加锁

    但是多进程模式,很容易就会在接受到请求的时候,由于多进程的响应,导致惊群问题。

    因此通过锁在用户态解决惊群问题,目的是为了在用户层处理连接的负载均衡。

    进程到达7/8 *connections 的时候,不在处理连接,让其他进程处理连接。

    当所有进程到达7/8 * connections的时候,连接处理将变得缓慢。

推荐一个零声学院免费教程,个人觉得老师讲得不错,

分享给大家:[Linux,Nginx,ZeroMQ,MySQL,Redis,

fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,

TCP/IP,协程,DPDK等技术内容,点击立即学习:

服务器

音视频

dpdk

Linux内核

一些网络编程方面的总结,以及redis、memcache、nginx组件的一些介绍的更多相关文章

  1. 20145215实验五 Java网络编程及安全

    20145215实验五 Java网络编程及安全 实验内容 掌握Socket程序的编写: 掌握密码技术的使用: 设计安全传输系统. 实验步骤 本次实验我的结对编程对象是20145208蔡野,我负责编写客 ...

  2. [C# 网络编程系列]专题六:UDP编程

    转自:http://www.cnblogs.com/zhili/archive/2012/09/01/2659167.html 引用: 前一个专题简单介绍了TCP编程的一些知识,UDP与TCP地位相当 ...

  3. Android利用网络编程HttpClient批量上传(两)AsyncTask+HttpClient监测进展情况,并上传

    请尊重别人的劳动.转载请注明出处: Android网络编程之使用HttpClient批量上传文件(二)AsyncTask+HttpClient并实现上传进度监听 执行效果图: 我曾在<Andro ...

  4. UNIX网络编程——原始套接字的魔力【续】

    如何从链路层直接发送数据帧 上一篇里面提到的是从链路层"收发"数据,该篇是从链路层发送数据帧. 上一节我们主要研究了如何从链路层直接接收数据帧,可以通过bind函数来将原始套接字绑 ...

  5. #8 Python网络编程(一)

    前言 语言是用来交流的,人类语言使人与人交流,编程语言使人与机器交流,那么问题来了,机器如何与机器交流.你是否疑惑过:为什么我们可以使用浏览器查资料.为什么我们可以使用聊天软件聊天.为什么我们可以通过 ...

  6. 【TCP/IP网络编程】:09套接字的多种可选项

    本篇文章主要介绍了套接字的几个常用配置选项,包括SO_SNDBUF & SO_RCVBUF.SO_REUSEADDR及TCP_NODELAY等. 套接字可选项和I/O缓冲大小 前文关于套接字的 ...

  7. GO学习-(19) Go语言基础之网络编程

    Go语言基础之网络编程 现在我们几乎每天都在使用互联网,我们前面已经学习了如何编写Go语言程序,但是如何才能让我们的程序通过网络互相通信呢?本章我们就一起来学习下Go语言中的网络编程. 关于网络编程其 ...

  8. Go语言系列之网络编程

    现在我们几乎每天都在使用互联网,我们前面已经学习了如何编写Go语言程序,但是如何才能让我们的程序通过网络互相通信呢?本章我们就一起来学习下Go语言中的网络编程. 关于网络编程其实是一个很庞大的领域,本 ...

  9. golang(9):网络编程 & redis

    网络编程 TCP/IP 协议: . TCP(传输控制协议) -- 应用程序之间通信 . UDP(用户数据包协议)-- 应用程序之间的简单通信 . IP(网际协议) -- 计算机之间的通信 . DHCP ...

  10. kestrel网络编程--开发redis服务器

    1 文章目的 本文讲解基于kestrel开发实现了部分redis命令的redis伪服务器的过程,让读者了解kestrel网络编程的完整步骤,其中redis通讯协议需要读者自行查阅,文章里不做具体解析. ...

随机推荐

  1. 一个 WPF + MudBlazor 的项目模板(附:多项目模板制作方法)

    最近做了几个 WPF + MudBlazor 的小东西,每次从头搭建环境比较繁琐,然鹅搭建过程还没啥技术含量,索性就直接做了个模板,方便以后使用. 1. 介绍 一个用来创建 .NET 8 + WPF ...

  2. 【链表】【python】力扣24. 两两交换链表中的节点【超详细的注释和解释】

    目录 说在前面的话 前言 一.题目(力扣24. 两两交换链表中的节点) 二.题目分析 实现完整代码(Python实现) 总结 说在前面的话 博主也好长一段时间没有更新力扣的刷题系列了,今天给大家带来一 ...

  3. Delphi 官方 MD5

    去官方文档搜就行了,引入System.Hash 单元: http://docwiki.embarcadero.com/Libraries/Athens/en/System.Hash.THashMD5 ...

  4. Spring Boot 参数校验注解(自整理,不停的测试更新)

    首先我们只使用java官方的 javax.validation.constraints ,足以使用了,不使用spring boot 自身的,自身的与官方的一致,可能会有扩展,但是还得引入包,麻烦,只用 ...

  5. Python numpy数组操作(分割数组)

    分割数组 函数 数组及操作 split 将一个数组分割为多个子数组 hsplit 将一个数组水平分割为多个子数组(按列) vsplit 将一个数组垂直分割为多个子数组(按行) numpy.split ...

  6. linux下进行MCU开发环境搭建

    why 为什么要搭建此开发环境? 在linux环境下开发可以利用shell命令实现对文件的批处理 伟大的程序员应该都用类unix系统! 可以实现对底层编译技术的了解,以便于更好的掌握嵌入式技术 通用性 ...

  7. Windows终端的一些配置

    前言 记录早前拿到新的笔记本(win10)后配置命令行的过程,以下是环境: 命令行 : CMD,PowerShell7 Shell :Windows Terminal 设置编码格式(当前代码页)为UT ...

  8. SP277 CTGAME - City Game 题解

    题目传送门 前置知识 单调栈 解法 令 \(f_{i,j}(1 \le i \le n,1 \le j \le m)\) 表示从 \((1,j)\) 到 \((i,j)\) 中以 \((i,j)\) ...

  9. JS LeetCode 303. 区域和检索 - 数组不可变,一维数组的前缀和

    壹 ❀ 引 本题来自LeetCode303. 区域和检索 - 数组不可变,属于一道简单题,不过题目期望的做法我也是看了题解才懂得,这里大致做个记录,题目描述如下: 给定一个整数数组 nums,求出数组 ...

  10. NC19910 [CQOI2007]矩形RECT

    题目链接 题目 题目描述 给一个a*b矩形,由a*b个单位正方形组成.你需要沿着网格线把它分成分空的两部分,每部分所有格子连通,且至少有一个格子在原矩形的边界上."连通"是指任两个 ...