库文件、结构体和全局变量
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <errno.h>
#include <getopt.h>
#include <arpa/inet.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <netdb.h>
#include <unistd.h>
#include <rdma/rsocket.h>
#include "common.h"
union rsocket_address {
struct sockaddr sa;
struct sockaddr_in sin;
struct sockaddr_in6 sin6;
struct sockaddr_storage storage;
};
// 这些都是通信要使用的配置信息
static const char *port = "7427";
static char *dst_addr;
//
static char *dst_file;
static char *src_file;
static struct timeval start, end;
//static void buf[1024 * 1024];
static uint64_t bytes;
static int fd;
static void *file_addr;
enum {
CMD_NOOP,
CMD_OPEN,
CMD_CLOSE,
CMD_WRITE,
CMD_RESP = 0x80,
};
/* TODO: handle byte swapping */
struct msg_hdr {
uint8_t version;
uint8_t command;
uint16_t len;
uint32_t data;
uint64_t id;
};
struct msg_open {
struct msg_hdr hdr;
char path[0];
};
struct msg_write {
struct msg_hdr hdr;
uint64_t size;
};
main函数
分析代码的主入口,可以看到首先是参数分析,根据参数的情况选择server_run或者是client_run。
int main(int argc, char **argv)
{
int ret;
if (argc == 1 || argv[1][0] == '-') {
server_opts(argc, argv);
ret = server_run();
} else {
client_opts(argc, argv);
ret = client_run();
}
return ret;
}
client_run函数
static int client_run(void)
{
struct msg_hdr ack;
int ret, rs;
size_t len;
rs = client_connect(); // 建立连接,通过rsocket的方式
if (rs < 0)
return rs;
ret = client_open(rs); // 发送数据过去
if (ret)
goto shutdown;
ret = client_start_write(rs); // 开始发送数据。
if (ret)
goto close;
printf("...");
fflush(NULL);
gettimeofday(&start, NULL);
len = rsend(rs, file_addr, bytes, 0); //发送数据,源地址是file_addr,大小是bytes。
if (len == bytes)
ret = msg_get_resp(rs, &ack, CMD_WRITE); // 获取相应信息,提取数据到ack里面
else
ret = (int) len;
gettimeofday(&end, NULL);
close:
client_close(rs);
shutdown:
rshutdown(rs, SHUT_RDWR);
rclose(rs);
if (!ret)
show_perf();
return ret;
}
client_open函数
主要是打开文件,然后发送数据到dst_file里面,然后判断发送的数据长度,然后判断返回的类型是否是CMD_RESP。
static int client_open(int rs)
{
struct msg_open *msg; // 里面包含了一个柔性数组和一个msg_hdr信息
struct stat stats;
uint32_t len;
int ret;
printf("opening...");
fflush(NULL);
fd = open(src_file, O_RDONLY);
if (fd < 0)
return fd;
ret = fstat(fd, &stats); // 这个是一个系统调用,把fd的信息存储在stats
if (ret < 0)
goto err1;
bytes = (uint64_t) stats.st_size; // fd文件的实际数据的大小
file_addr = mmap(NULL, bytes, PROT_READ, MAP_SHARED, fd, 0); // 做一个内存映射mmap
if (file_addr == (void *) -1) {
ret = errno;
goto err1;
}
len = (((uint32_t) strlen(dst_file)) + 8) & 0xFFFFFFF8;
msg = calloc(1, sizeof(*msg) + len);
if (!msg) {
ret = -1;
goto err2;
}
msg->hdr.command = CMD_OPEN;
msg->hdr.len = sizeof(*msg) + len;
msg->hdr.data = (uint32_t) stats.st_mode; // 文件的访问权限
strcpy(msg->path, dst_file); // 这个是把里面的内容拷贝到dst_file文件里面
ret = rsend(rs, msg, msg->hdr.len, 0); // 发送msg地址,长度为msg->hdr.len这么大
if (ret != msg->hdr.len)
goto err3;
ret = msg_get_resp(rs, &msg->hdr, CMD_OPEN); // 获取返回的信息,通过获取头部信息,判断长度和command是否是等于CMD_RESP。
if (ret)
goto err3;
return 0;
err3:
free(msg);
err2:
munmap(file_addr, bytes);
err1:
close(fd);
return ret;
}
server_run函数
服务器的代码执行入口,首先server_listen函数,内部具体做了创建rsocket,然后设置rsocket属性,然后绑定源地址,然后开始rlisten监听连接。
然后进入一个死循环处理请求连接,raccepte函数,然后打印client的地址信息,然后开启一个新的server_process函数处理这个请求。后面就是关闭。
static int server_run(void)
{
int lrs, rs;
union rsocket_address rsa;
socklen_t len;
lrs = server_listen();
if (lrs < 0)
return lrs;
while (1) {
len = sizeof rsa;
printf("waiting for connection...");
fflush(NULL);
rs = raccept(lrs, &rsa.sa, &len);
printf("client: %s\n", _ntop(&rsa));
server_process(rs);
rshutdown(rs, SHUT_RDWR);
rclose(rs);
}
return 0;
}
server_process函数
从rs里面获取一个msg信息,然后读取命令信息,最后根据不同的事件进行不同的处理。
static void server_process(int rs)
{
struct msg_hdr msg;
int ret;
do {
ret = msg_recv_hdr(rs, &msg);
if (ret != sizeof msg)
break;
switch (msg.command) {
case CMD_OPEN:
ret = server_open(rs, &msg);
break;
case CMD_CLOSE:
server_close(rs, &msg);
ret = 0;
break;
case CMD_WRITE:
ret = server_write(rs, &msg);
break;
default:
msg_send_resp(rs, &msg, EINVAL);
ret = -1;
break;
}
} while (!ret);
}