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

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

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位,只适用于IPV4unsigned 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.mzph.cn/news/56127.shtml

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

相关文章

运放的分类、运放的参数

一、运放的分类 运放按功能分为通用运放与专用运放&#xff08;高速运放、精密运放、低IB运放等&#xff09;。 1.1通用运放 除廉价外&#xff0c;没有任何最优指标的运放。 例&#xff1a;uA741&#xff0c;LM324&#xff0c;TL06X&#xff0c;TL07X、TL08X等 国外知名运放…

小红书口碑营销技巧有哪些,打造独特品牌内容

作为一款社交电商平台&#xff0c;从目前来看&#xff0c;其实小红书也算是比较不错的口碑营销传播平台了。今天就来带大家一起分享下&#xff0c;小红书口碑营销技巧有哪些&#xff0c;打造独特品牌内容&#xff01; ​ 一、小红书口碑营销怎么做 1. 了解目标用户 首先需要明…

linux并发服务器 —— 动态库和静态库实战(一)

-E 预处理指定源文件 -S 编译指定源文件 -c 汇编指定源文件 -o 生成可执行文件 -I directory 指定Include包含文件的搜索目录 -g 编译的时候生成调试信息 -D 在程序编译时指定一个宏 -w 不生成任何的警告信息 -Wall 生成所有警告 -On n:0~3&#xff1b;表示编译器的优…

(五)Docker 安装 redis镜像+启动redis容器(超详细)

输入&#xff1a;su root命令&#xff0c;切换到root 1、启动Docker 启动&#xff1a;sudo systemctl start docker 停止&#xff1a;systemctl stop docker 重启&#xff1a;systemctl restart docker 查看docker运行状态&#xff08;显示绿色代表正常启动&#xff09;&#x…

手写数字识别之损失函数

目录 交叉熵 手写数字识别之损失函数 分类任务的损失函数 Softmax函数 交叉熵的简单理解&#xff1a;真实分布与非真实分布的交叉&#xff0c;完全对应&#xff0c;熵为0 交叉熵的代码实现 交叉熵 给定一个策略, 交叉熵就是在该策略下猜中颜色所需要的问题的期望值。更普…

videojs 实现自定义组件(视频画质/清晰度切换) React

前言 最近使用videojs作为视频处理第三方库&#xff0c;用来对接m3u8视频类型。这里总结一下自定义组件遇到的问题及实现&#xff0c;目前看了许多文章也不全&#xff0c;官方文档写的也不是很详细&#xff0c;自己摸索了一段时间陆陆续续完成了&#xff0c;这是实现后的效果.…

DevOps系列文章 之 Python基础

Python语法结构 语句块缩进 1.python代码块通过缩进对齐表达代码逻辑而不是使用大括号 2.缩进表达一个语句属于哪个代码块 3.缩进风格 &#xff1a; 建议使用四个空格 如果是Linux系统的话&#xff0c;可以这样做&#xff0c;实现自动缩进 &#xff1a; vim ~/.vimrc set ai…

FreeSWITCH 1.10.10 简单图形化界面3 - 阿里云NAT设置

FreeSWITCH 1.10.10 简单图形化界面3 - 阿里云NAT设置 0、 界面预览1、 查看IP地址2、 修改协议配置3、 开放阿里云安全组4、 设置ACL5、 设置协议中ACL&#xff0c;让PBX匹配内外网6、 重新加载SIP模块7、 查看状态8、 测试一下 0、 界面预览 http://myfs.f3322.net:8020/ 用…

LeetCode-56-合并区间

题目描述&#xff1a; 以数组 intervals 表示若干个区间的集合&#xff0c;其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间&#xff0c;并返回 一个不重叠的区间数组&#xff0c;该数组需恰好覆盖输入中的所有区间 。 可以使用 LinkedList&#xff0c;…

一起学SF框架系列附-Springframework源码学习总结

