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的请求报文。