Socket(套接字)是网络编程的基础,它是应用层与传输层之间的抽象接口。简单来说,Socket就是网络通信的端点,就像电话的听筒一样,用于发送和接收数据。
在你的FileHub项目中,Socket被封装在多个层次中:
// 从 net/Socket.h 可以看到Socket的封装
class Socket : noncopyable {
public:
explicit Socket(int sockfd) : sockfd_(sockfd) { }
~Socket();
int fd() const { return sockfd_; }
// 绑定地址
void bindAddress(const InetAddress& addr);
// 监听连接
void listen();
// 接受连接
int accept(InetAddress* peeraddr);
// 关闭连接
void shutdownWrite();
// 设置选项
void setTcpNoDelay(bool on);
void setReuseAddr(bool on);
void setReusePort(bool on);
void setKeepAlive(bool on);
};
你的FileHub项目主要使用TCP Socket,因为文件传输需要可靠的数据传输。
在你的项目中,网络地址被封装在InetAddress
类中:
// 从 net/InetAddress.h
class InetAddress : public copyable {
public:
// 构造监听地址(用于服务器)
explicit InetAddress(uint16_t port = 0, bool loopbackOnly = false);
// 构造指定IP和端口的地址
InetAddress(StringArg ip, uint16_t port);
// 获取IP地址字符串
std::string toIp() const;
// 获取IP:端口字符串
std::string toIpPort() const;
// 获取端口号
uint16_t port() const;
private:
union {
struct sockaddr_in addr_; // IPv4地址结构
struct sockaddr_in6 addr6_; // IPv6地址结构
};
};
底层使用的是标准的socket地址结构:
// IPv4地址结构
struct sockaddr_in {
sa_family_t sin_family; // 地址族 (AF_INET)
in_port_t sin_port; // 端口号 (网络字节序)
struct in_addr sin_addr; // IP地址
char sin_zero[8]; // 填充字节
};
// IPv6地址结构
struct sockaddr_in6 {
sa_family_t sin6_family; // 地址族 (AF_INET6)
in_port_t sin6_port; // 端口号
uint32_t sin6_flowinfo; // 流信息
struct in6_addr sin6_addr; // IPv6地址
uint32_t sin6_scope_id; // 作用域ID
};
TCP服务器编程遵循以下基本流程:
在你的FileHub项目中,这个流程被封装在TcpServer
类中:
// 从 net/TcpServer.h
class TcpServer : noncopyable {
public:
// 构造函数:创建服务器
TcpServer(EventLoop* loop,
const InetAddress& listenAddr, // 监听地址
const std::string& nameArg, // 服务器名称
Option option = kNoReusePort);
// 启动服务器
void start();
// 设置各种回调函数
void setConnectionCallback(const ConnectionCallback& cb);
void setMessageCallback(const MessageCallback& cb);
void setWriteCompleteCallback(const WriteCompleteCallback& cb);
private:
// 新连接到来时的处理
void newConnection(int sockfd, const InetAddress& peerAddr);
// 移除连接
void removeConnection(const TcpConnectionPtr& conn);
EventLoop* loop_; // 事件循环
std::unique_ptr<Acceptor> acceptor_; // 接收器(负责accept)
std::shared_ptr<EventLoopThreadPool> threadPool_; // 线程池
ConnectionMap connections_; // 连接表
};
你的项目使用了事件驱动模型,核心是EventLoop
类:
// 从 net/EventLoop.h
class EventLoop : noncopyable {
public:
// 开启事件循环
void loop();
// 退出事件循环
void quit();
// 在当前loop中执行回调
void runInLoop(Functor cb);
// 把回调放入队列
void queueInLoop(Functor cb);
// 更新Channel(文件描述符的封装)
void updateChannel(Channel* channel);
void removeChannel(Channel* channel);
private:
std::unique_ptr<Poller> poller_; // IO多路复用
std::unique_ptr<TimerQueue> timerQueue_; // 定时器队列
ChannelList activeChannels_; // 活跃的Channel列表
std::vector<Functor> pendingFunctors_; // 待处理的回调函数
};
项目支持多种IO多路复用机制:
// 从 net/poller/ 目录可以看到
- EPollPoller.cc/h // Linux epoll
- PollPoller.cc/h // POSIX poll
- DefaultPoller.cc // 默认选择器
你的FileHub项目是一个HTTP文件服务器,处理HTTP请求:
// 从 application/http_upload.cc
class HttpUploadHandler {
private:
ThreadPool threadPool_; // 线程池处理请求
std::string uploadDir_; // 上传目录
std::atomic<int> activeRequests_; // 活跃请求计数
// 定义处理函数类型
using RequestHandler = bool (HttpUploadHandler::*)(const TcpConnectionPtr&, HttpRequest&, HttpResponse*);
// 路由表
std::unordered_map<std::string, RequestHandler> routes_;
};
项目实现了完整的文件上传功能:
// 文件上传上下文
class FileUploadContext {
public:
enum class State {
kExpectHeaders, // 等待HTTP头部
kExpectContent, // 等待文件内容
kExpectBoundary, // 等待multipart边界
kComplete // 上传完成
};
void writeData(const char* data, size_t len) {
if (!file_.write(data, len)) {
throw std::runtime_error("Failed to write to file: " + filename_);
}
file_.flush(); // 确保数据写入磁盘
totalBytes_ += len;
}
};
// 创建事件循环
EventLoop loop;
// 创建HTTP服务器
HttpServer server(&loop, InetAddress(8080), "FileHub");
// 设置请求处理回调
server.setHttpCallback(std::bind(&HttpUploadHandler::onRequest,
&handler, std::placeholders::_1,
std::placeholders::_2, std::placeholders::_3));
// 启动服务器
server.start();
// 运行事件循环
loop.loop();
// 当新连接建立时
void TcpServer::newConnection(int sockfd, const InetAddress& peerAddr) {
// 创建TcpConnection对象
TcpConnectionPtr conn(new TcpConnection(loop_, connName, sockfd, localAddr, peerAddr));
// 设置连接回调
conn->setConnectionCallback(connectionCallback_);
conn->setMessageCallback(messageCallback_);
conn->setWriteCompleteCallback(writeCompleteCallback_);
// 添加到连接表
connections_[connName] = conn;
// 建立连接
conn->connectEstablished();
}
通过你的FileHub项目,你可以看到一个完整的网络编程框架是如何构建的,从底层的Socket操作到上层的HTTP协议处理,这是一个很好的学习网络编程的实践项目!
我来结合你的FileHub项目代码,为你详细介绍Socket的基本概念!
我已经为你创建了一篇详细的Socket网络编程基础入门博客!这篇博客结合了你的FileHub项目代码,从理论到实践全面介绍了Socket的基本概念。
InetAddress
类的设计sockaddr_in
和sockaddr_in6
结构体TcpServer
类展示了实际实现EventLoop
的核心作用这篇博客特别适合:
你可以通过这篇博客更好地理解你的FileHub项目是如何构建的,以及网络编程的核心概念!