基于epoll的TP传输层实现
1. 抽象TP传输层设计
在使用epoll实现实际的传输层之前,先设计一个抽象的传输层,这个抽象的传输层是传输层实现的接口层。
接口层中一共有以下几个通用的类或者接口:
(1)Socket:通用的套接字层,用于封装本地套接字,同时会在析构时自动关闭套接字,避免资源泄漏
(2)DataSink:通用的数据接收层,当传输层接收到数据时,会通过用户定义的DataSink对象传输到外部
(3)IStream:通用的数据流程,代表可读/写的字节流类的接口
(4)IConnectable:一个接口,表示可以链接到其它服务器
(5)BasicServer:基本的服务器类,继承了Socket类
(6)BasicStream:基本的数据流泪,继承IStream和Socket类
1.1 抽象层类图
1.2 Socket类实现
#ifndef SOCKET
#define SOCKET int32_t
#endif typedef SOCKET NativeSocket; class Socket
{
public:
Socket() : _nativeSocket()
{ } Socket(NativeSocket nativeSocket)
:_nativeSocket(nativeSocket)
{ } virtual ~Socket()
{
close(_nativeSocket);
} NativeSocket GetNativeSocket() const {return _nativeSocket;}
void SetNativeSocket(NativeSocket nativeSocket) {_nativeSocket = nativeSocket;} private:
NativeSocket _nativeSocket;
};
1.3 DataSink类实现
class DataSink
{
public:
virtual int32_t OnDataIndication(IStream *stream, const char *buf, int64_t bytes) = ;
};
1.4 IStream类实现
class IStream
{
public:
typedef std::function<void(const char* buf, int64_t size)> DataIndicationHandler;
virtual int32_t Receive(char *buffer, int32_t buffSize, int32_t& readSize) = ;
virtual int32_t Send(const ByteArray& byteArray) = ; virtual void OnDataIndication(DataIndicationHandler handler) = ;
virtual DataIndicationHandler GetDataIndication() = ;
};
1.5 BasicStream类实现
class BasicStream : public Socket, public IStream
{
public:
BasicStream(NativeSocket nativeSocket) : Socket(nativeSocket) {} BasicStream(const BasicStream& stream) = delete; virtual void SetDataSink(DataSink* dataSink) {
_dataSink = dataSink;
} virtual DataSink* GetDataSink() {
return _dataSink;
} virtual const DataSink* GetDataSink() const {
return _dataSink;
} private:
DataSink* _dataSink;
};
1.6 IConnectable类实现
class IConnectable
{
public:
virtual void Connect(const std::string& host, int32_t port) = ;
};
1.7 BasicServer类实现
template <class ConnectionType>
class BasicServer : public Socket
{
public:
typedef std::function<void(IStream* stream)> ConnectIndicationHandler;
typedef std::function<void(IStream* stream)> DisconnectIndicationHandler; BasicServer() { } virtual int32_t Listen(const std::string& host, int32_t port, int backlog) = ;
virtual void OnConnectIndication(ConnectIndicationHandler handler) = ;
virtual void OnDisconnectIndication(DisconnectIndicationHandler handler) = ; virtual ConnectionType Accept(int32_t listenfd) = ;
};
2. 基于epoll实现服务器和客户端
在前面的内容中已经完成了抽象TP传输层和基础工具(消息队列、线程池、缓冲区抽象、事件循环和日志工具)的实现,接下来在抽象TP传输层和基础工具的基础上完成基于epoll机制服务器和客户端的实现。
2.1 类图设计
2.2 EpollSream代码实现
#pragma once #include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h> #include "../include/Net.h" namespace meshy
{
class EpollStream : public BasicStream
{
public:
EpollStream(NativeSocket nativeSocket) : BasicStream(nativeSocket){} int32_t Send(const ByteArray& byteArray) override
{
const char *buf = byteArray.data();
int32_t size = byteArray.size();
int32_t n = size;
while(n > )
{
int32_t writeSize = write(GetNativeSocket(), buf + size - n, n);
if(writeSize < n)
{
if (writeSize == - && errno != EAGAIN) {
TRACE_ERROR("FATAL write data to peer failed!");
}
break;
} n -= writeSize;
} return size; } int32_t Receive(char *buffer, int32_t buffSize, int32_t& readSize) override
{
readSize = ;
int32_t nread = ;
//NativeSocketEvent ev; while ((nread = read(GetNativeSocket(), buffer + readSize, buffSize - )) > ) {
readSize += nread;
} return nread;
} void OnDataIndication(DataIndicationHandler handler) override
{ } DataIndicationHandler GetDataIndication() override
{
return _handler;
} private:
DataIndicationHandler _handler;
}; typedef shared_ptr<EpollStream> EpollStreamPtr;
}
EpollStream.h
2.3 EpollServer代码实现
#pragma once #include <string> #include "../include/Net.h"
#include "EpollStream.h" namespace meshy
{
class EpollServer : public BasicServer<EpollStreamPtr>
{
public:
EpollServer(){}
virtual int32_t Listen(const std::string& host, int32_t port, int backlog = ) override;
virtual EpollStreamPtr Accept(int32_t eventfd); virtual void OnConnectIndication(ConnectIndicationHandler handler) override
{
_connectHandler = handler;
}
virtual void OnDisconnectIndication(DisconnectIndicationHandler handler) override
{
_disconnectIndication = handler;
} private:
int32_t _SetNonBlocking(int32_t fd);
int32_t _bind(const std::string& host, int32_t port); ConnectIndicationHandler _connectHandler;
DisconnectIndicationHandler _disconnectIndication;
};
}
EpollServer.h
#include "EpollServer.h"
#include "EpollLoop.h" #include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h> namespace meshy
{
int32_t EpollServer::_bind(const std::string& host, int32_t port)
{
int32_t listenfd;
if ((listenfd = socket(AF_INET, SOCK_STREAM, )) < ) {
TRACE_ERROR("Create socket failed!");
return -;
} int32_t option = ;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option));
_SetNonBlocking(listenfd);
SetNativeSocket(listenfd); sockaddr_in addr;
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr(host.c_str()); int32_t errorCode = bind(listenfd, (struct sockaddr *) &addr, sizeof(addr));
if (errorCode < ) {
TRACE_ERROR("Bind socket failed!");
return errorCode;
} return ;
} int32_t EpollServer::Listen(const std::string& host, int32_t port, int backlog)
{
_bind(host, port); int32_t listenfd = GetNativeSocket(); int32_t errorCode = listen(listenfd, backlog);
if (- == errorCode) {
TRACE_ERROR("Listen socket failed!");
return errorCode;
}
TRACE_DEBUG("Listen Success"); errorCode = EpollLoop::Get()->AddEpollEvents(EPOLLIN | EPOLLET, listenfd);
if(errorCode < )
{
TRACE_ERROR("epoll_ctl faild : listen socket");
return errorCode;
} EpollLoop::Get()->AddServer(listenfd, this);
return ;
} EpollStreamPtr EpollServer::Accept(int32_t eventfd)
{
sockaddr_in addr;
int32_t addrlen;
memset(&addr, , sizeof(sockaddr_in));
NativeSocket connSocket;
NativeSocket listenfd = GetNativeSocket(); while((connSocket = accept(listenfd, (sockaddr*)&addr, (socklen_t*)&addrlen)) > )
{
// 得到客户端IP
getpeername(connSocket, (struct sockaddr *)&addr, (socklen_t*)&addrlen); std::ostringstream os;
os.str("");
os << "client:" << inet_ntoa(addr.sin_addr) << "connect success";
std::string strInfo = os.str();
TRACE_DEBUG(strInfo); _SetNonBlocking(connSocket);
int32_t errorCode = EpollLoop::Get()->AddEpollEvents(EPOLLIN | EPOLLET, connSocket);
if(errorCode < )
{
TRACE_ERROR("epoll_ctl faild : conn socket");
} EpollStreamPtr connection = std::make_shared<EpollStream>(connSocket); //if ( _connectHandler ) {
// _connectHandler(connection.get());
//} return connection;
} if (connSocket == -)
{
TRACE_ERROR("Accept error");
} return EpollStreamPtr(nullptr);
} // 设置阻塞模式
int32_t EpollServer::_SetNonBlocking(int32_t fd)
{
int32_t opts = fcntl(fd, F_GETFL);
if(opts < ) {
TRACE_ERROR("fcntl(F_GETFL)");
return -;
}
opts = (opts | O_NONBLOCK);
if(fcntl(fd, F_SETFL, opts) < )
{
TRACE_ERROR("fcntl(F_SETFL)");
return -;
} return ;
}
}
EpollServer.cpp
2.4 EpollClient代码实现
// 基于Epoll实现的客户端
#include "../incldue/Net.h" #include <memory> class EpollClient : public IConnectable, public BasicStream
{
typdef std::shared_ptr<EpollClient*> EpollClientPtr; public:
EpollClient(const EPollClient&) = delete;
EpollClient& operator=(const EpollClient&) = delete; virtual int32_t Receive(char *buffer, int32_t buffSize, int32_t& readSize) override;
virtual int32_t Send(const ByteArray& byteArray) override; virtual void OnConnectIndication(ConnectIndicationHandler handler) override
{
_connectHandler = handler;
}
virtual void OnDisconnectIndication(DisconnectIndicationHandler handler) override
{
_disconnectIndication = handler;
} virtual void Connect(const std::string& host, int32_t port) override;
static EpollClientPtr Connect(const std::string& host, int32_t port); protected:
EpollClient(NativeSocket nativeSocket)
:BasicStream(nativeSocket)
{ } int32_t _SetNonBlocking(int32_t fd); private:
ConnectIndicationHandler _connectHandler;
DisconnectIndicationHandler _disconnectIndication;
}
EpollClient.h
#include "EpollClient.h"
#include "EpollLoop.h" #include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h> int32_t EpollClient::Receive(char *buffer, int32_t buffSize, int32_t& readSize)
{
readSize = ;
int32_t nread = ;
//NativeSocketEvent ev; while ((nread = read(GetNativeSocket(), buffer + readSize, buffSize - )) > ) {
readSize += nread;
} return nread;
} int32_t EpollClient::Send(const ByteArray& byteArray)
{
const char *buf = byteArray.data();
int32_t size = byteArray.size();
int32_t n = size;
while(n > )
{
int32_t writeSize = write(GetNativeSocket(), buf + size - n, n);
if(writeSize < n)
{
if (writeSize == - && errno != EAGAIN) {
TRACE_ERROR("FATAL write data to peer failed!");
}
break;
} n -= writeSize;
} return size;
} void EpollClient::Connect(const std::string& host, int32_t port)
{
sockaddr_in addr;
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr(host.c_str());
_SetNonBlocking(GetNativeSocket()); if (connect(sock, (sockaddr *)&addr, sizeof(addr)) < )
{
TREACE_ERROR("connect faild");
return;
}
} EpollClientPtr EpollClient::Connect(const std::string& host, int32_t port)
{
NativeSocket sock = socket(AF_INET, SOCK_STREAM, );
if(sock < )
{
TRACE_ERROR("EpollClient socket faild");
retrun nullptr;
}
EpollClientPtr client = EpollStreamPtr(new EpollClient(sock));
client->Connect(host, port); EpollLoop *pEpollLoop = EpollLoop::Get();
if(pEpollLoop)
{
if(pEpollLoop->AddEpollEvents(EPOLLIN | EPOLLET, sock) < )
{
TRACE_ERROR("cleint AddEpollEvents faild");
return nullptr;
}
} pEpollLoop->AddStream(client); return client;
} // 设置阻塞模式
int32_t EpollServer::_SetNonBlocking(int32_t fd)
{
int32_t opts = fcntl(fd, F_GETFL);
if(opts < ) {
TRACE_ERROR("fcntl(F_GETFL)");
return -;
}
opts = (opts | O_NONBLOCK);
if(fcntl(fd, F_SETFL, opts) < )
{
TRACE_ERROR("fcntl(F_SETFL)");
return -;
} return ;
}
EpollClient.cpp
2.5 EpollLoop事件循环代码实现
#pragma once #include "../include/Loop.h"
#include "../include/Logger.h" #include "EpollServer.h" #include <stdint.h>
#include <map>
#include <stdint.h>
#include <sys/epoll.h> namespace meshy
{
#define FD_SIZE 1024
#define BUFF_SIZE 1024 class EpollLoop : public Loop
{
public:
static EpollLoop* Get();
virtual ~EpollLoop(); int32_t AddEpollEvents(int32_t events, int32_t fd); // 第二个参数为要监听的描述符
int32_t ModifyEpollEvents(int32_t events, int32_t fd);
int32_t DelEpollEvents(int32_t events, int32_t fd); void AddServer(NativeSocket socket, EpollServer* server);
void AddStream(EpollStreamPtr stream); protected:
EpollLoop();
void _Run() override;
void _EpollThread();
void _HandleEvent(int32_t eventfd, struct epoll_event* events, int32_t nfds);
void _Initialize();
int32_t _Accept(int32_t eventfd, int32_t listenfd);
void _Read(int32_t eventfd, int32_t fd, uint32_t events); private:
int32_t _eventfd;
bool _shutdown; std::map<NativeSocket, EpollServer*> _servers;
std::map <NativeSocket, EpollStreamPtr> _streams;
};
}
EpollLoop.h
#include "EpollLoop.h"
#include "../include/util.h" #include <thread> namespace meshy
{
EpollLoop* EpollLoop::Get()
{
static EpollLoop epoolLoop;
return &epoolLoop;
} EpollLoop::EpollLoop()
{
_Initialize();
_shutdown = false;
} EpollLoop::~EpollLoop()
{
_shutdown = true;
} int32_t EpollLoop::AddEpollEvents(int32_t events, int32_t fd)
{
epoll_event ev;
ev.events = events;
ev.data.fd = fd; return epoll_ctl(_eventfd, EPOLL_CTL_ADD, fd, &ev);
} int32_t EpollLoop::ModifyEpollEvents(int32_t events, int32_t fd)
{
epoll_event event;
event.events = events;
event.data.fd = fd; return epoll_ctl(_eventfd, EPOLL_CTL_MOD, fd, &event);
} int32_t EpollLoop::DelEpollEvents(int32_t events, int32_t fd)
{
epoll_event event;
event.events = events;
event.data.fd = fd; return epoll_ctl(_eventfd, EPOLL_CTL_DEL, fd, &event);
} void EpollLoop::_Run()
{
std::thread ThreadProc(&EpollLoop::_EpollThread, this);
ThreadProc.detach();
} void EpollLoop::_EpollThread()
{
TRACE_DEBUG("_EPollThread");
epoll_event events[FD_SIZE]; while (!_shutdown)
{
int32_t nfds = epoll_wait(_eventfd, events, FD_SIZE, -);
if (- == nfds)
{
TRACE_ERROR("FATAL epoll_wait failed!");
exit(EXIT_FAILURE);
} _HandleEvent(_eventfd, events, nfds);
}
} void EpollLoop::_HandleEvent(int32_t eventfd, struct epoll_event* events, int32_t nfds)
{
for (int i = ; i < nfds; ++i)
{
int32_t fd;
fd = events[i].data.fd; // 得到监听的描述符 if (_servers.find(fd) != _servers.end())
{
_Accept(eventfd, fd);
continue;
} if (events[i].events & EPOLLIN)
{
_Read(eventfd, fd, events[i].events);
} if (events[i].events & EPOLLOUT)
{
}
}
} void EpollLoop::_Initialize()
{
_eventfd = epoll_create(FD_SIZE);
if(_eventfd < )
{
TRACE_ERROR("epoll_create failed");
}
std::ostringstream os;
os.str("");
os << "epoll_create success, fdsize:" << FD_SIZE;
std::string strInfo = os.str();
TRACE_DEBUG(strInfo);
} int32_t EpollLoop::_Accept(int32_t eventfd, int32_t listenfd)
{
TRACE_DEBUG("_Accept");
EpollServer* server = _servers.find(listenfd)->second;
EpollStreamPtr connection = server->Accept(eventfd); if (connection != nullptr) {
// _streams[connection->GetNativeSocket()] = connection;
AddStream(connection);
return ;
} return -;
} void EpollLoop::_Read(int32_t eventfd, int32_t fd, uint32_t events)
{
TRACE_DEBUG("_Read"); EpollStreamPtr stream = _streams[fd]; char buffer[BUFF_SIZE] = {};
int32_t readSize;
int32_t nread = stream->Receive(buffer, BUFF_SIZE, readSize); //stream->SetEvents(events); if ((nread == - && errno != EAGAIN) || readSize == ) {
_streams.erase(fd); // Print error message
char message[];
sprintf(message, "errno: %d: %s, nread: %d, n: %d", errno, strerror(errno), nread, readSize);
TRACE_WARNING(message);
return;
} char utf8_buff[BUFF_SIZE] = {};
int32_t destreadsize = GB2312ToUTF8(buffer, utf8_buff, BUFF_SIZE);
std::ostringstream os;
os.str("");
os << "srcreadSize:" << readSize << "--destreadsize:" << BUFF_SIZE - destreadsize;
std::string strInfo = os.str();
TRACE_DEBUG(strInfo); TRACE_INFO(std::string(utf8_buff));
// Write buf to the receive queue.
//_Enqueue(stream, buffer, readSize);
} void EpollLoop::AddServer(NativeSocket socket, EpollServer* server)
{
_servers.insert(std::pair<NativeSocket, EpollServer*>(socket, server));
} void EpollLoop::AddStream(EpollStreamPtr stream)
{
_streams[stream->GetNativeSocket()] = stream;
}
}
EpollLoop.cpp
2.6 main.cpp测试
#include "stdio.h" #include "./epoll/EpollServer.h"
#include "./epoll/EpollLoop.h" #include <thread>
#include <chrono> int main()
{
meshy::EpollLoop *pEpollLoop = meshy::EpollLoop::Get();
pEpollLoop->Start(); std::string ip = "192.168.1.40";
int32_t port = ;
meshy::EpollServer server;
server.Listen(ip, port, ); while ()
{
std::this_thread::sleep_for(std::chrono::milliseconds());
}
return ;
}
2.7 makefile
#!/bin/bash all: out COMPILE=g++
FLAGS=-Wall -g -O0 -std=c++ -lpthread out: main.o Logger.o EpollServer.o EpollLoop.o
$(COMPILE) $(FLAGS) -o out main.o Logger.o EpollServer.o EpollLoop.o main.o: main.cpp
$(COMPILE) $(FLAGS) -c main.cpp Logger.o: Logger.cpp
$(COMPILE) $(FLAGS) -c Logger.cpp EpollServer.o: ./epoll/EpollServer.cpp
$(COMPILE) $(FLAGS) -c ./epoll/EpollServer.cpp EpollLoop.o: ./epoll/EpollLoop.cpp
$(COMPILE) $(FLAGS) -c ./epoll/EpollLoop.cpp clean:
rm *.o
rm out
基于epoll的TP传输层实现的更多相关文章
- [转帖]技术扫盲:新一代基于UDP的低延时网络传输层协议——QUIC详解
技术扫盲:新一代基于UDP的低延时网络传输层协议——QUIC详解 http://www.52im.net/thread-1309-1-1.html 本文来自腾讯资深研发工程师罗成的技术分享, ...
- Windows网络驱动、NDIS驱动(微端口驱动、中间层驱动、协议驱动)、TDI驱动(网络传输层过滤)、WFP(Windows Filtering Platform)
catalog . 引言 . Windows 2000网络结构和OSI模型 . NDIS驱动 . NDIS微端口驱动编程实例 . NDIS中间层驱动编程实例 . NDIS协议层驱动编程实例 . TDI ...
- ASP.NET知识总结(1.网络传输层)
1.网络传输层 1应用层(HTTP.FTP.SMTP)报文Message 2传输层(TCP.UDP)报文段Segment,为运行在不同主机上的应用程序进程间提供数据 传输服务.通过套接字(Socket ...
- 网络层、传输层、应用层、端口通信协议编程接口 - http,socket,tcp/ip 网络传输与通讯知识总结
引: http://coach.iteye.com/blog/2024511 什么是TCP和UDP,以及二者区别是什么? TCP的全称为传输控制协议.这种协议可以提供面向连接的.可靠的.点到点的通信. ...
- TCP/IP入门(3) --传输层
原文:http://blog.csdn.net/zjf280441589/article/category/1854365 传输层的主要功能 1)传输层为应用进程之间提供端到端的逻辑通信(网络层是为主 ...
- Linux内核--网络栈实现分析(九)--传输层之UDP协议(下)
本文分析基于Linux Kernel 1.2.13 原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7549340 更多请查看专栏,地 ...
- Linux内核--网络栈实现分析(五)--传输层之UDP协议(上)
本文分析基于Linux Kernel 1.2.13 原创作品,转载请标明出处http://blog.csdn.net/yming0221/article/details/7532512 更多请看专栏, ...
- (传输层)TCP协议
目录 首部格式数据单位特定注意自动重传请求ARQ具体实现发送缓存接收缓存滑动窗口确认丢失和确认迟到超时重传时间选择报文段的发送时机运输连接发送TCP请求客户端拥塞处理相关概念避免拥塞具体实现TCP 的 ...
- 传输层之TCP
---恢复内容开始--- 坞无尘水槛清,相思迢递隔重城. 秋阴不散霜飞晚,留得枯荷听雨声. --李商隐 上一篇中我们了解了socket编程是基于TCP或者UDP,所以我们有必要对TCP,和UDP ...
随机推荐
- iOS 调整图片尺寸,告诉你的UI,别问我尺寸!我要最大的
如果UI问你“这个图片你要多大尺寸的?”,你该怎么回答呢? 为了不让图片因拉伸或压缩而变形或模糊,请精确告诉UI图片的比例,至于图片的分辨率,不必要要求太严,但是最低要求是要让最大的屏幕不会出现前面描 ...
- hadoop22---wait,notify
vv wait和notify,是要加syschronized的,是要获取锁的,wait是释放控制权,别的线程就可以执行了,notify和notifyall是通知其他线程执行.
- Mixed mode assembly is built against version 'v2.0.50727' of the runtime and cannot be loaded in the 4.0 runtime without additional configuration info
Mixed mode assembly is built against version 'v2.0.50727' of the runtime and cannot be loaded in the ...
- Spark机器学习2·准备数据(pyspark)
准备环境 anaconda nano ~/.zshrc export PATH=$PATH:/anaconda/bin source ~/.zshrc echo $HOME echo $PATH ip ...
- 20145201 《Java程序设计》第三周学习总结
20145201 <Java程序设计>第三周学习总结 教材学习内容总结 本周学习了课本第四.五章内容,即认识对象和对象封装. 第四章 认识对象 4.1类与对象 定义类 要产生对象必须先定义 ...
- JAVA集合类汇总 - 转载
一.集合与数组 数组(可以存储基本数据类型)是用来存现对象的一种容器,但是数组的长度固定,不适合在对象数量未知的情况下使用. 集合(只能存储对象,对象类型可以不一样)的长度可变,可在多数情况下使用. ...
- apue.3e源码下载及编译
下载地址:http://www.apuebook.com/code3e.html 编译方法:http://blog.sina.com.cn/s/blog_94977c890102vdms.html
- 更新CentOS 6.7源为阿里源
1.备份 mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup 2.下载新的CentOS-Base ...
- docker之DockerSwarm的了解
这次一起了解下docker Swarm,什么是dockerSwarm. 什么是docker Swarm 产品背景 使用docker的流程,ssh到一台服务器,运行docker命令来运行本机的docke ...
- codeforces 831B. Keyboard Layouts 解题报告
题目链接:http://codeforces.com/contest/831/problem/B 题目意思:给出两个长度为26,由小写字母组成的字符串s1和s2,对于给出的第三个字符串s3,写出对应s ...