這里簡要分析TCPConnection的用法以及代碼
1. TcpConnection 發送

2. TcpConnection 接收
TcpConnection::handleRead呼叫
ssize_t n = inputBuffer_.readFd(channel_->fd(), &savedErrno);
一次把內核緩沖區的資料讀完

3 TcpConnection 一些小點
3.1 TcpConnection 接收設計原理
TCP協議的shutdown與close等描述
上個文章解釋了為什么muduo的TCP連接中,使用shutdown后呼叫read() == 0來判斷對端是否關閉寫,因為發送FIN之后,接收方會read回傳0,非阻塞read沒有資料回傳-1,


這里有一點需要注意,socket::shutdown()本身只會關閉連接,不會關閉socket fd,TCPConnection有一個 組合的socket物件,std::unique_ptr< Socket> socket_;而socket物件RAII的解構式中有 sockets::close(sockfd_);

3.2 TcpConnection 存盤變數
TcpConnection 中,可以使用使用boost::any與boost::any_cast實作任意型別的資料存盤與提取,muduo中例子實作對conn中的FILE*指標的存盤與操作
boost::any相關使用分析
boost::any context_;
void setContext(const boost::any& context)
{ context_ = context; }
const boost::any& getContext() const
{ return context_; }

注意問題


3.3 TcpConnection 中一個簡單的shared_ptr
這是muduo的一個簡單的tcp連接,但是通過shared_ptr來管理FILE*的生命周期,使其運用了C++的RAII,生命周期與TCPConnection一致,同時節省了很多代碼,
#include "muduo/base/Logging.h"
#include "muduo/net/EventLoop.h"
#include "muduo/net/TcpServer.h"
#include <stdio.h>
#include <unistd.h>
using namespace muduo;
using namespace muduo::net;
void onHighWaterMark(const TcpConnectionPtr& conn, size_t len)
{
LOG_INFO << "HighWaterMark " << len;
}
const int kBufSize = 64*1024;
const char* g_file = NULL;
typedef std::shared_ptr<FILE> FilePtr; //line3
void onConnection(const TcpConnectionPtr& conn)
{
LOG_INFO << "FileServer - " << conn->peerAddress().toIpPort() << " -> "
<< conn->localAddress().toIpPort() << " is "
<< (conn->connected() ? "UP" : "DOWN");
if (conn->connected())
{
LOG_INFO << "FileServer - Sending file " << g_file
<< " to " << conn->peerAddress().toIpPort();
conn->setHighWaterMarkCallback(onHighWaterMark, kBufSize+1);
FILE* fp = ::fopen(g_file, "rb"); // line1
if (fp)
{
FilePtr ctx(fp, ::fclose); //line2
conn->setContext(ctx);
char buf[kBufSize];
size_t nread = ::fread(buf, 1, sizeof buf, fp);
conn->send(buf, static_cast<int>(nread));
}
else
{
conn->shutdown();
LOG_INFO << "FileServer - no such file";
}
}
}
void onWriteComplete(const TcpConnectionPtr& conn)
{
const FilePtr& fp = boost::any_cast<const FilePtr&>(conn->getContext());
char buf[kBufSize];
size_t nread = ::fread(buf, 1, sizeof buf, get_pointer(fp));
if (nread > 0)
{
conn->send(buf, static_cast<int>(nread));
}
else
{
conn->shutdown();
LOG_INFO << "FileServer - done";
}
}
int main(int argc, char* argv[])
{
LOG_INFO << "pid = " << getpid();
if (argc > 1)
{
g_file = argv[1];
EventLoop loop;
InetAddress listenAddr(2021);
TcpServer server(&loop, listenAddr, "FileServer");
server.setConnectionCallback(onConnection);
server.setWriteCompleteCallback(onWriteComplete);
server.start();
loop.loop();
}
else
{
fprintf(stderr, "Usage: %s file_for_downloading\n", argv[0]);
}
}
如果不用shared_ptr,則如下
void onConnection(const TcpConnectionPtr& conn)
if...
else
{
if (!conn->getContext().empty())
{
FILE* fp = boost::any_cast<FILE*>(conn->getContext());
if (fp)
{
::fclose(fp);
}
}
}
4. bind導致的shared_ptr計數變化以及muduo中的使用
之前有寫過文章涉及到shared_ptr計數變化https://blog.csdn.net/weixin_44537992/article/details/109227442
但是沒有詳細思考過bind會導致shared_ptr的參考計數發生什么變化,這次分析一下
4.1 默認move建構式分析
分析bind函式時候,跟蹤到后面的tuple的move構造使用了
constexpr tuple(tuple&&) = default;
那么這個默認的轉移建構式具體是怎么實作的呢,會在指標拷貝后,將原指標置nullptr嗎?
先說結論,默認轉移建構式會呼叫物件中的每一個變數,分別呼叫其變數自己的move建構式,
class A
{
B b1,b2;
};
A::A(A&& a)
{
this.b1(move(a.b1));
this.b2(move(a.b2));
}
下面例子可以驗證上述結論
class Test
{public:
Test(int p):i(p){};
Test(Test&&){std::cout<<"Move"<<std::endl;} //
int i;
};
class forwordtest
{
public:
forwordtest(int i,int* ptr = new int(2)):p(ptr),t(i){}
//forwordtest(forwordtest&& fw) = default;
Test t;
int* p;
};
int main()
{
int tep = 3;
forwordtest forw(3,&tep);
std::cout<<"forw.p "<<forw.p<<std::endl;
std::cout<<"forw.t.i "<<forw.t.i<<std::endl;
forwordtest forw_a(std::move(forw));
std::cout<<"forw_a.p "<<forw_a.p<<std::endl;
std::cout<<"forw_a.t.i "<<forw_a.t.i<<std::endl;
std::cout<<"forw.p "<<forw.p<<std::endl;
std::cout<<"forw.t.i "<<forw.t.i<<std::endl;
}
forw.p0x61fccc
forw.t.i3
Move
forw_a.p0x61fccc
forw_a.t.i0
forw.p0x61fccc
forw.t.i3
默認轉移建構式沒有完成將原來指標置空,因為其變數自己的轉移構造沒有實作該功能,
4.2 bind與shared_ptr計數關系
class my_class :public std::enable_shared_from_this<my_class>{
public:
typedef std::function<void()> callback;
void clean_something_up(){std::cout<<"helloworld"<<std::endl;}
void on_complete() {
complete_callback = std::bind(&my_class::clean_something_up,shared_from_this());
std::cout<<"1 shared_from_this: "<<shared_from_this().use_count()<<std::endl; //3
}
private:
callback complete_callback;
};
int main(){
std::shared_ptr<my_class> obj3 = std::make_shared<my_class>();
std::weak_ptr<my_class> myob(obj3);
std::function<void()> fun = std::bind(&my_class::clean_something_up,obj3); //這里如果只使用std::bind(&my_class::clean_something_up,obj3),則use.count為1
std::cout<<obj3.use_count()<<std::endl; //2
}
2
上述例子中,obj3.use_count()為2,為什么呢?

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/275905.html
標籤:其他
下一篇:思嵐激光雷達A1使用串口測算距離角度,本文可用于學習串口,串口資料發送和PWM。激光雷達源代碼可到我的主頁花1積分下載。

