已有数据同步功能完成
This commit is contained in:
237
kvs_slave.c
237
kvs_slave.c
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user