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

主要关注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. TP5图片处理常见问题

    一.Class 'think\Image' not found composer require topthink/think-image 装上了扩展控制器头部加了 use think\Image然后 ...

  2. Java Calendar 多用,日期 加减

    服务需要订购一个月,订购一个月 不等于增加 30天:若是1,3,5的话应该 31天,要善用 Calendar public static void main(String[] args) throws ...

  3. [Java] 详细解说final关键字

    final final 可以修饰变量.方法和类,表示所修饰的内容一旦赋值之后就不会再被改变.例如String类就是一个final类型的类. 1.具体使用场景 1.1 变量 1.1.1 成员变量 每个类 ...

  4. 【译】.NET 8 网络改进(二)

    原文 | Máňa,Natalia Kondratyeva 翻译 | 郑子铭 修改 HttpClient 日志记录 自定义(甚至简单地关闭)HttpClientFactory 日志记录是长期请求的功能 ...

  5. python实现百度贴吧页面爬取

    import requests class TiebaSpider: """百度贴吧爬虫类""" def __init__(self, ti ...

  6. Pandas 美国竞选捐赠案例

    import pandas as pd """ 需求 1.加载数据 2.查看数据的基本信息 3.指定数据截取,将如下字段的数据进行提取,其他数据舍弃 cand_nm: 候 ...

  7. Java并发编程实例--1.创建和运行一个线程

    从这一篇开始写Java并发编程实例,内容都翻译整理自书籍:<Java 7 Concurrency Cookbook> 谈到线程,无法逃避的一个问题就是: 并发(concurrency)和并 ...

  8. Telegraph多线程下载器v0.5--tkinter

    介绍 最近在拿python写一点小工具,结合之前的多线程.线程池技术做了个GUI版的Telegraph图册批量下载工具. 因为开发平台是在Mac,虽然对Windows平台的也进行了打包,但最垃圾的Wi ...

  9. [BAT面试题系列]乐观锁和悲观锁

    基本概念 乐观锁和悲观锁是两种思想,用于解决并发场景下的数据竞争问题(使用非常广泛,不局限于某种编程语言或数据库). 乐观锁:乐观锁在操作数据时非常乐观,认为别人不会同时修改数据.因此乐观锁不会上锁, ...

  10. 【LeetCode回溯算法#12】二叉树的直径,树形dp的前置内容(使用dfs)

    二叉树的直径 给你一棵二叉树的根节点,返回该树的 直径 . 二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 .这条路径可能经过也可能不经过根节点 root . 两节点之间路径的 长度 由它们 ...