C++实现客户端/服务端通信(一)

news2024/9/28 17:30:03

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

C++实现客户端/服务端通信(一)

  • 网络编程的基本概念
    • 1. 客户端/服务器通信模型:
    • 2. socket函数:
    • 3. 主机字节序和网络字节序:
    • 4. sockaddr 结构体
    • 5. sockaddr_in 结构体
    • 6. 字符串IP与大端序IP的转换
  • 实现简单的客户端
  • 实现简单的服务端
  • 总结


网络编程的基本概念

1. 客户端/服务器通信模型:

在这里插入图片描述

2. socket函数:

int socket(int domain, int type, int protocol);
1)domain:通讯协议族
PF_INET: IPV4互联网协议族
PF_INET6: IPV6互联网协议族
PF_LOCAL: 本地通信协议族
PF_PACKET: 内核底层的协议族
PF_IPX: IPX Novell协议族
2)type:数据传输类型
SOCK_STREAM: 面向连接的socket
SOCK_DGRAM: 无连接的socket
3)protocol:最终使用的协议
在IPV4互联网协议族中,传输类型为SOCK_STREAM的协议只有IPPROTO_TCP,数据传输方式为SOCK_DGRAM的协议类型只有IPPROTO_UDP
可以填0(编译器自动识别)

3. 主机字节序和网络字节序:

多个字节组成的整数存放涉及到字节序。
大端序:低位字节存在高位,高位字节存在低位(如0X123456内存中存放为12 34 56)
小端序:低位字节存在低位,高位字节存在高位(如0X123456内存中存放为56 34 12),如Intel

为了解决不同字节序的计算机之间传输数据的问题,约定采用网络字节序(大端序)
主机字节序与网络字节序之间的转换:
uint16_t htons(uint16_t hostshort);
uint32_t htonl(uint32_t hostlong);
uint16_t ntohs(uint16_t netshort);
uint32_t ntohl(uint32_t netlong);

4. sockaddr 结构体

存放协议族、端口和地址信息

struct sockaddr {
    unsigned short sa_family;  // 协议族
    unsigned char sa_data[14]; // 14字节的端口和地址
};

5. sockaddr_in 结构体

sockaddr结构体为了统一地址结构的表示方法,统一接口函数,但是操作不方便,所以定义了等价的sockaddr_in结构体,其大小与sockaddr相同,可以强制转换为sockaddr

struct in_addr {
    unsigned int s_addr;       // IP地址,大端序
};

struct sockaddr_in {
    unsigned short sa_family;  // 协议族
    unsigned short sin_port;   // 端口号,大端序
    struct in_addr sin_addr;   // IP地址,32位,只适用于IPV4
    unsigned char sin_zero[8]; // 未使用,为了与sockaddr大小相同
};

之所以搞两个结构体,可能是因为sockaddr可以用于IPV4,后续也可以用于IPV6,sockaddr_in是为了IPV4操作方便(根据sin_addr存放的位数看)。

6. 字符串IP与大端序IP的转换

typedef unsigned int in_addr_t;  // 大端序IP地址

// 将字符串格式的IP转换为大端序IP
in_addr_t inet_addr(const char* cp);
int inet_aton(const char* cp, struct in_addr* inp);

// 将大端序IP转换为字符串格式IP
char *inet_ntoa(struct in_addr in);

实现简单的客户端

功能需求:能够与服务端建立连接,并发送、接收三次信息

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <unistd.h>
#include <arpa/inet.h>
#include <netdb.h>

using namespace std;

