虽然C/C++提供了内置的整数类型定义,但不同的计算机硬件体系下整数的宽度是不一样的
32位--》int 4字节
64 int 8字节
为了使我们以后开发的程序具有跨平台的兼容性,所以我们使用Boost提供的精确宽度的整数定义
为了与客户端统一我们还是把文件建在share目录
新建msg_def.h
#ifndef _MSGDEF_H
#define _MSGDEF_H
#include <boost/cstdint.hpp>
#include <iostream>
#include <string>
using namespace std;
//虽然C/C++提供了内置的整数类型定义,但不同的计算机硬件体系下整数的宽度是不一样的
//32位--》int 4字节
//64 int 8字节
//为了使我们以后开发的程序具有跨平台的兼容性,所以我们使用Boost提供的精确宽度的整数定义
//精确宽度整数的定义
//使用using引入自己的名字空间
using boost::int8_t;
using boost::uint8_t;
using boost::int16_t;
using boost::uint16_t;
using boost::int32_t;
using boost::uint32_t;
using boost::int64_t;
using boost::uint64_t;
typedef boost::uint8_t byte_t;
typedef boost::uint8_t uchar_t;
typedef unsigned short ushort_t;
typedef unsigned int uint_t;
typedef unsigned long ulong_t;
typedef boost::uint16_t word_t;
typedef boost::uint32_t dword_t;
//定义消息头的结构
struct msg_head
{
uint32_t type; //消息类型 整数
uint32_t size; //消息体大小 整数
uint32_t chksum; //CRC校验 整数
};
//如果消息的长度大于 MAX_MSG_SIZE
//可以分解数据
#define MAX_MSG_SIZE 1024 //消息体最大长度
#endif
下面新建消息体tcp_message.h
使用typedef是因为这命名实在是太长了所以取了一个别名
#ifndef _TCP_MESSAGE_H
#define _TCP_MESSAGE_H
#include "msg_def.h"
#include <boost/checked_delete.hpp> //待检查的指针删除
#include <boost/shared_ptr.hpp> //智能指针
#include <boost/smart_ptr.hpp>
#include <boost/array.hpp> //关于数组的STL容器风格包装
#include <boost/function.hpp>
#include <boost/noncopyable.hpp> //禁止拷贝
#include <boost/crc.hpp> //crc校验
using namespace boost;
//class tcp_session;
//typedef boost::shared_ptr<tcp_session> tcp_session_ptr; //共享指针定义
//为了避免消息在传递过程中反复拷贝的代价
//把该类设计成一个不可拷贝的,这样就强制只能以指针的方式使用;
class tcp_message: boost::noncopyable
{
public:
//为了支持创建方式的灵活处理,
typedef boost::function<void(tcp_message *)> destroy_type; //销毁器
typedef char char_type;
typedef boost::array<char_type, sizeof(msg_head)> head_data_type; //存放消息头的数据(固定大小)
typedef boost::array<char_type, MAX_MSG_SIZE> body_data_type; //存放消息体的数据(不是固定大小)
public:
//template<typename Func>
//tcp_message(const tcp_session_ptr &s, Func func);
//tcp_message(const tcp_session_ptr &s);
//tcp_message();
//tcp_session_ptr get_session();
tcp_message();
template<typename Func>
tcp_message(Func func);
void destroy();
//可以直接访问数据的成员函数
head_data_type& head_data();
body_data_type& msg_data();
//转换为消息头结构的操作
msg_head* get_head();
//简单的检查消息头是否正确
bool check_head();
//检查消息体的CRC校验
bool check_msg_crc();
void set_msg_crc();
private:
//tcp_session_ptr m_session;
destroy_type m_destroy;
head_data_type m_head; //消息头
body_data_type m_msg; //消息体
};
//这些是一些意义更明确,更便于使用的名称
typedef tcp_message tcp_request; //tcp请求消息
typedef tcp_message tcp_response; //tcp响应消息
typedef tcp_request* tcp_request_ptr; //tcp请求消息的指针
typedef tcp_response* tcp_response_ptr; //tcp响应消息的指针
#endif
tcp_message.cpp
#include "tcp_message.h"
//
// template<typename Func>
// tcp_message::tcp_message(const tcp_session_ptr &s, Func func):
// m_session(s), m_destroy(func)
// {
//
// }
//
//
// tcp_message::tcp_message(const tcp_session_ptr &s):
// m_session(s)
// {
//
// }
//
//
// tcp_session_ptr tcp_message::get_session()
// {
// return m_session;
// }
tcp_message::tcp_message()
{
}
template<typename Func>
tcp_message::tcp_message(Func func)
:m_destroy(func)
{
}
//销毁消息
void tcp_message::destroy()
{
//如果传入的销毁器,就使用传入的销毁器进行销毁消息
if (m_destroy)
{
m_destroy(this);
}
else
{
//使用boost中的销毁器销毁自身
boost::checked_delete(this);
}
}
//获取消息头数据
tcp_message::head_data_type &tcp_message::head_data()
{
return m_head;
}
//获取消息体数据
tcp_message::body_data_type &tcp_message::msg_data()
{
return m_msg;
}
//将消息头的数据转换为消息体的结构
msg_head* tcp_message::get_head()
{
return reinterpret_cast<msg_head *>(m_head.data());
}
//简单地检查消息头是否正确
bool tcp_message::check_head()
{
return (get_head()->size < MAX_MSG_SIZE);
}
//检查消息体的crc校验
bool tcp_message::check_msg_crc()
{
//使用boost的crc库进行简单的校验
boost::crc_32_type crc32;
//计算校验和
crc32.process_bytes(&m_msg[0], get_head()->size);
//比较crc值
return get_head()->chksum == crc32.checksum();
}
//在消息头里设置消息体的crc校验
void tcp_message::set_msg_crc()
{
boost::crc_32_type crc32;
//计算校验和
crc32.process_bytes(&m_msg[0], get_head()->size);
//设置校验
get_head()->chksum = crc32.checksum();
}
上面消息体一已经建好了
我们测试下消息体新建test_tcp_message.h
#ifndef _TEST_TCPMESSAGE_H
#define _TEST_TCPMESSAGE_H
#include "comm.h"
#include "tcp_message.h"
void print_msg(boost::shared_ptr<tcp_message> pMsg)
{
cout<<"head type:"<<pMsg->get_head()->type<<endl;
cout<<"head size:"<<pMsg->get_head()->size<<endl;
cout<<"head checksum:"<<pMsg->get_head()->chksum<<endl;
cout<<"data:"<<pMsg->msg_data().data()<<endl;
}
//test tcp_message
void send_data(socket_type sock, const std::string& str)
{
//消息头
msg_head head;
head.size = str.length() + 1;
head.chksum = std::for_each(str.begin(), str.end(), crc_32_type())();
head.type = 99;
//构建消息体
boost::shared_ptr<tcp_message> pSendMsg = boost::make_shared<tcp_message>();
pSendMsg->get_head()->chksum = head.chksum;
pSendMsg->get_head()->size = head.size;
pSendMsg->get_head()->type = head.type;
//拷贝数据
//std::copy(str.begin(), str.end(), pSendMsg->msg_data().begin());
memcpy(pSendMsg->msg_data().begin(), str.c_str(), head.size);
//设置校验和
pSendMsg->set_msg_crc();
//阻塞发送
//先发送消息头
sock->write_some(buffer(pSendMsg->head_data().data(), pSendMsg->head_data().size()));
//再发送消息体
sock->write_some(buffer(pSendMsg->msg_data().data(), pSendMsg->get_head()->size));
int n = pSendMsg->get_head()->size;
//输出
print_msg(pSendMsg);
}
std::string read_data(socket_type sock)
{
//创建消息体
boost::shared_ptr<tcp_message> pRecvMsg = boost::make_shared<tcp_message>();
//读消息头
sock->read_some(buffer(pRecvMsg->head_data().data(), pRecvMsg->head_data().size()));
//读消息体
sock->read_some(buffer(pRecvMsg->msg_data().data(), pRecvMsg->get_head()->size));
//校验
bool bRight = pRecvMsg->check_msg_crc();
//输出数据
if (bRight)
{
print_msg(pRecvMsg);
}
return pRecvMsg->msg_data().data();
}
#endif
上面主要是写了读取和发送消息体,发送的是传人了一个string就是我们要发送的消息
修改下我们昨天用到的main.cpp
#include <iostream>
using namespace std;
#include "test_tcp_message.h"
void proc_data(socket_type sock)
{
//输出客户端的地址的字符串
cout<<"client connected :"<<sock->remote_endpoint().address()<<endl;
while (true)
{
//捕获可能发生的异常
try
{
cout<<"///"<<endl;
cout<<"recv data from client:"<<endl;
std::string strData = read_data(sock);
cout<<endl;
cout<<"send data to client:"<<endl;
send_data(sock, strData);
cout<<"///"<<endl<<endl;
}
catch(std::exception& e)
{
cout<<e.what()<<endl;
break;
}
}
}
//处理网络连接
void proc_accept()
{
cout<<"wait connect..."<<endl;
io_service ios; //asio程序必须的io_service对象
ip::tcp::endpoint ep(ip::tcp::v4(), PORT_NUM);
//用于接收连接
ip::tcp::acceptor acceptor(ios, ep);
while (true)
{
//初始化一个socket对象
socket_type sock(new ip::tcp::socket(ios));
//阻塞等待socket连接
acceptor.accept(*sock);
//为每一个建立连接的客户端建立一个线程处理数据
thread t(proc_data, sock);
}
}
int main(int argc, char *argv)
{
//创建线程,以及传递线程处理函数
thread t1(proc_accept);
//线程阻塞等待,知道线程处理结束
t1.join();
return 0;
};
主要是修改了proc_data函数,先读取了客户端的消息体然后又发送到客户端