1. TCP Server类封装测试
1.1 调用如下
static Logger::ptr g_logger = KIT_LOG_ROOT();void run(){auto addr = Address::LookUpAny("0.0.0.0:8888");//特意测试一下Unix域套接字是否有问题auto addr2 = UnixAddress::ptr(new UnixAddress("./tmp/unix_addr"));KIT_LOG_INFO(g_logger) << "addr=" << addr->toString();KIT_LOG_INFO(g_logger) << "addr2=" << addr2->toString();std::vector<Address::ptr> in_addrs, out_addrs;in_addrs.push_back(addr);in_addrs.push_back(addr2);TcpServer::ptr server(new TcpServer);while(!server->bind(in_addrs, out_addrs))sleep(2);server->start();}int main(){IOManager iom("tcp_server");iom.schedule(&run);return 0;}
BUG:接收客户端新连接的时Socket::ptr == nullptr
现象:Socket类封装的accept返回了一个空的智能指针,但是返回的通信套接字的是大于0的,Socket对象初始化发生问题。
问题解决:GDB调试跟踪发现传入的fd参数有误,是个低级错误…….

1.2 运行结果
已经能够正确的识别两个不同类型地址的套接字,一个是IPv4地址、TCP类型的套接字;一个是Unix通信文件路径、TCP类型的套接字。
小BUG:Unix域套接字无法设置IPPROTO_TCP这一级别的参数
现象:centOS环境下似乎可以设置,ubuntu环境下设置不了
#define PATH "./tmp/unix_addr"int main(){int fd = socket(AF_UNIX, SOCK_STREAM, 0);if(fd < 0)std::cout << "socket error" << std::endl;struct sockaddr_un sockaddr;bzero(&sockaddr, sizeof(struct sockaddr_un));sockaddr.sun_family = AF_UNIX;strcpy(sockaddr.sun_path, PATH);int n = offsetof(struct sockaddr_un, sun_path) + strlen(sockaddr.sun_path);int val = 1;int ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));if(ret < 0){std::cout << "setsockopt1 error, errno=" << errno<< ", is:"<< strerror(errno) << std::endl;}ret = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));if(ret < 0){std::cout << "setsockopt2 error, errno=" << errno<< ", is:" << strerror(errno) << std::endl;}return 0;}

2. 实例:echo_server
目的:构建一个相对标准的对这一套服务器框架接口使用的小实例,以加强对接口使用的理解以及排错。
使用:
- 继承
tcp_server.cpp封装好的TCP服务器,重写虚函数handleClient()执行本次服务器中我们希望执行的服务:接收来自客户端的消息,并且缓存在内存池ByteArray类中,又从内存池中读出数据,并且打印验证。 - 写一个
run()方法,在其中构建一个EchoServer对象,作为服务器一直被调度。绑定任意地址0.0.0.0+8888端口,作为服务器的对外通信网络地址。开启服务器运行,开始监听、接收新客户端的连接请求。
**echo_server.cpp**```cppinclude “../kit_server/tcp_server.h”
include “../kit_server/Log.h”
include “../kit_server/iomanager.h”
include “../kit_server/socket.h”
include “../kit_server/bytearray.h”
include
include
include
include
include
static kit_server::Logger::ptr g_logger = KIT_LOG_ROOT();
class EchoServer: public kit_server::TcpServer
{
public:
typedef std::shared_ptr
//标识输出的内容是二进制还是文本EchoServer(Type type);//处理已经连接的客户端 完成数据交互通信void handleClient(kit_server::Socket::ptr client);
private: Type m_type = Type::UNKOWN;
};
EchoServer::EchoServer(Type type) :m_type(type) {
}
//处理已经连接的客户端 完成数据交互通信 void EchoServer::handleClient(kit_server::Socket::ptr client) { KIT_LOG_INFO(g_logger) << “handleClient start, client=” << *client;
kit_server::ByteArray::ptr ba(new kit_server::ByteArray);while(1){ba->clear();std::vector<struct iovec> iovs;ba->getWriteBuf(iovs, 1024);int ret = client->recv(&iovs[0], iovs.size());if(ret == 0){KIT_LOG_INFO(g_logger) << "client closed:" << *client;break;}else if(ret < 0){KIT_LOG_ERROR(g_logger) << "client error, errno=" << errno<< ", is:" << strerror(errno);break;}ba->setPosition(ba->getPosition() + ret);ba->setPosition(0);if(m_type == Type::TEXT){// KIT_LOG_INFO(g_logger) << "recv msg:" << ba->toString();std::cout << "recv msg:" << ba->toString() << std::endl;}else{// KIT_LOG_INFO(g_logger) << "recv msg:" << ba->toHexString();std::cout << "recv msg:" << ba->toHexString() << std::endl;}}
}
EchoServer::Type type = EchoServer::Type::TEXT;
void run() { EchoServer::ptr server(new EchoServer(type));
auto addr = kit_server::Address::LookUpAny("0.0.0.0:8888");while(!server->bind(addr))sleep(1);server->start();
}
int main(int argc, char* argv[]) { if(argc < 2) { KIT_LOG_INFO(g_logger) << “used as:” << argv[0] << “-t] or [“ << argv[0] << “-b]”; return 0; }
if(strcmp(argv[1], "-b") == 0){type = EchoServer::Type::BIN;}kit_server::IOManager iom("echo_server", 2);iom.schedule(&run);return 0;
} ```
2.1 小测试1:使用telnet本地回环连接服务器
$ telnet 127.0.0.1 8888 
可以看到服务器已经收到消息,并且能够成功写入内存池,又从内存池读出,并且完整显示。
2.2 小测试2:使用Web浏览器连接服务器
由于是从物理主机的浏览器上键入网络地址直接访问,需要将虚拟机Ubuntu上的防火墙关闭,打开外来连接的权限才能访问成功,此处略。
可以看到服务器成功收到来自浏览器的连接,并且收到HTTP的请求报文。
