本节内容和前节事件管理封装是息息相关的,本节内容主要包含的代码在connection{.h, .cc}中。

这里面最主要的有两个类:connection类和tcpsconn类,connetion类主要服务于单个套接字,包括套接字上的数据读取写入等,而tcpsconn类则是服务于套接字集合,如接收连接,更新失效套接字等。具体我们看头文件。

class chanmgr {
public:
virtual bool got_pdu(connection *c, char *b, int sz) = ;
virtual ~chanmgr() {}
};

我们首先看到的是这个虚基类类,这个类会以委托的形式用在connection和tcpsconn类中,它只有一个方法即got_pdu,它在RPC实现中扮演着重要角色,后面使用的时候会再次介绍它。

connection类

 class connection : public aio_callback {
public:
//内部buffer类,主要用于接收/写入数据的buffer
struct charbuf {
charbuf(): buf(NULL), sz(), solong() {}
charbuf (char *b, int s) : buf(b), sz(s), solong(){}
char *buf;
int sz;
int solong; //amount of bytes written or read so far
};
//m1: chanmgr, f1: socket or file,
connection(chanmgr *m1, int f1, int lossytest=);
~connection(); int channo() { return fd_; }
bool isdead();
void closeconn(); bool send(char *b, int sz);
void write_cb(int s);
void read_cb(int s);
//增加/减少引用计数
void incref();
void decref();
int ref(); int compare(connection *another);
private: bool readpdu();
bool writepdu(); chanmgr *mgr_;
const int fd_;
bool dead_; charbuf wpdu_; //write pdu
charbuf rpdu_; //read pdu struct timeval create_time_; int waiters_;
int refno_;
const int lossy_; pthread_mutex_t m_;
pthread_mutex_t ref_m_; //保护更新引用计数的安全性
pthread_cond_t send_complete_;
pthread_cond_t send_wait_;
};

这段代码即是connetion类的定义,它继承至aio_callback,在上一节说过,aio_callback在事件管理类中作为回调类,读取或写入数据,现在connection类就相当于一个回调类。

我们从connection的构造函数中便可以得知。

connection::connection(chanmgr *m1, int f1, int l1)
: mgr_(m1), fd_(f1), dead_(false),waiters_(), refno_(),lossy_(l1)
{ int flags = fcntl(fd_, F_GETFL, NULL);
flags |= O_NONBLOCK; //no blocking
fcntl(fd_, F_SETFL, flags);
//ignore信号
signal(SIGPIPE, SIG_IGN);
VERIFY(pthread_mutex_init(&m_,)==);
VERIFY(pthread_mutex_init(&ref_m_,)==);
VERIFY(pthread_cond_init(&send_wait_,)==);
VERIFY(pthread_cond_init(&send_complete_,)==); VERIFY(gettimeofday(&create_time_, NULL) == );
//事件管理类将本类作为回调类添加到相应的事件管理数组中
PollMgr::Instance()->add_callback(fd_, CB_RDONLY, this);
}

那这个类的具体作用是啥呢?其实它就是用于在给定套接字上通信用的,对于发送数据,会发送直到数据发送完成为止,未发送完成则会将该事件添加到事件管理中,在下一轮事件循环中继续发送,这一点我们可以从send函数中看出:

bool
connection::send(char *b, int sz)
{
ScopedLock ml(&m_);
waiters_++;
//当活着,且write pdu中还有数据时等待数据清空(发送完)
while (!dead_ && wpdu_.buf) {
VERIFY(pthread_cond_wait(&send_wait_, &m_)==);
}
waiters_--;
if (dead_) {
return false;
}
wpdu_.buf = b;
wpdu_.sz = sz;
wpdu_.solong = ; if (lossy_) {
if ((random()%) < lossy_) {
jsl_log(JSL_DBG_1, "connection::send LOSSY TEST shutdown fd_ %d\n", fd_);
shutdown(fd_,SHUT_RDWR);
}
} //发送失败时
if (!writepdu()) {
dead_ = true;
VERIFY(pthread_mutex_unlock(&m_) == );
PollMgr::Instance()->block_remove_fd(fd_);
VERIFY(pthread_mutex_lock(&m_) == );
}else{
if (wpdu_.solong == wpdu_.sz) {
}else{
//should be rare to need to explicitly add write callback
//这会继续写,因为这会添加本类(回调),然后调用里面的回调函数write_cb,
//就像是一个递归
PollMgr::Instance()->add_callback(fd_, CB_WRONLY, this);
while (!dead_ && wpdu_.solong >= && wpdu_.solong < wpdu_.sz) {
VERIFY(pthread_cond_wait(&send_complete_,&m_) == );
}
}
}
//清空写buffer
bool ret = (!dead_ && wpdu_.solong == wpdu_.sz);
wpdu_.solong = wpdu_.sz = ;
wpdu_.buf = NULL;
if (waiters_ > )
pthread_cond_broadcast(&send_wait_); //唤醒上面的等待
return ret;
}

