RDMA编程实践-SEND-RECEICVE原语应用

RDMA编程实践

本文描述了RDMA编程过程中的SEND-RECEIVE双边原语的代码实现。包含多个版本,1、client向server发送消息,server回复client收到消息(ACK),然后两边断开连接。2、server端循环等待客户端建立连接,client发送一次消息后,双方断开连接。3、server端循环等待客户端建立连接,一旦建立,client端可以一直向server端发送消息,直到发送消息为disconnect,server和client断开链接,但是server此时仍然可以等待别的client发送消息。
代码基于代码基于send-receive样例实现。关于代码注释,可以参考代码解释:
Makefile文件、会编译当前目录下的所有.c文件:

.PHONY: all clean

CC := gcc
CFLAGS := -Wall -g
LDLIBS := -lrdmacm -libverbs -lpthread -g

SRCS := $(wildcard *.c)
APPS := $(SRCS:.c=)

all: $(APPS)

%: %.c
	$(CC) $(CFLAGS) $< -o $@ $(LDLIBS)

clean:
	rm -f $(APPS)

version1 客户端-服务端消息一次传递

在这个阶段,我们希望能实现下面这样一个场景。client与server端相连接,client端能够发送一条消息给server,server收到该条消息之后恢复一条消息给client端表示我已经确认收到。之后两者断开连接。

代码:

// client1.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <errno.h>
#include <getopt.h>
#include <rdma/rdma_cma.h>
#include <rdma/rdma_verbs.h>

static const char *server = "10.10.10.1";
static const char *port = "7471";

static struct rdma_cm_id *id;
static struct ibv_mr *mr, *send_mr;
static int send_flags;
static uint8_t send_msg[16];
static uint8_t recv_msg[16];

static int run(void)
{
	struct rdma_addrinfo hints, *res;
	struct ibv_qp_init_attr attr;
	struct ibv_wc wc;
	int ret;

	memset(&hints, 0, sizeof hints);
	hints.ai_port_space = RDMA_PS_TCP;
	ret = rdma_getaddrinfo(server, port, &hints, &res);
	if (ret) {
		printf("rdma_getaddrinfo: %s\n", gai_strerror(ret));
		goto out;
	}

	memset(&attr, 0, sizeof attr);
	attr.cap.max_send_wr = attr.cap.max_recv_wr = 1;
	attr.cap.max_send_sge = attr.cap.max_recv_sge = 1;
	attr.cap.max_inline_data = 16;
	attr.qp_context = id;
	attr.sq_sig_all = 1;
	ret = rdma_create_ep(&id, res, NULL, &attr);
	// Check to see if we got inline data allowed or not
	if (attr.cap.max_inline_data >= 16)
		send_flags = IBV_SEND_INLINE;
	else
		printf("rdma_client: device doesn't support IBV_SEND_INLINE, "
		       "using sge sends\n");

	if (ret) {
		perror("rdma_create_ep");
		goto out_free_addrinfo;
	}

	mr = rdma_reg_msgs(id, recv_msg, 16);
	if (!mr) {
		perror("rdma_reg_msgs for recv_msg");
		ret = -1;
		goto out_destroy_ep;
	}
	if ((send_flags & IBV_SEND_INLINE) == 0) {
		send_mr = rdma_reg_msgs(id, send_msg, 16);
		if (!send_mr) {
			perror("rdma_reg_msgs for send_msg");
			ret = -1;
			goto out_dereg_recv;
		}
	}

	ret = rdma_post_recv(id, NULL, recv_msg, 16, mr);
	if (ret) {
		perror("rdma_post_recv");
		goto out_dereg_send;
	}

	ret = rdma_connect(id, NULL);
	if (ret) {
		perror("rdma_connect");
		goto out_dereg_send;
	}


	printf("client send: %s\n", (char *)send_msg);
	ret = rdma_post_send(id, NULL, send_msg, 16, send_mr, send_flags);
	if (ret) {
		perror("rdma_post_send");
		goto out_disconnect;
	}

	while ((ret = rdma_get_send_comp(id, &wc)) == 0);
	if (ret < 0) {
		perror("rdma_get_send_comp");
		goto out_disconnect;
	}

	while ((ret = rdma_get_recv_comp(id, &wc)) == 0);
	if (ret < 0)
		perror("rdma_get_recv_comp");
	else
		ret = 0;
	
	printf("client received: %s\n", (char *) recv_msg);

out_disconnect:
	rdma_disconnect(id);
out_dereg_send:
	if ((send_flags & IBV_SEND_INLINE) == 0)
		rdma_dereg_mr(send_mr);
out_dereg_recv:
	rdma_dereg_mr(mr);
out_destroy_ep:
	rdma_destroy_ep(id);
out_free_addrinfo:
	rdma_freeaddrinfo(res);
out:
	return ret;
}