int main(int argc, char** argv)
{
	if (argc < 3) {
		cout << "Error: you should enter server ip and port" << endl;
		cout << "Usage: ./djclient [IP ADDR] [PORT]" << endl;
		return 0;
	}

	int client_socket = socket(AF_INET, SOCK_STREAM, 0);

	struct sockaddr_in server_addr;
	memset(&server_addr, 0, sizeof(server_addr));
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(atoi(argv[2]));
	struct hostent* server_ip = gethostbyname(argv[1]);   // gethostname支持域名、主机名、字符串
	if (server_ip == nullptr) {
		cout << "gethostbyname failed " << argv[1] << endl;
		close(client_socket);
		return -1;
	}
	memcpy(&server_addr.sin_addr, server_ip->h_addr, server_ip->h_length);

	int ret = connect(client_socket, (struct sockaddr*)&server_addr, sizeof(server_addr));
	if (ret == -1) {
		cout << "connect failed." << endl;
		close(client_socket);
		return -1;
	}

	char buf[1024] = {0};

	// 接受发送消息3次
	for (int i = 0; i < 3; i++) {
		memset(buf, 0, sizeof(buf));
		sprintf(buf, "这是发送的第%d个消息.", i);
		ret = write(client_socket, buf, strlen(buf));
		if (ret == -1) {
			cout << "ERROR: 第 " << i << " 次发送信息失败" << endl;
			break;
		}
		cout << "INFO: 第 " << i << " 次发送了 " << ret << " 个字节,内容为:" << buf << endl;

		memset(buf, 0, sizeof(buf));
		ret = read(client_socket, buf, sizeof(buf) - 1);
		if (ret == -1) {
			cout << "ERROR: 第 " << i << " 次接收信息失败" << endl;
			break;
		}
        
        if (ret == 0) {
 
        }
		cout << "INFO: 第 " << i << " 次接收了 " << ret << " 个字节,内容为:" << buf << endl;
	}

	close(client_socket);

	cout << "通信结束!!!" << endl;
	return 0;
}

实现简单的服务端

功能需求:实现简单的服务端,接收客户端连接,打印接收和连接信息

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <cstring>
#include <unistd.h>
#include <arpa/inet.h>

using namespace std;

int main(int argc, char** argv)
{
	int server_socket = socket(AF_INET, SOCK_STREAM, 0);
	
	struct sockaddr_in server_addr;
	memset(&server_addr, 0, sizeof(server_addr));

	server_addr.sin_family = AF_INET;
	server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	server_addr.sin_port = htons(3560);

	int ret = bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr));
	if (ret != 0) {
		cout << "ERROR: bind() 执行失败!!!" << endl;
		close(server_socket);
		return -1;
	}

	ret = listen(server_socket, 5);
	if (ret != 0) {
		cout << "ERROR: listen() 执行失败!!!" << endl;
		close(server_socket);
		return -1;
	}

	// int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen); 
	struct sockaddr_in cliaddr;
	memset(&cliaddr, 0, sizeof(cliaddr));
	socklen_t addrlen = sizeof(cliaddr);

	cout << "等待连接..." << endl;
	int client_socket = accept(server_socket, (struct sockaddr*)&cliaddr, &addrlen);

	char* client_ip = inet_ntoa(cliaddr.sin_addr);

	cout << "INFO: 来自客户端的连接为:" << client_ip << ":"
		 << ntohs(cliaddr.sin_port) << endl;


	char buf[1024] = {0};
	int i = 0;
	while (true) {
		memset(buf, 0, sizeof(buf));
		ret = read(client_socket, buf, sizeof(buf) - 1);
		if (ret == -1) {
			cout << "ERROR: 第 " << i << " 次接收信息失败" << endl;
			break;
		}

		if (ret == 0) {
			cout << "INFO: 客户端断开连接" << endl;
			break;
		}
		cout << "INFO: 第 " << i << " 次接收了 " << ret << " 个字节,内容为:" << buf << endl;

		strcpy(buf, "success!!!");
		ret = write(client_socket, buf, strlen(buf));
		if (ret == -1) {
			cout << "ERROR: 第 " << i << " 次发送信息失败" << endl;
			break;
		}
		cout << "INFO: 第 " << i << " 次发送了 " << ret << " 个字节,内容为:" << buf << endl;
		
		i++;
	}

	close(client_socket);
	close(server_socket);
	return 0;
}

执行结果如下所示:

# 客户端
[root@localhost code]# g++ djclient.cpp -o djclient
[root@localhost code]# ./djclient 
Error: you should enter server ip and port
Usage: ./djclient [IP ADDR] [PORT]
[root@localhost code]# ./djclient 192.168.66.124 3560
INFO: 第 0 次发送了 29 个字节,内容为:这是发送的第0个消息.
INFO: 第 0 次接收了 10 个字节,内容为:success!!!
INFO: 第 1 次发送了 29 个字节,内容为:这是发送的第1个消息.
INFO: 第 1 次接收了 10 个字节,内容为:success!!!
INFO: 第 2 次发送了 29 个字节,内容为:这是发送的第2个消息.
INFO: 第 2 次接收了 10 个字节,内容为:success!!!
通信结束!!!
[root@localhost code]# 