send

对于读取数据,则当rpdu_(read buffer)未满时继续读,读取完成后就是用chanmgr类的got_pdu处理读取后的数据。

注意发送数据/接收数据都会首先发送数据大小/接收数据大小,然后再做后续发送数据/接收数据的工作。

除了connection类的发送/接收数据的功能外,我们还看到一个私有变量refno_变量,该变量的作用是用于引用计数,引用计数是一种很常见的编程技巧,例如在python中,引用计数用于对象的管理,当引用计数为0时,对象便会销毁,这里的引用计数也是也是同样的道理,这一点可以从decref函数中得知

void
connection::decref()
{
VERIFY(pthread_mutex_lock(&ref_m_)==);
refno_ --;
VERIFY(refno_>=);
//当引用计数为0时,销毁对象
if (refno_==) {
VERIFY(pthread_mutex_lock(&m_)==);
if (dead_) {
VERIFY(pthread_mutex_unlock(&ref_m_)==);
VERIFY(pthread_mutex_unlock(&m_)==);
delete this;
return;
}
VERIFY(pthread_mutex_unlock(&m_)==);
}
pthread_mutex_unlock(&ref_m_);
}

tcpscon类:

这个类则是用于管理connection的,我们先看它的定义

/**
* 管理客户连接,将连接放入一个map中map<int, connection*>
*
*/
class tcpsconn {
public:
tcpsconn(chanmgr *m1, int port, int lossytest=);
~tcpsconn(); void accept_conn();
private: pthread_mutex_t m_;
pthread_t th_;
int pipe_[]; int tcp_; //file desciptor for accepting connection
chanmgr *mgr_;
int lossy_;
std::map<int, connection *> conns_; void process_accept();
};

可看到里面定义了一个map,该map的key其实是connection类指针对应的套接字,我们看构造函数实现

