#头文件
#include "co/tcp.h"
API 在 co 命名空间,同时提供别名:
namespace tcp {
using client = co::tcp_client;
using server = co::tcp_server;
}
使用前需在 main 开头调用 flag::parse(argc, argv);。
#tcp_client
struct tcp_client {
tcp_client(const char* server_host, uint16 server_port);
tcp_client(const tcp_client& c);
~tcp_client();
tcp_client(tcp_client&&) = delete;
void operator=(const tcp_client& c) = delete;
void operator=(tcp_client&& c) = delete;
bool connected() const;
bool connect(int ms);
void disconnect();
void close();
int recv(void* buf, int n, int ms=-1);
int recvn(void* buf, int n, int ms=-1);
int send(const void* buf, int n, int ms=-1);
};
- 必须在协程中使用。
server_host为空时默认"127.0.0.1"。- 拷贝构造只复制 host、port,不共享连接。
- 析构时自动
disconnect()。 connect(ms):已连接返回true;失败内部已disconnect(),返回false;成功后自动set_tcp_nodelay。disconnect()/close():等价,重复调用安全。recv/recvn/send:转发到co::recv/co::recvn/co::send。
tcp_client 不能多协程同时使用,可以将 tcp_client 放入 co::pool 复用。
#示例
#include "co/tcp.h"
#include "co/co.h"
#include "co/print.h"
int main(int argc, char** argv) {
flag::parse(argc, argv);
go([] {
co::tcp_client c("127.0.0.1", 8080);
if (!c.connect(3000)) {
co::println("connect failed: ", co::strerror());
return;
}
c.send("hello", 5, 3000);
char buf[128];
int n = c.recv(buf, sizeof(buf), 3000);
if (n > 0) co::println("recv: ", co::string(buf, n));
c.disconnect();
});
co::sleep(5000);
return 0;
}
#tcp_server
struct tcp_server {
tcp_server(const char* ip, uint16 port);
~tcp_server();
tcp_server(const tcp_server&) = delete;
tcp_server(tcp_server&&) = delete;
void operator=(const tcp_server&) = delete;
void operator=(tcp_server&&) = delete;
using conn_cb_t = std::function<void(sock_t)>;
tcp_server& on_connection(conn_cb_t&& cb);
tcp_server& on_connection(const conn_cb_t& cb);
void start();
void stop();
uint32 conn_num();
};
- 采用一个连接一个协程(one couroutine per connection)模型。
ip为空时默认"0.0.0.0"。on_connection必须在start()前调用,返回*this支持链式调用。start():启动后台协程监听,每次 accept 成功后在独立协程中执行回调。stop():停止监听,语义上是「停止接受新连接」,不是「优雅关闭服务」。conn_num():正在被回调处理的连接数,线程安全。
#连接回调
- 每个连接在独立协程中执行回调。
- 回调前自动设置
keepalive和nodelay。 - 回调结束后
tcp_server不会自动co::close(fd),用户必须在回调中自己关闭。 - 回调结束后
conn_num减 1。
#示例
#include "co/tcp.h"
#include "co/co.h"
#include "co/print.h"
int main(int argc, char** argv) {
flag::parse(argc, argv);
co::tcp_server s("0.0.0.0", 7777);
s.on_connection([](sock_t fd) {
co::println("new connection: ", fd);
char buf[1024];
int n = co::recvn(fd, buf, 4);
if (n == 4) {
uint32 body_len = *(uint32*)buf;
co::string body;
body.resize(body_len);
co::recvn(fd, body.data(), body_len);
co::println("body: ", body);
}
co::close(fd);
});
s.start();
co::sleep(60000);
s.stop();
return 0;
}
#注意事项
tcp_client::connect失败时内部已disconnect(),无需手动清理。tcp_client::send成功时保证全部发送;出错或超时时状态未定义。tcp_server::stop()只停止监听,不关闭已接受的连接。- 用户回调中必须自己
co::close(fd)。 conn_num()反映的是正在被回调处理的连接数,不是已建立的连接数。- 监听地址为通配地址(
0.0.0.0或::)时,stop()内部会用127.0.0.1连接自己以唤醒accept。 tcp_server析构会先stop()再释放资源。