学习过程 学习Springframework6.0.8&#xff0c;前后将近4个月终于结束了。学习主要内容如图&#xff08;红框&#xff09;&#xff1a; 本次学习主要针对核心模块&#xff1a;Beans、Context、Core、SpEL&#xff08;完全独立于框架的&#xff0c;没深入学习&#xff09;、…

MySQL 实战(一):实现“附近的人”功能

❤️ 个人主页&#xff1a;水滴技术 &#x1f680; 支持水滴&#xff1a;点赞&#x1f44d; 收藏⭐ 留言&#x1f4ac; &#x1f338; 订阅专栏&#xff1a;MySQL 教程&#xff1a;从入门到精通 文章目录 ST_Distance_Sphere 函数示例一&#xff1a;计算北京站到北京西站的距…

【深度学习】实验03 特征处理

文章目录 特征处理标准化归一化正则化 特征处理 标准化 # 导入标准化库 from sklearn.preprocessing import StandardScalerfrom matplotlib import gridspec import numpy as np import matplotlib.pyplot as plt import warnings warnings.filterwarnings("ignore&quo…

aws PinPoint发附件demo

php 版aws PinPoint发附件demo Laravel8框架&#xff0c;安装了"aws/aws-sdk-php": "^3.257" 主要代码&#xff1a; public function sendRawMail(Request $request) {$file $request->file(attachment);/*echo count($file);dd($file);*/$filenam…

Mobx在非react组件中修改数据,在ts/js中修改数据实现响应式更新

我们都之前在封装mobx作为数据存储的时候&#xff0c;使用到了useContext作为包裹&#xff0c;将store变成了一个hooks使用&#xff0c;封装代码: import React from react import UserInfo from ./user import Setting from ./seting import NoteStore from ./noteclass Stor…

基于Jenkins构建生产CICD环境(第三篇)

目录 基于Jenkins自动打包并部署docker环境 1、安装docker-ce 2、阿里云镜像加速器 3、构建tomcat 基础镜像 4、构建一个Maven项目 基于Jenkins自动化部署PHP环境 基于rsync部署 基于Jenkins自动打包并部署docker环境 1、安装docker-ce 在192.168.2.123 机器上&#x…

Go 第三方库引起的线上问题、如何在线线上环境进行调试定位问题以及golang开发中各种问题精华整理总结

Go 第三方库引起的线上问题、如何在线线上环境进行调试定位问题以及golang开发中各种问题精华整理总结。 01 前言 在使用 Go 语言进行 Web 开发时&#xff0c;我们往往会选择一些优秀的库来简化 HTTP 请求的处理。其中&#xff0c;go-resty 是一个被广泛使用的 HTTP 客户端。…

如何有效进行RLHF的数据标注?

编者按&#xff1a;随着大语言模型在自然语言处理领域的广泛应用&#xff0c;如何从人类反馈进行强化学习&#xff08;RLHF&#xff09;已成为一个重要的技术挑战。并且RLHF需要大量高质量的人工数据标注&#xff0c;这是一个非常费力的过程。 本文作者在数据标注领域具有丰富经…

解决jupyter notebook可以使用pytorch而Pycharm不能使用pytorch的问题

之前我是用的这个目录下的Python 开始更新目录 1、 2、 3、

【javaweb】学习日记Day6 - Mysql 数据库 DDL DML

之前学习过的SQL语句笔记总结戳这里→【数据库原理与应用 - 第六章】T-SQL 在SQL Server的使用_Roye_ack的博客-CSDN博客 目录 一、概述 1、如何安装及配置路径Mysql&#xff1f; 2、SQL分类 二、DDL 数据定义 1、数据库操作 2、IDEA内置数据库使用 &#xff08;1&…

Python3 列表

Python3 列表 序列是 Python 中最基本的数据结构。 序列中的每个值都有对应的位置值&#xff0c;称之为索引&#xff0c;第一个索引是 0&#xff0c;第二个索引是 1&#xff0c;依此类推。 Python 有 6 个序列的内置类型&#xff0c;但最常见的是列表和元组。 列表都可以进…