已有数据同步功能完成

This commit is contained in:
1iaan
2026-01-29 10:47:24 +00:00
parent fe257cafec
commit 2bdb48d63d
27 changed files with 1134 additions and 139 deletions

View File

@@ -3,40 +3,56 @@
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
/* 创建并监听用于接收快照的 TCP 监听 socket成功返回 listen fd失败返回 -1 */
static int create_listen_socket(const char *ip, int port){
static int recv_exact(int sockfd, char *buf, size_t len) {
size_t received = 0;
while (received < len) {
ssize_t ret = recv(sockfd, buf + received, len - received, 0);
if (ret <= 0) {
if (ret == 0) {
fprintf(stderr, "connection closed by peer\n");
} else {
perror("recv");
}
return -1;
}
received += ret;
}
return 0;
}
/* 主动连接 master 指定地址用于控制面通信SSYNC / SREADY返回连接 fd 或 -1 */
static int connect_master(const char *master_ip, int master_port){
}
/* 通过控制连接向 master 发送 SSYNC 请求,声明本 slave 的快照接收地址 */
static int send_ssync(int ctrl_fd, const char *listen_ip, int listen_port){
}
/* 接收并校验 master 对 SSYNC 的确认响应(如 +OK成功返回 0 */
static int recv_ssync_ok(int ctrl_fd){
}
/* 在快照监听 socket 上阻塞等待 master 的快照发送连接,返回已建立连接的 fd */
static int accept_snapshot_conn(int listen_fd){
}
/* 从快照连接中接收完整快照数据并构建内存状态,确保快照已完全应用 */
static int recv_and_apply_snapshot(int snapshot_fd){
}
/* 通过控制连接向 master 发送 SREADY 通知表示快照已应用slave 即将进入服务态 */
int send_sready(int ctrl_fd){
static int receive_snapshot_file(int sockfd, const char *filename) {
FILE *fp = fopen(filename, "wb");
if (!fp) {
perror("fopen");
return -1;
}
char buf[4096];
ssize_t n;
while ((n = recv(sockfd, buf, sizeof(buf), 0)) > 0) {
if (fwrite(buf, 1, n, fp) != n) {
perror("fwrite");
fclose(fp);
return -1;
}
}
if (n < 0) {
perror("recv");
fclose(fp);
return -1;
}
fclose(fp);
return 0;
}
@@ -47,64 +63,155 @@ int slave_bootstrap(
int master_port
) {
int listen_fd = -1;
int ctrl_fd = -1;
int snap_fd = -1;
int master_fd = -1;
int snapshot_fds[3] = {-1, -1, -1};
int ret = -1;
/* 1. 监听 snapshot port */
listen_fd = create_listen_socket(listen_ip, listen_port);
// 1. 创建监听socket
listen_fd = socket(AF_INET, SOCK_STREAM, 0);
if (listen_fd < 0) {
goto fail;
perror("socket");
goto cleanup;
}
/* 2. 连接 master, 发送 SSYNC */
ctrl_fd = connect_master(master_ip, master_port);
if (ctrl_fd < 0) {
goto fail;
// 设置SO_REUSEADDR
int opt = 1;
if (setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
perror("setsockopt");
goto cleanup;
}
if (send_ssync(ctrl_fd, listen_ip, listen_port) < 0) {
goto fail;
struct sockaddr_in listen_addr;
memset(&listen_addr, 0, sizeof(listen_addr));
listen_addr.sin_family = AF_INET;
listen_addr.sin_port = htons(listen_port);
listen_addr.sin_addr.s_addr = inet_addr(listen_ip);
if (bind(listen_fd, (struct sockaddr*)&listen_addr, sizeof(listen_addr)) < 0) {
perror("bind");
goto cleanup;
}
if (recv_ssync_ok(ctrl_fd) < 0) {
goto fail;
if (listen(listen_fd, 5) < 0) {
perror("listen");
goto cleanup;
}
close(ctrl_fd);
ctrl_fd = -1;
printf("Slave listening on %s:%d\n", listen_ip, listen_port);
/* 3. accept snapshot 连接 */
snap_fd = accept_snapshot_conn(listen_fd);
if (snap_fd < 0) {
goto fail;
// 2. 连接master并发送SSYNC命令
master_fd = socket(AF_INET, SOCK_STREAM, 0);
if (master_fd < 0) {
perror("socket");
goto cleanup;
}
/* 4. 接收 snapshot */
if (recv_and_apply_snapshot(snap_fd) < 0) {
goto fail;
struct sockaddr_in master_addr;
memset(&master_addr, 0, sizeof(master_addr));
master_addr.sin_family = AF_INET;
master_addr.sin_port = htons(master_port);
master_addr.sin_addr.s_addr = inet_addr(master_ip);
if (connect(master_fd, (struct sockaddr*)&master_addr, sizeof(master_addr)) < 0) {
perror("connect to master");
goto cleanup;
}
close(snap_fd);
snap_fd = -1;
printf("Connected to master %s:%d\n", master_ip, master_port);
// 构造RESP协议消息: SSYNC listen_ip listen_port
char ssync_cmd[256];
int cmd_len = snprintf(ssync_cmd, sizeof(ssync_cmd),
"*3\r\n$5\r\nSSYNC\r\n$%zu\r\n%s\r\n$%d\r\n%d\r\n",
strlen(listen_ip), listen_ip,
(int)snprintf(NULL, 0, "%d", listen_port), listen_port);
if (send(master_fd, ssync_cmd, cmd_len, 0) < 0) {
perror("send SSYNC");
goto cleanup;
}
printf("Sent SSYNC command to master\n");
// 3. 接收master的+OK\r\n回包
char resp[32];
if (recv_exact(master_fd, resp, 5) < 0) {
fprintf(stderr, "Failed to receive OK from master\n");
goto cleanup;
}
if (memcmp(resp, "+OK\r\n", 5) != 0) {
fprintf(stderr, "Unexpected response from master: %.5s\n", resp);
goto cleanup;
}
printf("Received OK from master\n");
// 4. Accept来自master的三个连接接收三个snapshot文件
const char *snapshot_files[3] = {
"data/kvs_array.db",
"data/kvs_rbtree.db",
"data/kvs_hash.db"
};
for (int i = 0; i < 3; i++) {
struct sockaddr_in client_addr;
socklen_t addr_len = sizeof(client_addr);
snapshot_fds[i] = accept(listen_fd, (struct sockaddr*)&client_addr, &addr_len);
if (snapshot_fds[i] < 0) {
perror("accept");
goto cleanup;
}
printf("Accepted connection %d from %s:%d\n",
i + 1, inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
if (receive_snapshot_file(snapshot_fds[i], snapshot_files[i]) < 0) {
fprintf(stderr, "Failed to receive snapshot file %d\n", i + 1);
goto cleanup;
}
printf("Received snapshot file: %s\n", snapshot_files[i]);
close(snapshot_fds[i]);
snapshot_fds[i] = -1;
}
// 5. 关闭监听和snapshot连接发送SREADY
close(listen_fd);
listen_fd = -1;
/* 5. 通知 master 快照传输完毕 */
ctrl_fd = connect_master(master_ip, master_port);
if (ctrl_fd >= 0) {
send_sready(ctrl_fd);
close(ctrl_fd);
ctrl_fd = -1;
const char *sready_cmd = "*1\r\n$6\r\nSREADY\r\n";
if (send(master_fd, sready_cmd, strlen(sready_cmd), 0) < 0) {
perror("send SREADY");
goto cleanup;
}
/* 6. bootstrap complete */
return 0;
printf("Sent SREADY to master\n");
// 6. 接收回包+OK\r\n
if (recv_exact(master_fd, resp, 5) < 0) {
fprintf(stderr, "Failed to receive final OK from master\n");
goto cleanup;
}
fail:
if (snap_fd >= 0) close(snap_fd);
if (ctrl_fd >= 0) close(ctrl_fd);
if (memcmp(resp, "+OK\r\n", 5) != 0) {
fprintf(stderr, "Unexpected final response from master: %.5s\n", resp);
goto cleanup;
}
printf("Received final OK from master, bootstrap complete\n");
ret = 0;
cleanup:
if (master_fd >= 0) close(master_fd);
if (listen_fd >= 0) close(listen_fd);
return -1;
for (int i = 0; i < 3; i++) {
if (snapshot_fds[i] >= 0) close(snapshot_fds[i]);
}
return ret;
}