# 服务端
[root@centos server]# g++ djserver.cpp -o djserver
[root@centos server]# ./djserver 
等待连接...
INFO: 来自客户端的连接为:192.168.91.153:14098
INFO: 第 0 次接收了 29 个字节,内容为:这是发送的第0个消息.
INFO: 第 0 次发送了 10 个字节,内容为:success!!!
INFO: 第 1 次接收了 29 个字节,内容为:这是发送的第1个消息.
INFO: 第 1 次发送了 10 个字节,内容为:success!!!
INFO: 第 2 次接收了 29 个字节,内容为:这是发送的第2个消息.
INFO: 第 2 次发送了 10 个字节,内容为:success!!!
INFO: 客户端断开连接
[root@centos server]# 

总结

C++实现客户端/服务端通信(一)基于socket通信的基本API,实现了客户端/服务端的基本通信框架。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Docker容器学习:搭建自己专属的LAMP环境

目录 编写Dockerfile 1.文件内容需求&#xff1a; 2.值得注意的是centos6官方源已下线&#xff0c;所以需要切换centos-vault源&#xff01; 3.Dockerfile内容 4.进入到 lamp 开始构建镜像 推送镜像到私有仓库 1.把要上传的镜像打上合适的标签 2.登录harbor仓库 3.上传镜…

InDesign软件安装包分享(附安装教程)

目录 一、软件简介 二、软件下载 一、软件简介 InDesign是一款专业的页面设计和布局软件&#xff0c;主要用于印刷和数字出版领域。它是由Adobe公司开发的一款基于文档的排版工具&#xff0c;与Adobe Acrobat、Adobe Illustrator等软件有着相似的用户界面和功能。以下是InDes…

Leetcode 1812。判断国际象棋棋盘中一个格子的颜色

国际棋盘问题&#xff1a; 给你一个坐标 coordinates &#xff0c;它是一个字符串&#xff0c;表示国际象棋棋盘中一个格子的坐标。下图是国际象棋棋盘示意图。 如果所给格子的颜色是白色&#xff0c;请你返回 true&#xff0c;如果是黑色&#xff0c;请返回 false 。 给定坐标…

IDEA软件安装包分享(附安装教程)

目录 一、软件简介 二、软件下载 一、软件简介 IntelliJ IDEA是一款流行的Java集成开发环境&#xff08;IDE&#xff09;&#xff0c;由捷克软件开发公司JetBrains开发。它专为Java开发人员设计&#xff0c;提供了许多高级功能和工具&#xff0c;使得开发人员能够更高效地编写…

最新域名和子域名信息收集技术

域名信息收集 1&#xff0e;WHOIS查询 WHOIS是一个标准的互联网协议&#xff0c;可用于收集网络注册信息、注册域名﹑IP地址等信息。简单来说&#xff0c;WHOIS就是一个用于查询域名是否已被注册及注册域名详细信息的数据库&#xff08;如域名所有人、域名注册商&#xff09;…

Python如何获取用户输入

获取用户输入的方法 在编程中&#xff0c;获取用户输入是一项常见任务&#xff0c;Python提供了多种方法来实现这一目标。让我们一起来看看这些方法。 使用input()函数获取用户输入 Python内置函数input()可以用来获取用户的输入。当程序运行到input()函数时&#xff0c;程序…

【滑动窗口】leetcode1658:将x减到0的最小操作数

目录 一.题目描述 二.思路分析 三.代码编写 一.题目描述 将x减到0的最小操作数 题目要求我们在数组的两端不断地取值&#xff0c;使得取出的数之和等于x&#xff0c;问我们最少需要取几次。 也就是说&#xff0c;在两边取两个区间&#xff0c;使得这两个区间的之和等于x&a…

数据结构(Java实现)-优先级队列(堆)

队列是一种先进先出(FIFO)的数据结构&#xff0c;但有些情况下&#xff0c;操作的数据可能带有优先级&#xff0c;一般出队 列在这种情况下&#xff0c;数据结构应该提供两个最基本的操作&#xff0c;一个是返回最高优先级对象&#xff0c;一个是添加新的对象。 这种数据结构就…

Markdown 扩展语法练习

风无痕 August 26, 2023 Markdown 入门指南Markdown 基本语法Markdown 扩展语法Markdown 基本语法练习Markdown 扩展语法练习 代码 <h2 id"table">表格</h2>| Syntax | Description | | --- | --- | | Header | Title | | Paragraph | Text |### 对齐| …