tcpsconn::tcpsconn(chanmgr *m1, int port, int lossytest)
: mgr_(m1), lossy_(lossytest)
{ VERIFY(pthread_mutex_init(&m_,NULL) == ); struct sockaddr_in sin;
memset(&sin, , sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(port); tcp_ = socket(AF_INET, SOCK_STREAM, );
if(tcp_ < ){
perror("tcpsconn::tcpsconn accept_loop socket:");
VERIFY();
} int yes = ;
//设置TCP参数, reuseaddr, nodelay
setsockopt(tcp_, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
setsockopt(tcp_, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)); if(bind(tcp_, (sockaddr *)&sin, sizeof(sin)) < ){
perror("accept_loop tcp bind:");
VERIFY();
} if(listen(tcp_, ) < ) {
perror("tcpsconn::tcpsconn listen:");
VERIFY();
} jsl_log(JSL_DBG_2, "tcpsconn::tcpsconn listen on %d %d\n", port,
sin.sin_port); if (pipe(pipe_) < ) {
perror("accept_loop pipe:");
VERIFY();
} int flags = fcntl(pipe_[], F_GETFL, NULL);
flags |= O_NONBLOCK;
fcntl(pipe_[], F_SETFL, flags); //无阻塞管道 VERIFY((th_ = method_thread(this, false, &tcpsconn::accept_conn)) != );
}

该构造函数主要是初始化服务器端连接,然后创建一个线程来等待客户端的连接,后面处理客户端连接时,会将连接的客户端套接字添加到conns_的map中,即创建套接字到connection指针的对应关系,然后遍历conns_,清除死亡的connection,从而达到及时处理死亡连接的效果。

MIT 2012 分布式课程基础源码解析-底层通讯实现的更多相关文章

  1. MIT 2012分布式课程基础源码解析一-源码概述

    课程主页 课程介绍:本课程会在给出的源码的基础上要求完成8个lab Lab overviewLab 1 - Lock ServerLab 2 - Basic File ServerLab 3 - MK ...

  2. MIT 2012分布式课程基础源码解析-线程池实现

    主要内容 ScopedLock 队列实现 线程池实现 在正式讲解线程池实现之前,先讲解两个有用的工具类: ScopedLock fifo队列 ScopedLock: ScopedLock是局域锁的实现 ...

  3. MIT 2012分布式课程基础源码解析-事件管理封装

    这部分的内容主要包括Epoll/select的封装,在封装好相应函数后,再使用一个类来管理相应事件,实现的文件为pollmgr.{h, cc}. 事件函数封装 可看到pollmgr.h文件下定一个了一 ...

  4. FastDFS分布式文件系统及源码解析

    记录一次本人学习FastDFS-分布式文件系统的学习过程,希望能帮助到有需要的人. 首选得对此技术有个大概的了解,可以参考 https://www.cnblogs.com/centos2017/p/7 ...

  5. [源码解析] 模型并行分布式训练Megatron (5) --Pipedream Flush

    [源码解析] 模型并行分布式训练Megatron (5) --Pipedream Flush 目录 [源码解析] 模型并行分布式训练Megatron (5) --Pipedream Flush 0x0 ...

  6. [源码解析] PyTorch 分布式 Autograd (4) ---- 如何切入引擎

    [源码解析] PyTorch 分布式 Autograd (4) ---- 如何切入引擎 目录 [源码解析] PyTorch 分布式 Autograd (4) ---- 如何切入引擎 0x00 摘要 0 ...

  7. [源码解析] PyTorch 分布式 Autograd (5) ---- 引擎(上)

    [源码解析] PyTorch 分布式 Autograd (5) ---- 引擎(上) 目录 [源码解析] PyTorch 分布式 Autograd (5) ---- 引擎(上) 0x00 摘要 0x0 ...

  8. [源码解析] PyTorch 分布式 Autograd (6) ---- 引擎(下)

    [源码解析] PyTtorch 分布式 Autograd (6) ---- 引擎(下) 目录 [源码解析] PyTtorch 分布式 Autograd (6) ---- 引擎(下) 0x00 摘要 0 ...

  9. [源码解析] PyTorch分布式优化器(1)----基石篇

    [源码解析] PyTorch分布式优化器(1)----基石篇 目录 [源码解析] PyTorch分布式优化器(1)----基石篇 0x00 摘要 0x01 从问题出发 1.1 示例 1.2 问题点 0 ...

随机推荐

  1. Android基本控件之GridView

    我们在使用手机的过程中,会看到一些图片配上文字的一些情况,今天我们就来介绍一下安卓控件的GridView GridView组件用来以网格方式排列视图,与矩阵类似,当屏幕上有很多元素(文字.图片或其他元 ...

  2. Java字符流和字节流对文件操作

    记得当初自己刚开始学习Java的时候,对Java的IO流这一块特别不明白,所以写了这篇随笔希望能对刚开始学习Java的人有所帮助,也方便以后自己查询.Java的IO流分为字符流(Reader,Writ ...

  3. JavaScript版几种常见排序算法

    今天发现一篇文章讲“JavaScript版几种常见排序算法”,看着不错,推荐一下原文:http://www.w3cfuns.com/blog-5456021-5404137.html 算法描述: * ...

  4. poj 1469 二分图最大匹配

    就是最简单的最大匹配,没的说 #include<iostream> #include<cstdio> #include<cstring> #include<a ...

  5. Lombok(1.14.8) - @Cleanup

    @Cleanup @Cleanup,关闭流.如果最后清理资源的方法不是 close(),可以指定,例如 @Cleanup("clean"). package com.huey.lo ...

  6. Unity3D 之UGUI 滚动条

    先上效果图. 这里来说明下UGUI 滚动条,不涉及到代码. 主要用到的控件Scroll Rect ,Mask,Scrollbar. 第一步,建立一个Image,然后绑定一个滑动块的组件,添加一个mas ...

  7. Ubuntu系统中Sogou输入法面板问题解决方案

    好消息- Ubuntu Kylin团队与搜狗公司合作开发了“搜狗输入法 for Linux”版本,支持Ubuntu 12.04 和 Ubuntu 14.04操作系统,在Sougou官网就可以下载到,附 ...

  8. Android之进度条2

    我之前有写过一篇“Android之进度条1”,那个是条形的进度条(显示数字进度),这次实现圆形进度条. 点击查看Android之进度条1:http://www.cnblogs.com/caidupin ...

  9. Python基础-简单输出

    很好的一个博客地址:http://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014316 ...

  10. SQL_insert into(把B表某些字段,插入A表某些字段)

    insert into table_A([column],[column],[column]) select column,column,columnfrom table_Bwhere ...orde ...