《C++Boost库asio》第八篇 Echo服务器
// Echo服务器 :
// 客户端给服务器发来的信息,服务器原封不动的还给客户端。作用:保证服务器可以完
// 整的收到客户端的消息包,并且能把客户端的信息完整的会给客户端,确认两边通信是没有问题的
// 消息包:可以打上时间戳。包的长度两边都是不知道的
// 我另外加入了一条逻辑,当有新的客户端进入后,先发送一条当前时间的消息给客户端;
// 再开始原封不动的把客户端发来的消息还给客户端
#include <boost/asio.hpp>
#include <utility>
#include <iostream>
#include <string>
using boost::asio::ip::tcp;
std::string make_daytime_string()
{
using namespace std;//For time_t, time and ctime;
//time_t now = time(0);
auto now = time(nullptr);
return ctime(&now); // 返回const char*
}
// session是智能指针管理方法,从public std::enable_shared_from_this<session>派生出来就意
// 味着自身是一个智能指针
class session : public std::enable_shared_from_this<session>
{
public:
session(tcp::socket socket) : socket_(std::move(socket))
{
// start();
// do_read();
// 如果写了start()或者do_read(),就调用了shared_from_this(),但是这个时候自身构造还没有完毕,还在
//构造函数里,没有出构造这个圈子,这个时候调用shared_from_this()是没办法通过自身来构造
//shared_from_this的,构造没完成,new就没完成,自己就还不是智能指针呢,所以public
//std::enable_shared_from_this<session>的派生类不能在构造函数和析构函数调用
//shared_from_this(),这个是它的局限性。
}
void start()
{
first_msg();
}
private:
// 另外加的逻辑,先发送一条消息给客户端;
void first_msg()
{
message_ = make_daytime_string();//返回当前时间
auto self = shared_from_this();
boost::asio::async_write(
socket_,
boost::asio::buffer(message_),
(const boost::system::error_code & error, std::size_t length){
self->do_read(error);
std::cout << "send first msg..." << std::endl;
});
}
void do_read(const boost::system::error_code & error)
{
if (!error) {
// 等待客户端信息的传来
// 拷贝,引用计数加1,shared_from_this()是右值
auto self(shared_from_this());
//发出一个事件,收到一点数据也可以,读一些客户端传来的信息,存到data数组里,最
//大长度1024,self传入进来的作用是做一个拷贝,拷贝后,它的声明周期和lambda的是一样长的,
//引用计数加1
socket_.async_read_some(
boost::asio::buffer(data_, max_length),
(boost::system::error_code ec, std::size_t length)
{
if (!ec)
{
do_write(ec, length);//写数据给客户端
}
});
}
else
{
std::cout << "in do_read error" << std::endl;
}
}
//收到什么信息就写回去
void do_write(const boost::system::error_code & error, std::size_t length)
{
if (!error) {
auto self(shared_from_this());
boost::asio::async_write(
socket_, boost::asio::buffer(data_, length),
(boost::system::error_code ec, std::size_t length)
{
if (!ec)
{
do_read(ec);
}
});
}
else
{
std::cout << "in do_write error" << std::endl;
}
}
tcp::socket socket_;
enum { max_length = 1024 };
char data_;
std::string message_;
};
class server
{
public:
server(boost::asio::io_service &io_service, short port) :
acceptor_(io_service, tcp::endpoint(tcp::v4(), port)),
socket_(io_service)
{
//do_accept();//在构造函数里把资源构造好就可以了,不要去做事情,做的事情移到外面来
}
//提供一个外部函数,没有必要构造好就立即工作,可以在某个时机调用
void start()
{
do_accept();
}
private:
//接收客户端,对客户端进行构造,调用start,接着接收下一个客户端
void do_accept()//异步接收客户端的连接
{
acceptor_.async_accept(socket_, (boost::system::error_code ec)
{
if (!ec)
{
//move操作过后,socket相当于还是socket_(io_service)这种状态.
auto newSession = std::make_shared<session>(std::move(socket_));
newSession->start();
}//session当成一个客户端
do_accept();//不管错没错都接着接收客户端
});
}
tcp::acceptor acceptor_;//监听端口号,连接新的客户端
tcp::socket socket_;
};
int main()
{
// 我们的程序和操作系统底层之间的一个相互传递的枢纽
// 会把事件按照一定方式进行处理;
boost::asio::io_service io;
try
{
server s(io, 2001);
s.start();
io.run();
}
catch (std::exception &e)
{
std::cerr << e.what() << std::endl;
}
std::cout << "mian finish run \n" << std::endl;// 提示下main函数已经结束了
system("pause");// 让VS控制台程序不会一闪而过;
return 0;
}
//move完后,原先的值是未定义的状态,相当于是被初始化了。
class ObjectWithMove {
public:
ObjectWithMove(int a) : m_a(a), m_p(p) {}
ObjectWithMove(ObjectWithMove&& lhs) : m_a(lhs.m_a), m_p(lhs.m_p) {
lhs.m_a = 0;
lhs.m_p = nullptr;
}
private:
int m_a;
int *m_p;
};
用C#写了个简单的客户端来测试:
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Text;
using System;
public class Test002 {
private static byte[] result = new byte;
int main () {
//设定服务器IP地址
IPAddress ip = IPAddress.Parse("127.0.0.1");
Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
clientSocket.Connect(new IPEndPoint(ip, 2001)); //配置服务器IP与端口
Console.WriteLine("连接服务器成功");
}
catch
{
Console.WriteLine("连接服务器失败,请按回车键退出!");
return;
}
//通过clientSocket接收数据
int receiveLength = clientSocket.Receive(result);
Console.WriteLine("接收服务器消息:"+Encoding.ASCII.GetString(result,0,receiveLength));
//通过 clientSocket 发送数据
for (int i = 0; i < 3; i++)
{
try
{
Thread.Sleep(1000); //等待1秒钟
string sendMessage = "client send Message Hellp" + DateTime.Now;
clientSocket.Send(Encoding.ASCII.GetBytes(sendMessage));
Console.WriteLine("向服务器发送消息:" + sendMessage);
receiveLength = clientSocket.Receive(result);
Console.WriteLine("接收服务器消息:"+Encoding.ASCII.GetString(result,0,receiveLength));
}
catch
{
Console.WriteLine("与服务器断开连接");
clientSocket.Shutdown(SocketShutdown.Both);
clientSocket.Close();
break;
}
}
Console.WriteLine("发送完毕,按回车键退出");
}
}
对应的C++客户端在下一篇研究{:10_266:}
页:
[1]