int main(int argc, char **argv)
{
	int ret;

	char *s = "hello world";
	// printf("client send: %s\n", s);
	memcpy(send_msg, s , strlen(s));

	printf("rdma_client: start\n");
	ret = run();
	printf("rdma_client: end %d\n", ret);
	return ret;
}

server端代码

// server1.c
/*
 * Copyright (c) 2005-2009 Intel Corporation.  All rights reserved.
 *
 * This software is available to you under the OpenIB.org BSD license
 * below:
 *
 *     Redistribution and use in source and binary forms, with or
 *     without modification, are permitted provided that the following
 *     conditions are met:
 *
 *      - Redistributions of source code must retain the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer.
 *
 *      - Redistributions in binary form must reproduce the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer in the documentation and/or other materials
 *        provided with the distribution.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AWV
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <getopt.h>
#include <netdb.h>
#include <rdma/rdma_cma.h>
#include <rdma/rdma_verbs.h>

static const char *server = "0.0.0.0";
static const char *port = "7471";

static struct rdma_cm_id *listen_id, *id;
static struct ibv_mr *mr, *send_mr;
static int send_flags;
static uint8_t send_msg[16];
static uint8_t recv_msg[16];

static int run(void)
{
	struct rdma_addrinfo hints, *res;
	struct ibv_qp_init_attr init_attr;
	struct ibv_qp_attr qp_attr;
	struct ibv_wc wc;
	int ret;

	memset(&hints, 0, sizeof hints);
	hints.ai_flags = RAI_PASSIVE;
	hints.ai_port_space = RDMA_PS_TCP;
	ret = rdma_getaddrinfo(server, port, &hints, &res);
	if (ret) {
		printf("rdma_getaddrinfo: %s\n", gai_strerror(ret));
		return ret;
	}

	memset(&init_attr, 0, sizeof init_attr);
	init_attr.cap.max_send_wr = init_attr.cap.max_recv_wr = 1;
	init_attr.cap.max_send_sge = init_attr.cap.max_recv_sge = 1;
	init_attr.cap.max_inline_data = 16;
	init_attr.sq_sig_all = 1;
	ret = rdma_create_ep(&listen_id, res, NULL, &init_attr);
	if (ret) {
		perror("rdma_create_ep");
		goto out_free_addrinfo;
	}

	ret = rdma_listen(listen_id, 0);
	if (ret) {
		perror("rdma_listen");
		goto out_destroy_listen_ep;
	}

	ret = rdma_get_request(listen_id, &id);
	if (ret) {
		perror("rdma_get_request");
		goto out_destroy_listen_ep;
	}

	memset(&qp_attr, 0, sizeof qp_attr);
	memset(&init_attr, 0, sizeof init_attr);
	ret = ibv_query_qp(id->qp, &qp_attr, IBV_QP_CAP,
			   &init_attr);
	if (ret) {
		perror("ibv_query_qp");
		goto out_destroy_accept_ep;
	}
	if (init_attr.cap.max_inline_data >= 16)
		send_flags = IBV_SEND_INLINE;
	else
		printf("rdma_server: device doesn't support IBV_SEND_INLINE, "
		       "using sge sends\n");

	mr = rdma_reg_msgs(id, recv_msg, 16);
	if (!mr) {
		ret = -1;
		perror("rdma_reg_msgs for recv_msg");
		goto out_destroy_accept_ep;
	}
	if ((send_flags & IBV_SEND_INLINE) == 0) {
		send_mr = rdma_reg_msgs(id, send_msg, 16);
		if (!send_mr) {
			ret = -1;
			perror("rdma_reg_msgs for send_msg");
			goto out_dereg_recv;
		}
	}

	ret = rdma_post_recv(id, NULL, recv_msg, 16, mr);
	if (ret) {
		perror("rdma_post_recv");
		goto out_dereg_send;
	}

	ret = rdma_accept(id, NULL);
	if (ret) {
		perror("rdma_accept");
		goto out_dereg_send;
	}

	while ((ret = rdma_get_recv_comp(id, &wc)) == 0);
	if (ret < 0) {
		perror("rdma_get_recv_comp");
		goto out_disconnect;
	}

    printf("server received: %s\n" , (char *)recv_msg);
    
    char *s = "ACK";
    memcpy(send_msg, s, strlen(s));
    printf("server send: %s\n", (char *)send_msg);

	ret = rdma_post_send(id, NULL, send_msg, 16, send_mr, send_flags);
	if (ret) {
		perror("rdma_post_send");
		goto out_disconnect;
	}

	while ((ret = rdma_get_send_comp(id, &wc)) == 0);
	if (ret < 0)
		perror("rdma_get_send_comp");
	else
		ret = 0;

out_disconnect:
	rdma_disconnect(id);
out_dereg_send:
	if ((send_flags & IBV_SEND_INLINE) == 0)
		rdma_dereg_mr(send_mr);
out_dereg_recv:
	rdma_dereg_mr(mr);
out_destroy_accept_ep:
	rdma_destroy_ep(id);
out_destroy_listen_ep:
	rdma_destroy_ep(listen_id);
out_free_addrinfo:
	rdma_freeaddrinfo(res);
	return ret;
}

int main(int argc, char **argv)
{
	int ret;

	printf("rdma_server: start\n");
	ret = run();
	printf("rdma_server: end %d\n", ret);
	return ret;
}

首先make编译完之后,在server端执行 ./server1,然后在客户端执行./client1
运行结果:
在这里插入图片描述
在这里插入图片描述
可以看到 client向server发送了hello world,server收到之后打印出来并回复给client端ACK消息,client收到之后并打印。最后双方断开连接,完成!

version2-客户端发送一次,服务端循环等待

client2的代码跟上面一样,server2代码不一样。
server2的逻辑:在run函数进来之后记录一个connect点,当远程客户端发送完信息后,释放连接的资源,跳转到connect阶段准备让下一个client连接。

/*
 * Copyright (c) 2005-2009 Intel Corporation.  All rights reserved.
 *
 * This software is available to you under the OpenIB.org BSD license
 * below:
 *
 *     Redistribution and use in source and binary forms, with or
 *     without modification, are permitted provided that the following
 *     conditions are met:
 *
 *      - Redistributions of source code must retain the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer.
 *
 *      - Redistributions in binary form must reproduce the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer in the documentation and/or other materials
 *        provided with the distribution.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AWV
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <getopt.h>
#include <netdb.h>
#include <rdma/rdma_cma.h>
#include <rdma/rdma_verbs.h>


#define N 100
#define MAX_CAP 32

static const char *server = "0.0.0.0";
static const char *port = "7471";

static struct rdma_cm_id *listen_id, *id;
static struct ibv_mr *mr, *send_mr;
static int send_flags;
static uint8_t send_msg[MAX_CAP];
static uint8_t recv_msg[MAX_CAP];

static int run(void)
{
	struct rdma_addrinfo hints, *res;
	struct ibv_qp_init_attr init_attr;
	struct ibv_qp_attr qp_attr;
	struct ibv_wc wc;
	int ret;

    while(1)
    {

        memset(&hints, 0, sizeof hints);
        hints.ai_flags = RAI_PASSIVE;
        hints.ai_port_space = RDMA_PS_TCP;
        ret = rdma_getaddrinfo(server, port, &hints, &res);
        if (ret) {
            printf("rdma_getaddrinfo: %s\n", gai_strerror(ret));
            return ret;
        }

        memset(&init_attr, 0, sizeof init_attr);
        init_attr.cap.max_send_wr = init_attr.cap.max_recv_wr = N;
        init_attr.cap.max_send_sge = init_attr.cap.max_recv_sge = 1;
        init_attr.cap.max_inline_data = MAX_CAP;
        init_attr.sq_sig_all = 1;

        ret = rdma_create_ep(&listen_id, res, NULL, &init_attr);
        if (ret) {
            perror("rdma_create_ep");
            goto out_free_addrinfo;
        }

        ret = rdma_listen(listen_id, 0);
        if (ret) {
            perror("rdma_listen");
            goto out_destroy_listen_ep;
        }

        ret = rdma_get_request(listen_id, &id);
        if (ret) {
            perror("rdma_get_request");
            goto out_destroy_listen_ep;
        }

        memset(&qp_attr, 0, sizeof qp_attr);
        memset(&init_attr, 0, sizeof init_attr);
        ret = ibv_query_qp(id->qp, &qp_attr, IBV_QP_CAP,
                &init_attr);
        if (ret) {
            perror("ibv_query_qp");
            goto out_destroy_accept_ep;
        }
        if (init_attr.cap.max_inline_data >= MAX_CAP)
            send_flags = IBV_SEND_INLINE;
        else
            printf("rdma_server: device doesn't support IBV_SEND_INLINE, "
                "using sge sends\n");
        mr = rdma_reg_msgs(id, recv_msg, N);
        if (!mr) {
            ret = -1;
            perror("rdma_reg_msgs for recv_msg");
            goto out_destroy_accept_ep;
        }
        if ((send_flags & IBV_SEND_INLINE) == 0) {
            send_mr = rdma_reg_msgs(id, send_msg, MAX_CAP);
            if (!send_mr) {
                ret = -1;
                perror("rdma_reg_msgs for send_msg");
                goto out_dereg_recv;
            }
        }   
        

        ret = rdma_accept(id, NULL);
        if (ret) {
            perror("rdma_accept");
            goto out_dereg_send;
        }

        
        memset(recv_msg, 0 , sizeof recv_msg);
        memset(send_msg, 0 , sizeof send_msg);
        ret = rdma_post_recv(id, NULL, recv_msg, MAX_CAP, mr);
        if (ret) {
            perror("rdma_post_recv");
            goto out_dereg_send;
        }
        while ((ret = rdma_get_recv_comp(id, &wc)) == 0);
        if (ret < 0) {
            perror("rdma_get_recv_comp");
            goto out_disconnect;
        }
        
        printf("server received: %s\n", (char *)recv_msg);
        memcpy(send_msg, recv_msg, sizeof(recv_msg));
        
        ret = rdma_post_send(id, NULL, send_msg, MAX_CAP, send_mr, send_flags);
        if (ret) {
            perror("rdma_post_send");
            goto out_disconnect;
        }
        while ((ret = rdma_get_send_comp(id, &wc)) == 0); // 确认对方已经收到 对方会发送ack
        if (ret < 0)
            perror("rdma_get_send_comp");
        else
            ret = 0;
        
        rdma_disconnect(id);
        if ((send_flags & IBV_SEND_INLINE) == 0)
            rdma_dereg_mr(send_mr);
        rdma_dereg_mr(mr);
        rdma_destroy_ep(id);
        rdma_destroy_ep(listen_id);
        rdma_freeaddrinfo(res);  
    }
         
    

out_disconnect:
    rdma_disconnect(id);
out_dereg_send:
    if ((send_flags & IBV_SEND_INLINE) == 0)
        rdma_dereg_mr(send_mr);
out_dereg_recv:
    rdma_dereg_mr(mr);
out_destroy_accept_ep:
    rdma_destroy_ep(id);
out_destroy_listen_ep:
    rdma_destroy_ep(listen_id);
out_free_addrinfo:
    rdma_freeaddrinfo(res);  

	return ret;
}

int main(int argc, char **argv)
{
	int ret;
	printf("rdma_server: start\n");
	ret = run();
	printf("rdma_server: end %d\n", ret);
	return ret;
}

运行结果:
在这里插入图片描述

在这里插入图片描述

可以看到客户端发送一次消息之后便结束了,服务端却一直等待连接,直到按下ctrl+c。

version3-客户端循环发送,服务端循环等待,一次连接

和上述版本2不同的时候,这里client和server只连接一次,然后可以多次发送消息。直到client发送的消息为disconnect

// client3.c
/*
 * Copyright (c) 2010 Intel Corporation.  All rights reserved.
 *
 * This software is available to you under the OpenIB.org BSD license
 * below:
 *
 *     Redistribution and use in source and binary forms, with or
 *     without modification, are permitted provided that the following
 *     conditions are met:
 *
 *      - Redistributions of source code must retain the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer.
 *
 *      - Redistributions in binary form must reproduce the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer in the documentation and/or other materials
 *        provided with the distribution.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AWV
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <errno.h>
#include <getopt.h>
#include <rdma/rdma_cma.h>
#include <rdma/rdma_verbs.h>

#define N 100
#define MAX_CAP 32

static const char *server = "10.10.10.1";
static const char *port = "7471";

static struct rdma_cm_id *id;
static struct ibv_mr *mr, *send_mr;
static int send_flags;
static uint8_t send_msg[MAX_CAP];
static uint8_t recv_msg[MAX_CAP];

static int run(void)
{
	struct rdma_addrinfo hints, *res;
	struct ibv_qp_init_attr attr;
	struct ibv_wc wc;
	int ret;

	memset(&hints, 0, sizeof hints);
	hints.ai_port_space = RDMA_PS_TCP;
	ret = rdma_getaddrinfo(server, port, &hints, &res);
	if (ret) {
		printf("rdma_getaddrinfo: %s\n", gai_strerror(ret));
		goto out;
	}
    memset(&attr, 0, sizeof attr);
    attr.cap.max_send_wr = attr.cap.max_recv_wr = 5;
    attr.cap.max_send_sge = attr.cap.max_recv_sge = 1;
    attr.cap.max_inline_data = MAX_CAP;
    attr.qp_context = id;
    attr.sq_sig_all = 1;
    ret = rdma_create_ep(&id, res, NULL, &attr);
    // Check to see if we got inline data allowed or not
    if (attr.cap.max_inline_data >= MAX_CAP)
        send_flags = IBV_SEND_INLINE;
    else
        printf("rdma_client: device doesn't support IBV_SEND_INLINE, "
            "using sge sends\n");

    if (ret) {
        perror("rdma_create_ep");
        goto out_free_addrinfo;
    }

    mr = rdma_reg_msgs(id, recv_msg, MAX_CAP);
    if (!mr) {
        perror("rdma_reg_msgs for recv_msg");
        ret = -1;
        goto out_destroy_ep;
    }
    if ((send_flags & IBV_SEND_INLINE) == 0) {
        send_mr = rdma_reg_msgs(id, send_msg, MAX_CAP);
        if (!send_mr) {
            perror("rdma_reg_msgs for send_msg");
            ret = -1;
            goto out_dereg_recv;
        }
    }

    // ret = rdma_post_recv(id, NULL, recv_msg, 16, mr);
    // if (ret) {
    //     perror("rdma_post_recv");
    //     goto out_dereg_send;
    // }

    // printf("123\n");

    ret = rdma_connect(id, NULL);
    if (ret) {
        perror("rdma_connect");
        goto out_dereg_send;
    }

    while(1)
    {
        // sleep(5);
        memset(recv_msg, 0 , sizeof recv_msg);
        memset(send_msg, 0 , sizeof send_msg);
        printf("input send message: ");
        scanf("%s", send_msg);
        getchar();
        
        ret = rdma_post_recv(id, NULL, recv_msg, MAX_CAP, mr);
        if (ret) {
            perror("rdma_post_recv");
            goto out_dereg_send;
        }

        ret = rdma_post_send(id, NULL, send_msg, MAX_CAP, send_mr, send_flags);
        if (ret) {
            perror("rdma_post_send");
            goto out_disconnect;
        }

        while ((ret = rdma_get_send_comp(id, &wc)) == 0);
        if (ret < 0) {
            perror("rdma_get_send_comp");
            goto out_disconnect;
        }

        while ((ret = rdma_get_recv_comp(id, &wc)) == 0);
        if (ret < 0)
            perror("rdma_get_recv_comp");
        else
            ret = 0;
        
        if(strcmp((char*)send_msg,"disconnect") == 0)
        {
            printf("disconnect\n");
            goto out_disconnect;
        }
        else
        {
            printf("%s\n", recv_msg);
        }
            
    }

out_disconnect:
    rdma_disconnect(id);
out_dereg_send:
    if ((send_flags & IBV_SEND_INLINE) == 0)
            rdma_dereg_mr(send_mr);
out_dereg_recv:
    rdma_dereg_mr(mr);
out_destroy_ep:
    rdma_destroy_ep(id);
out_free_addrinfo:
    rdma_freeaddrinfo(res);
out:
    return ret;

}



int main(int argc, char **argv)
{
	int ret;
    //memcpy(send_msg, argv[1], 50);
	// while ((op = getopt(argc, argv, "s:p:")) != -1) {
	// 	switch (op) {
	// 	case 's':
	// 		server = optarg;
	// 		break;
	// 	case 'p':
	// 		port = optarg;
	// 		break;
	// 	default:
	// 		printf("usage: %s\n", argv[0]);
	// 		printf("\t[-s server_address]\n");
	// 		printf("\t[-p port_number]\n");
	// 		exit(1);
	// 	}
	// }

	printf("rdma_client: start\n");
	ret = run();
	printf("rdma_client: end %d\n", ret);
    
	return ret;
}
// server3.c
/*
 * Copyright (c) 2005-2009 Intel Corporation.  All rights reserved.
 *
 * This software is available to you under the OpenIB.org BSD license
 * below:
 *
 *     Redistribution and use in source and binary forms, with or
 *     without modification, are permitted provided that the following
 *     conditions are met:
 *
 *      - Redistributions of source code must retain the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer.
 *
 *      - Redistributions in binary form must reproduce the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer in the documentation and/or other materials
 *        provided with the distribution.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AWV
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <getopt.h>
#include <netdb.h>
#include <rdma/rdma_cma.h>
#include <rdma/rdma_verbs.h>


#define N 100
#define MAX_CAP 32

static const char *server = "0.0.0.0";
static const char *port = "7471";

static struct rdma_cm_id *listen_id, *id;
static struct ibv_mr *mr, *send_mr;
static int send_flags;
static uint8_t send_msg[MAX_CAP];
static uint8_t recv_msg[MAX_CAP];

static int run(void)
{
	struct rdma_addrinfo hints, *res;
	struct ibv_qp_init_attr init_attr;
	struct ibv_qp_attr qp_attr;
	struct ibv_wc wc;
	int ret;

connect:
	memset(&hints, 0, sizeof hints);
	hints.ai_flags = RAI_PASSIVE;
	hints.ai_port_space = RDMA_PS_TCP;
	ret = rdma_getaddrinfo(server, port, &hints, &res);
	if (ret) {
		printf("rdma_getaddrinfo: %s\n", gai_strerror(ret));
		return ret;
	}

	memset(&init_attr, 0, sizeof init_attr);
	init_attr.cap.max_send_wr = init_attr.cap.max_recv_wr = N;
	init_attr.cap.max_send_sge = init_attr.cap.max_recv_sge = 1;
	init_attr.cap.max_inline_data = MAX_CAP;
	init_attr.sq_sig_all = 1;

	ret = rdma_create_ep(&listen_id, res, NULL, &init_attr);
	if (ret) {
		perror("rdma_create_ep");
		goto out_free_addrinfo;
	}

	ret = rdma_listen(listen_id, 0);
	if (ret) {
		perror("rdma_listen");
		goto out_destroy_listen_ep;
	}

    ret = rdma_get_request(listen_id, &id);
    if (ret) {
        perror("rdma_get_request");
        goto out_destroy_listen_ep;
    }

    memset(&qp_attr, 0, sizeof qp_attr);
    memset(&init_attr, 0, sizeof init_attr);
    ret = ibv_query_qp(id->qp, &qp_attr, IBV_QP_CAP,
            &init_attr);
    if (ret) {
        perror("ibv_query_qp");
        goto out_destroy_accept_ep;
    }
    if (init_attr.cap.max_inline_data >= MAX_CAP)
        send_flags = IBV_SEND_INLINE;
    else
        printf("rdma_server: device doesn't support IBV_SEND_INLINE, "
            "using sge sends\n");
    mr = rdma_reg_msgs(id, recv_msg, N);
    if (!mr) {
        ret = -1;
        perror("rdma_reg_msgs for recv_msg");
        goto out_destroy_accept_ep;
    }
    if ((send_flags & IBV_SEND_INLINE) == 0) {
        send_mr = rdma_reg_msgs(id, send_msg, MAX_CAP);
        if (!send_mr) {
            ret = -1;
            perror("rdma_reg_msgs for send_msg");
            goto out_dereg_recv;
        }
    }   
    

    ret = rdma_accept(id, NULL);
    if (ret) {
        perror("rdma_accept");
        goto out_dereg_send;
    }

    while (1) {
        memset(recv_msg, 0 , sizeof recv_msg);
        memset(send_msg, 0 , sizeof send_msg);
        ret = rdma_post_recv(id, NULL, recv_msg, MAX_CAP, mr);
        if (ret) {
            perror("rdma_post_recv");
            goto out_dereg_send;
        }
        while ((ret = rdma_get_recv_comp(id, &wc)) == 0);
        if (ret < 0) {
            perror("rdma_get_recv_comp");
            goto out_disconnect;
        }
        
        char *s = (char *)recv_msg;
        int total_length = strlen("server get ") + strlen(s); // 加1是为了存储字符串结束符'\0'
        char *recv_str = (char *)malloc(total_length);  // 分配足够的空间
        strcpy(recv_str, "server get ");
        strcat(recv_str, s);
        //printf("%s\n", recv_str);
        memcpy(send_msg, recv_str, strlen(recv_str));
        
        ret = rdma_post_send(id, NULL, send_msg, MAX_CAP, send_mr, send_flags);
        if (ret) {
            perror("rdma_post_send");
            goto out_disconnect;
        }

        if(strcmp((char*)recv_msg,"disconnect") == 0)
        {
            //printf("%s\n",recv_msg);
            printf("client disconnect\n");
            rdma_disconnect(id);
            if ((send_flags & IBV_SEND_INLINE) == 0)
                rdma_dereg_mr(send_mr);
            rdma_dereg_mr(mr);
            rdma_destroy_ep(id);
            rdma_destroy_ep(listen_id);
            rdma_freeaddrinfo(res);  
            //goto out_disconnect;
            goto connect;
            
        }
        else
        {
            printf("%s\n", recv_msg);
        }
        
        // while ((ret = rdma_get_send_comp(id, &wc)) == 0); // 确认对方已经收到 对方发送ack
        // printf("after send\n");
        // if (ret < 0)
        //     perror("rdma_get_send_comp");
        // else
		//     ret = 0;
         
    }

out_disconnect:
    rdma_disconnect(id);
out_dereg_send:
    if ((send_flags & IBV_SEND_INLINE) == 0)
        rdma_dereg_mr(send_mr);
out_dereg_recv:
    rdma_dereg_mr(mr);
out_destroy_accept_ep:
    rdma_destroy_ep(id);
out_destroy_listen_ep:
    rdma_destroy_ep(listen_id);
out_free_addrinfo:
    rdma_freeaddrinfo(res);  

	return ret;
}

int main(int argc, char **argv)
{
	int op, ret;

	while ((op = getopt(argc, argv, "s:p:")) != -1) {
		switch (op) {
		case 's':
			server = optarg;
			break;
		case 'p':
			port = optarg;
			break;
		default:
			printf("usage: %s\n", argv[0]);
			printf("\t[-s server_address]\n");
			printf("\t[-p port_number]\n");
			exit(1);
		}
	}

	printf("rdma_server: start\n");
	ret = run();
	printf("rdma_server: end %d\n", ret);
	return ret;
}

运行结果:

在这里插入图片描述
在这里插入图片描述

总结:

本文实现了rdma中send-receive双边原语的三种需求版本,从单次发送到两者都能多次发送。理解其中的代码逻辑,想要发送消息之前对端得创建一个recv队列用来接收消息。发送完了有一个发送完成队列,接收完了也有一个接收完成队列。最后双方断开连接需要一起断开,不能某一方执行disconnect另一方不执行。本次实验有一个关键点:

while ((ret = rdma_get_send_comp(id, &wc)) == 0)

这一行代码是等待发送成功,发送成功之后,对方会给一个隐式信息表示我已经收到。这里耗费得时间比较长一点,在版本3中,如果不注释掉,在server端的receive队列还没有建立好,这就导致client发送了消息,server还没有收到,双方就陷入了死循环中。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/324164.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数据库和表的操作

文章目录 前言一、库的操作创建数据库字符集和校验规则操纵数据库查看数据库显示创建语句修改数据库删除数据库备份和恢复数据库还原查看连接情况 二、表的操作创建表查看表结构修改表修改表名添加一列修改某一列属性删除某一列 删除表 前言 一、库的操作 创建数据库 语法&am…

新手必看:腾讯云服务器购买详细图文教程

腾讯云服务器购买流程很简单&#xff0c;有两种购买方式&#xff0c;直接在官方活动上购买比较划算&#xff0c;在云服务器CVM或轻量应用服务器页面自定义购买价格比较贵&#xff0c;但是自定义购买云服务器CPU内存带宽配置选择范围广&#xff0c;活动上购买只能选择固定的活动…

我的NPI项目之设备系统启动(三) -- CDT的一个实例

上面说了这么多&#xff0c;这里就添加一个CDT的使用实例和简单的代码解析。 首先生成cdt_configure.xml配置文件&#xff0c;然后执行如下命令&#xff1a; python cdt_generator.py cdt_configure.xml CDT.bin; 就可以生成对应的CDT.bin文件。同时也会生成, 我们会利用ha…

STM32串口485通信如何控制收发管脚

要有效的控制485的收发管脚&#xff0c;首先要知道485通信发送命令完成的时间&#xff0c;我们执行发送命令结束并不代表硬件已经把数据发出去了&#xff0c;可能1ms以后才真正完成&#xff0c;如果我们控制管脚不当&#xff0c;可能导致数据不能完全发出去。导致串口通信异常。…

Leetcode20-唯一摩尔斯密码词(804)

1、题目 国际摩尔斯密码定义一种标准编码方式&#xff0c;将每个字母对应于一个由一系列点和短线组成的字符串&#xff0c; 比如: ‘a’ 对应 “.-” &#xff0c; ‘b’ 对应 “-…” &#xff0c; ‘c’ 对应 “-.-.” &#xff0c;以此类推。 为了方便&#xff0c;所有 26…

深度学习记录--Train/dev/test sets

为什么需要训练集、验证集(简单交叉验证集)和测试集&#xff1f; 为了创建高效的神经网络&#xff0c;需要不断进行训练(迭代) 一个神经网络的产生 从最开始的想法idea开始&#xff0c;然后付诸于代码code&#xff0c;根据结果验证反过来对一开始的想法idea进行修正&#xf…

LeetCode 429. N 叉树的层序遍历

429. N 叉树的层序遍历 给定一个 N 叉树&#xff0c;返回其节点值的层序遍历。&#xff08;即从左到右&#xff0c;逐层遍历&#xff09;。 树的序列化输入是用层序遍历&#xff0c;每组子节点都由 null 值分隔&#xff08;参见示例&#xff09;。 示例 1&#xff1a; 输入&…

商业世界,从2023到2024

作者&#xff5c;潮汐商业评论 编辑&#xff5c;Ray 变化总在发生&#xff0c;你不去迎接进步的变化&#xff0c;就会等到退步的变化。 —— 查理.芒格 2023, 我们似乎总在不断告别。从“一生自由”的大家黄永玉到“智慧”投资家查理.芒格&#xff0c;再到写出《不能承受的生命…

利用fd子系统实现图案与图片显示方法

//第一&#xff1a;利用fb子系统画圆的方法与实现 //1、头文件信息 #include <sys/ioctl.h> #include <linux/fb.h> #include <stdio.h> #include <sys/types.h> #include <stdio.h> #include <sys/mman.h> #include <sys/types.h>…

系分备考计算机网络传输介质、通信方式和交换方式

文章目录 1、概述2、传输介质3、网络通信4、网络交换5、总结 1、概述 计算机网路是系统分析师考试的常考知识点&#xff0c;本篇主要记录了知识点&#xff1a;网络传输介质、网络通信和数据交换方式等。 2、传输介质 网络的传输最常见的就是网线&#xff0c;也就是双绞线&…

OpenGL Assimp加载各类型模型(.obj、.fbx、.glb、.3ds)

1.简介 本博客以.glb格式为例&#xff0c;加载glb格式的3d模型&#xff0c;网上找了一圈&#xff0c;基本上都是根据OpenGL官方示例&#xff0c;加载.obj格式的3d模型。 下面以.obj和.glb格式的3D模型简单介绍一下。 常见的.obj格式的3D模型如下所示&#xff1a;纹理都已经被…

QT 文本框的绘制与复选框组键

.cpp文件 #include "widget.h" #include "ui_widget.h"#include<QDebug> Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);//更改窗口标题setWindowTitle("我爱xyy");//更改图标setWindow…

OpenCV-Python(32):SIFT算法

目标 学习SIFT 算法的概念学习在图像中查找SIFT关键点和描述符 原理及介绍 在前面两节我们学习了一些角点检测技术&#xff1a;比如Harris 等。它们具有旋转不变特性&#xff0c;即使图片发生了旋转&#xff0c;我们也能找到同样的角点。很明显即使图像发生旋转之后角点还是角…

2024腾讯云服务器购买指南一步步全流程攻略(超详细)

腾讯云服务器购买流程很简单&#xff0c;有两种购买方式&#xff0c;直接在官方活动上购买比较划算&#xff0c;在云服务器CVM或轻量应用服务器页面自定义购买价格比较贵&#xff0c;但是自定义购买云服务器CPU内存带宽配置选择范围广&#xff0c;活动上购买只能选择固定的活动…

基于Java (spring-boot)的停车场管理系统

一、项目介绍 基于Java (spring-boot)的停车场管理系统、预订车位系统、停车缴费系统功能&#xff1a; 登录、注册、后台首页、用户信息管理、车辆信息管理、新增车辆、车位费用设置、停泊车辆查询、车辆进出管理、登录日志查询、个人中心、预定停车位、缴费信息。 适用人群&…

CSS实现超出部分的省略

1、为什么要省略 在日常开发过程中我们难免会遇到后端返回给我们的的数据太长的情况&#xff0c;此时我们通常采取的是...的省略方式&#xff0c;其中的CSS大致如下&#xff0c;既可以实现对应的省略显示&#xff0c;但有些时候我们有需要用户可以查看具体的完整信息&#xff0…

安卓开发-02-基础

文章目录 一、基本UI组件文本类组件TextViewEditText 按钮类组件普通按钮为普通按钮添加的单击事件监听器 图片按钮单选按钮复选框&#xff08;进行多选&#xff09; 日期时间类组件日期选择器时间选择器计时器 二、高级UI组件进度条组件拖动条组件星级评分条图像类组件图像切换…

面试官常问问题:Java中的128陷阱详解

看这样两段代码&#xff0c;思考结果返回的是什么 Integer num1 100; Integer num2 100; System.out.println(num1 num2);Integer num3 128; Integer num4 128; System.out.println(num3 num4); 揭晓答案&#xff1a;第一段代码的结果是true&#xff0c;第二段代码的结…

城市信息模型平台顶层设计与实践-CIM-读书笔记

城市信息模型平台顶层设计与实践-CIM-读书笔记 1、地理空间框架 GB/T 30317—2013《地理空间框架基本规定》规定地理空间框架为&#xff1a;“地理信息数据及其采集、加工、交换、服务所涉及的政策、法规、标准、技术、设施、机制和人力资源的总称&#xff0c;由基础地理信息…

Graphics2D - 生成证书图片

需求&#xff1a;用户领取证书功能。起初做法是将证书内容保存至富文本编辑器&#xff0c;再将富文本内容返回&#xff0c;由前端组成证书页面。但是前端因为屏幕尺寸、拉伸等问题&#xff0c;导致展示的效果不尽人意&#xff0c;无法满足业务要求。所以选择了使用Graphics2D在…