RDMA技术零:windows+Socket编程

1.windows10环境下进行socket编程

通过socket编程的代码,分析用户空间、内核空间和硬件在网络编程中的特点,引入 RDMA编程。

2.代码

2.1 服务端

// #include "pch.h"
#include<iostream>
#include<winsock.h>

#pragma comment(lib,"ws2_32.lib")

using namespace std;
void initialization();

int main() 
{
	//定义长度变量
	int send_len = 0;
	int recv_len = 0;
	int len = 0;
	//定义发送缓冲区和接受缓冲区
	char send_buf[100];
	char recv_buf[100];
	//定义服务端套接字,接受请求套接字
	SOCKET s_server;
	SOCKET s_accept;
	//服务端地址客户端地址
	SOCKADDR_IN server_addr;
	SOCKADDR_IN accept_addr;
	initialization();
	//填充服务端信息
	server_addr.sin_family = AF_INET;
	//server_addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
	//server_addr.sin_port = htons(5010);
	server_addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	server_addr.sin_port = htons(1234);
	//创建套接字
	s_server = socket(AF_INET, SOCK_STREAM, 0);
	if (bind(s_server, (SOCKADDR *)&server_addr, sizeof(SOCKADDR)) == SOCKET_ERROR) {
		cout << "套接字绑定失败!" << endl;
		WSACleanup();
	}
	else {
		cout << "套接字绑定成功!" << endl;
	}
	//设置套接字为监听状态
	if (listen(s_server, SOMAXCONN) < 0) {
		cout << "设置监听状态失败!" << endl;
		WSACleanup();
	}
	else {
		cout << "设置监听状态成功!" << endl;
	}
	cout << "服务端正在监听连接,请稍候...." << endl;
	//接受连接请求
	len = sizeof(SOCKADDR);
	s_accept = accept(s_server, (SOCKADDR *)&accept_addr, &len);
	if (s_accept == SOCKET_ERROR) {
		cout << "连接失败!" << endl;
		WSACleanup();
		return 0;
	}
	cout << "连接建立,准备接受数据" << endl;
	//接收数据
	while (1) {
		recv_len = recv(s_accept, recv_buf, 100, 0);
		if (recv_len < 0) {
			cout << "接受失败!" << endl;
			break;
		}
		else {
			cout << "客户端信息:" << recv_buf << endl;
		}
		cout << "请输入回复信息:";
		cin >> send_buf;
		send_len = send(s_accept, send_buf, 100, 0);
		if (send_len < 0) {
			cout << "发送失败!" << endl;
			break;
		}
	}
	//关闭套接字
	closesocket(s_server);
	closesocket(s_accept);
	//释放DLL资源
	WSACleanup();
	return 0;
}

void initialization() 
{
	//初始化套接字库
	WORD w_req = MAKEWORD(2, 2);//版本号
	WSADATA wsadata;
	int err;
	err = WSAStartup(w_req, &wsadata);
	if (err != 0) {
		cout << "初始化套接字库失败!" << endl;
	}
	else {
		cout << "初始化套接字库成功!" << endl;
	}
	//检测版本号
	if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wHighVersion) != 2) {
		cout << "套接字库版本号不符!" << endl;
		WSACleanup();
	}
	else {
		cout << "套接字库版本正确!" << endl;
	}
}
	//填充服务端地址信息


2.2 客户端

// #include "pch.h"
#include<iostream>
#include<winsock.h>

#pragma comment(lib,"ws2_32.lib")

using namespace std;
void initialization();

int main() 
{
	//定义长度变量
	int send_len = 0;
	int recv_len = 0;
	//定义发送缓冲区和接受缓冲区
	char send_buf[100];
	char recv_buf[100];
	//定义客户端套接字,接受请求套接字
	SOCKET s_client;
	//客户端地址客户端地址
	SOCKADDR_IN server_addr;
	initialization();
	//填充客户端信息
	server_addr.sin_family = AF_INET;
	server_addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	server_addr.sin_port = htons(1234);
	//创建套接字
	s_client = socket(AF_INET, SOCK_STREAM, 0);
	if (connect(s_client, (SOCKADDR *)&server_addr, sizeof(SOCKADDR)) == SOCKET_ERROR) {
		cout << "服务器连接失败!" << endl;
		WSACleanup();
	}
	else {
		cout << "服务器连接成功!" << endl;
	}

	//发送数据
	while (1) {
		cout << "请输入发送信息:";
		cin >> send_buf;
		send_len = send(s_client, send_buf, 100, 0);
		if (send_len < 0) {
			cout << "发送失败!" << endl;
			break;
		}
		recv_len = recv(s_server, recv_buf, 100, 0);
		if (recv_len < 0) {
			cout << "接受失败!" << endl;
			break;
		}
		else {
			cout << "服务端信息:" << recv_buf << endl;
		}

	}
	//关闭套接字
	closesocket(s_client);
	//释放DLL资源
	WSACleanup();
	return 0;
}

void initialization() {
	//初始化套接字库
	WORD w_req = MAKEWORD(2, 2);//版本号
	WSADATA wsadata;
	int err;
	err = WSAStartup(w_req, &wsadata);
	if (err != 0) {
		cout << "初始化套接字库失败!" << endl;
	}
	else {
		cout << "初始化套接字库成功!" << endl;
	}
	//检测版本号
	if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wHighVersion) != 2) {
		cout << "套接字库版本号不符!" << endl;
		WSACleanup();
	}
	else {
		cout << "套接字库版本正确!" << endl;
	}
}
	//填充客户端地址信息

2.3 代码说明

不管是客户端还是接收端,基本的过程是打开网络编程,创建socket,建立连接,发送(客户端)/接受(服务端)数据,关闭socket,清理资源。

3. 整个流程

整个过程中,用户空间app只需要负责调用内核空间的接口,不需要关心内核层、网卡等其他信息。

分析下整个底层逻辑:

上图,两台服务器上的应用之间传输数据,过程是这样的:

首先要把数据从应用缓存拷贝到Kernel中的TCP协议栈缓存;然后再拷贝到驱动层;最后拷贝到网卡缓存。
        数据发送方需要讲数据从用户空间 Buffer 复制到内核空间的 Socket Buffer。
        数据发送方要在内核空间中添加数据包头,进行数据封装
        数据从内核空间的 Socket Buffer 复制到 NIC Buffer 进行网络传输
        数据接受方接收到从远程机器发送的数据包后,要将数据包从 NIC Buffer 中复制到内核空间的 Socket Buffer
        经过一系列的多层网络协议进行数据包的解析工作,解析后的数据从内核空间的 Socket Buffer 被复制到用户空间 Buffer

3.1 特点一

需要在用户空间和内核层中来回复制数据

发送时,用户空间数据拷贝到内核空间;接收时,内核空间数据拷贝到用户空间。

例如:上述客户端中的代码send()接口中,功能就是将数据发送出去,涉及到用户空间到内核层的数据拷贝,底层的实现就是memcpy();

3.2 特点二

cpu参与大量数据的封装和解析,影响cpu资源的调度。

根据内核中的各种协议,cpu会对socket中的数据进行重新包装,方便数据能正确到达客户端,如封装(发送)和解析(接收)报文头,校验信息等,cpu不能处理其他的事情。

3.3 特点3

内核频繁操作,会影响系统调用和上下文切换的开销。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值