基于Python+djangoAI 农作物病虫害预警系统智能识别系统设计与实现(源码&教程)

1.背景 随着科技的发展&#xff0c;机器学习技术在各个领域中的应用越来越广泛。在农业领域&#xff0c;机器学习技术的应用有助于提高农作物的产量和质量&#xff0c;降低农业生产的成本。本文针对农作物健康识别问题&#xff0c;提出一种基于机器学习方法的农作健康识别系统&…

1、Odoo开发起点

1.1.odoo的模块组成 init.py将一个文件夹编程python包manifestpyodoo模块定义的清单文件&#xff0c;用于对odoo模块管理详见model模型类文件&#xff0c;存放py文件security表级别权限管理static静态文件views视图文件。wizard瞬态模型向导文件位置 1.2.odoo的开发规范 非强…

Vue3.0极速入门 - 环境安装新建项目

Vue介绍 Vue.js 是什么 Vue (读音 /vjuː/&#xff0c;类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是&#xff0c;Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层&#xff0c;不仅易于上手&#xff0c;还便于与第三方库或既有项目整…

模型量化(Model Quantization)

1. 简介 模型量化&#xff08;Model Quantization&#xff09;通过某种方法将浮点模型转为定点模型。比如说原来的模型里面的权重&#xff08;weight&#xff09;都是float32&#xff0c;通过模型量化&#xff0c;将模型变成权重&#xff08;weight&#xff09;都是int8的定点…

c++ qt--事件过滤(第七部分)

c qt–事件过滤&#xff08;第七部分&#xff09; 一.为什么要用事件过滤 上一篇博客中我们用到了事件来进行一些更加细致的操作&#xff0c;如监控鼠标的按下与抬起&#xff0c;但是我们发现如果有很多的组件那每个组件都要创建一个类&#xff0c;这样就显得很麻烦&#xff…

Vue3.0 新特性以及使用变更总结

Vue3.0 在2020年9月正式发布了&#xff0c;也有许多小伙伴都热情的拥抱Vue3.0。去年年底我们新项目使用Vue3.0来开发&#xff0c;这篇文章就是在使用后的一个总结&#xff0c; 包含Vue3新特性的使用以及一些用法上的变更。 图片.png 为什么要升级Vue3 使用Vue2.x的小伙伴都熟悉…

亚马逊云科技 re:Inforce 大会云安全合规与技术实践及 Security Jam 大赛,快来报名吧!...

‍‍ 2023年8月31日在北京 亚马逊云科技 re:Inforce 大会 首次登陆中国&#xff01; 我们期待您的莅临&#xff0c; 并与您一起迎接 AI 时代&#xff0c; 开启全面智能的安全旅程&#xff01; 在13:00-17:00的 培训与动手实验环节中 云安全合规与技术实践 及 Security Jam 大赛…

APP爬虫之-Protobuf协议逆向解析

在做APP抓取时&#xff0c;会发现有的APP Response回来的数据有“加密”。不知道返回的内容是什么。 如下&#xff1a; 如上&#xff0c;内容不是明文的&#xff0c;没办法解析数据。APP常见的对数据加密有三种情况&#xff1a;第一种是&#xff0c;用诸如AES这类加密算法对数…

图神经网络和分子表征:3. 不变网络最后的辉煌

写这篇文章的时候已经是2023年的8月份&#xff0c;GNN for molecule property prediction 这个小领域正在变得火热起来&#xff0c;各大榜单被不断刷新&#xff0c;颇有当年 CNN 刷榜 imagenet 的势头。 由于对力、维里等性质有着内禀优势&#xff0c;当下高居榜首的模型毫无疑…

设计模式--工厂模式(Factory Pattern)

一、 什么是工厂模式 工厂模式&#xff08;Factory Pattern&#xff09;是一种创建型设计模式&#xff0c;它提供了一种创建对象的接口&#xff0c;但是将对象的实例化过程推迟到子类中。工厂模式允许通过调用一个共同的接口方法来创建不同类型的对象&#xff0c;而无需暴露对…

<C++> STL_vector

1.vector的介绍 vector是表示可变大小数组的序列容器。就像数组一样&#xff0c;vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问&#xff0c;和数组一样高效。但是又不像数组&#xff0c;它的大小是可以动态改变的&#xff0c;而且它的…