Article directory
foreword
Five simple TCP examples are discard, daytime, time, echo, and chargen. These functions are described as follows:
discard: discard all received data.
daytime: After the server accepts the connection, it sends the current time in the form of a string, and then actively disconnects.
time: After the server accepts the connection, it sends the current time in binary form, and then actively disconnects. We need a client program to convert the received time into a string.
echo: Echo service, send the received data back to the client.
Chargen: After the server accepts the connection, it keeps sending test data.
It is mentioned in <<UNIX Network Programming Volume 1>>, refer to Chapter 2 2.12 Standard Internet Services
Five Simple TCPs
1. discard
It is relatively simple, and only cares about the "message/data arrival" event in the "three and a half events". The event processing function is as follows:
void DiscardServer::onMessage(const TcpConnectionPtr& conn,
Buffer* buf,
Timestamp time)
{
string msg(buf->retrieveAllAsString());
LOG_INFO << conn->name() << " discards " << msg.size()
<< " bytes received at " << time.toString();
}
run:
服务端:
root@ubuntu:/opt/muduo/examples/simple/discard# ./a.out
20220528 17:05:13.611973Z 62510 INFO pid = 62510 - main.cc:13
20220528 17:05:22.777054Z 62510 INFO TcpServer::newConnection [DiscardServer] - new connection [DiscardServer-0.0.0.0:2009#1] from 192.168.1.14:57380 - TcpServer.cc:80
20220528 17:05:22.777182Z 62510 INFO DiscardServer - 192.168.1.14:57380 -> 192.168.1.14:2009 is UP - discard.cc:25
20220528 17:05:25.590716Z 62510 INFO DiscardServer-0.0.0.0:2009#1 discards 1 bytes received at 1653757525.590698 - discard.cc:35
20220528 17:05:25.798819Z 62510 INFO DiscardServer-0.0.0.0:2009#1 discards 1 bytes received at 1653757525.798808 - discard.cc:35
20220528 17:05:26.020168Z 62510 INFO DiscardServer-0.0.0.0:2009#1 discards 1 bytes received at 1653757526.020155 - discard.cc:35
20220528 17:05:26.234700Z 62510 INFO DiscardServer-0.0.0.0:2009#1 discards 1 bytes received at 1653757526.234687 - discard.cc:35
20220528 17:05:26.425043Z 62510 INFO DiscardServer-0.0.0.0:2009#1 discards 1 bytes received at 1653757526.425030 - discard.cc:35
20220528 17:05:30.313801Z 62510 INFO DiscardServer-0.0.0.0:2009#1 discards 4 bytes received at 1653757530.313788 - discard.cc:35
客户端使用netcat的nc命令模拟:
root@ubuntu:/home/lvch# nc 192.168.1.14 2009
123
2.daytime
daytime is a short connection protocol, after sending the current time, the server actively disconnects the connection. It only needs to pay attention to the "connection established" event in the "three and a half events", and the event processing function is as follows:
void DaytimeServer::onConnection(const TcpConnectionPtr& conn)
{
LOG_INFO << "DaytimeServer - " << conn->peerAddress().toIpPort() << " -> "
<< conn->localAddress().toIpPort() << " is "
<< (conn->connected() ? "UP" : "DOWN");
if (conn->connected())
{
conn->send(Timestamp::now().toFormattedString() + "\n");
conn->shutdown();
}
}
run:
root@ubuntu:/opt/muduo/examples/simple/daytime# ./a.out
20220528 17:10:57.295966Z 62523 INFO pid = 62523 - main.cc:13
20220528 17:11:04.130141Z 62523 INFO TcpServer::newConnection [DaytimeServer] - new connection [DaytimeServer-0.0.0.0:2013#1] from 192.168.1.14:49562 - TcpServer.cc:80
20220528 17:11:04.130248Z 62523 INFO DaytimeServer - 192.168.1.14:49562 -> 192.168.1.14:2013 is UP - daytime.cc:26
`客户端:
root@ubuntu:/home/lvch# nc 192.168.1.14 2013
20220528 17:11:04.130295
3. echo
Add one more than discardconn->send(msg);
void EchoServer::onMessage(const muduo::net::TcpConnectionPtr& conn,
muduo::net::Buffer* buf,
muduo::Timestamp time)
{
muduo::string msg(buf->retrieveAllAsString());
LOG_INFO << conn->name() << " echo " << msg.size() << " bytes, "
<< "data received at " << time.toString();
conn->send(msg);
}
4. time
The time protocol is similar to daytime, except that instead of a datetime string, it returns a 32-bit integer representing the number of seconds from 1970-01-01 00:00:00Z to the present. Of course this agreement has the "2038 problem". The server only needs to pay attention to
the "connection established" event in the "three and a half events", and the event processing function:
void TimeServer::onConnection(const muduo::net::TcpConnectionPtr& conn)
{
LOG_INFO << "TimeServer - " << conn->peerAddress().toIpPort() << " -> "
<< conn->localAddress().toIpPort() << " is "
<< (conn->connected() ? "UP" : "DOWN");
if (conn->connected())
{
time_t now = ::time(NULL);
int32_t be32 = sockets::hostToNetwork32(static_cast<int32_t>(now));
conn->send(&be32, sizeof be32);
conn->shutdown();
}
}
client:
#include "muduo/base/Logging.h"
#include "muduo/net/Endian.h"
#include "muduo/net/EventLoop.h"
#include "muduo/net/InetAddress.h"
#include "muduo/net/TcpClient.h"
#include <utility>
#include <stdio.h>
#include <unistd.h>
using namespace muduo;
using namespace muduo::net;
class TimeClient : noncopyable
{
public:
TimeClient(EventLoop* loop, const InetAddress& serverAddr)
: loop_(loop),
client_(loop, serverAddr, "TimeClient")
{
client_.setConnectionCallback(
std::bind(&TimeClient::onConnection, this, _1));
client_.setMessageCallback(
std::bind(&TimeClient::onMessage, this, _1, _2, _3));
// client_.enableRetry();
}
void connect()
{
client_.connect();
}
private:
EventLoop* loop_;
TcpClient client_;
void onConnection(const TcpConnectionPtr& conn)
{
LOG_INFO << conn->localAddress().toIpPort() << " -> "
<< conn->peerAddress().toIpPort() << " is "
<< (conn->connected() ? "UP" : "DOWN");
if (!conn->connected())
{
loop_->quit();
}
}
void onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp receiveTime)
{
if (buf->readableBytes() >= sizeof(int32_t))
{
const void* data = buf->peek();
int32_t be32 = *static_cast<const int32_t*>(data);
buf->retrieve(sizeof(int32_t));
time_t time = sockets::networkToHost32(be32);
Timestamp ts(implicit_cast<uint64_t>(time) * Timestamp::kMicroSecondsPerSecond);
LOG_INFO << "Server time = " << time << ", " << ts.toFormattedString();
}
else
{
LOG_INFO << conn->name() << " no enough data " << buf->readableBytes()
<< " at " << receiveTime.toFormattedString();
}
}
};
int main(int argc, char* argv[])
{
LOG_INFO << "pid = " << getpid();
if (argc > 1)
{
EventLoop loop;
InetAddress serverAddr(argv[1], 2037);
TimeClient timeClient(&loop, serverAddr);
timeClient.connect();
loop.loop();
}
else
{
printf("Usage: %s host_ip\n", argv[0]);
}
}
5. batches
The protocol is rather special, only sending but not receiving data. Moreover, the data it sends cannot be faster than the client can receive it, so you need to pay attention to the "message/data sent" event (onWriteComplete)
`#include "examples/simple/chargen/chargen.h"
#include "muduo/base/Logging.h"
#include "muduo/net/EventLoop.h"
#include <stdio.h>
using namespace muduo;
using namespace muduo::net;
ChargenServer::ChargenServer(EventLoop* loop,
const InetAddress& listenAddr,
bool print)
: server_(loop, listenAddr, "ChargenServer"),
transferred_(0),
startTime_(Timestamp::now())
{
server_.setConnectionCallback(
std::bind(&ChargenServer::onConnection, this, _1));
server_.setMessageCallback(
std::bind(&ChargenServer::onMessage, this, _1, _2, _3));
server_.setWriteCompleteCallback(
std::bind(&ChargenServer::onWriteComplete, this, _1));
if (print)
{
loop->runEvery(3.0, std::bind(&ChargenServer::printThroughput, this));
}
string line;
for (int i = 33; i < 127; ++i)
{
line.push_back(char(i));
}
line += line;
for (size_t i = 0; i < 127-33; ++i)
{
message_ += line.substr(i, 72) + '\n';
}
}
void ChargenServer::start()
{
server_.start();
}
void ChargenServer::onConnection(const TcpConnectionPtr& conn)
{
LOG_INFO << "ChargenServer - " << conn->peerAddress().toIpPort() << " -> "
<< conn->localAddress().toIpPort() << " is "
<< (conn->connected() ? "UP" : "DOWN");
if (conn->connected())
{
conn->setTcpNoDelay(true);
conn->send(message_);
}
}
void ChargenServer::onMessage(const TcpConnectionPtr& conn,
Buffer* buf,
Timestamp time)
{
string msg(buf->retrieveAllAsString());
LOG_INFO << conn->name() << " discards " << msg.size()
<< " bytes received at " << time.toString();
}
void ChargenServer::onWriteComplete(const TcpConnectionPtr& conn)
{
transferred_ += message_.size();
conn->send(message_);
}
void ChargenServer::printThroughput()
{
Timestamp endTime = Timestamp::now();
double time = timeDifference(endTime, startTime_);
printf("%4.3f MiB/s\n", static_cast<double>(transferred_)/time/1024/1024);
transferred_ = 0;
startTime_ = endTime;
}