C++Linux网络编程Day1

最简单server程序

#include <iostream>// sys(系统),socket(套接字),这个还是挺好理解的
#include <sys/socket.h>#include <arpa/inet.h>#include <stdio.h>
#include <string.h>int main(){// 创建一个套接字描述符,这个描述符本质上就是一个Linux的文件描述符int socketfd = socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in serv_addr;bzero(&serv_addr, sizeof(serv_addr));serv_addr.sin_family = AF_INET;// s_addr就是用来存储32位IPV4地址的serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");// 开启服务器的8888端口,问题是这个hton是什么// hton是一个将主机字节序(host byte order)的端口号转换为网络字节序(network byte order)serv_addr.sin_port = htons(8888);/*bind首先绑定套接字的文件描述符后者将传入的serv_addr强制转换为一个sockaddr类型的指针也就是将sockaddr_in强制转换为sockaddr这是因为后者是通用套接字结构体,而前者是专属于IPv4的由此可知:网络套接字实际上是基于通用套接字的*/bind(socketfd, (sockaddr*)&serv_addr, sizeof(serv_addr));// 后者是一个常量,表示监听队列的最大长度listen(socketfd, SOMAXCONN);struct sockaddr_in clnt_addr;/*初始化一个socklen_t类型的变量,其值为clnt_addr的大小这个类型是一个无符号整型,用于表示套接字地址结构体的长度*/socklen_t clnt_addr_len = sizeof(clnt_addr);bzero(&clnt_addr, clnt_addr_len);int clnt_sockfd = accept(socketfd, (sockaddr*)&clnt_addr, &clnt_addr_len);/*inet_ntoa是将将一个 32 位的 IPv4 地址从网络字节序转换为点分十进制的IP地址字符串ntohs用于将网络字节序转换为主机字节序在这里就是将看不懂的内容转换为我们能看得懂的东西*/printf("new client fd %d IP: %s Port: %d\n", clnt_sockfd, inet_ntoa(clnt_addr.sin_addr), ntohs(clnt_addr.sin_port));return 0;
}

基本所有的知识点我都写在其中了,有些基础性的东西需要大家自己去学习”计算机网络“的相关知识,至于为什么在使用accept等函数的时候,需要将sockaddr_in转换成sockaddr,这点需要看书:游双的《Linux高性能服务器编程》,在其中的第五章第一节:socket地址API中有详细讨论。
接下来我们看看其中使用到的头文件:sys/inet.h和arpa/inet.h

sys/socket.h

这个头文件是网络编程的核心头文件之一,它包含了一些用于网络贬称搞到常量、数据结构和函数原型。这里指出一些常用的:

常量
  • AF_INET:表示IPv4地址族
  • AF_INET6:表示IPv6地址族
  • SOCK_STREAM:表示TCP套接字类型
  • SOCK_DGRAM:表示UDP套接字类型
  • SO_REUSEADDR:表示允许套接字地址重用
数据结构
  • sockaddr_in:用于储存IPv4地址和端口号的结构体
  • aockaddr_in6:用于存储IPv6地址和端口号的结构体
  • sockaddr:用于通用的套接字地址结构体
函数

与网络通信的一系列API都在其中:

  • socket():用于创建套接字
  • bind():用于将套接字绑定到指定的地址和端口
  • listen():用于监听套接字上的连接请求
  • accept():用于接收连接请求并创建新的套接字,用于与客户端通信
  • connect():用于发起连接请求,并与服务器建立连接
  • sned()/sendto():用于发送数据
  • recv()/recvfrom():用于接收数据
  • close():用于关闭套接字

这些函数并不是sys/socket.h中的全部,这里只写了比较基础的、常用的,还有很多的内容在netinet/in.h头文件中

arpa/inet.h

在上面,我们提到了头文件netinet/in.h,这个头文件包含在arpa/inet.h中,因此我们使用了这个头文件就可以不用再次包含。它也包含了许多网络通信相关的常量、结构体、函数。

常量
  • INADDR_ANY:表示任意地址,用于将套接字绑定到所有可用的网络接口
  • INADDR_LOOPBACK:表示回环地址,即:127.0.0.1,用于本地回环测试和通信
  • INADDR_BROADCAST:表示广播地址,用于向同一网络中的所有主机发送数据
数据结构
  • in_addr:IPv4地址的结构体,定义为struct in_addr。该结构体包含一个字段s_addr,用于存储32位的IPv4地址
函数
  • inet_addr():将一个表示IPv4地址的字符串转换为对应的32位无符号整数,返回网络字节序表示的IPv4地址
  • inet_addr():将一个32位无符号整数表示的IPv4地址转换为对应的点分十进制字符串表示
  • inet_pton():将一个IPv4或IPv6地址的字符串表示转换为对应的二进制格式,并储存到指定的地址结构体中
  • inet_ntop():将一个二进制格式的IPv4或IPv6地址转换为对应的字符串表示

第一个C/S应用

server.cpp:

#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include "util.h"int main() {int sockfd = socket(AF_INET, SOCK_STREAM, 0);errif(sockfd == -1, "socket create error");struct sockaddr_in serv_addr;bzero(&serv_addr, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");serv_addr.sin_port = htons(8888);errif(bind(sockfd, (sockaddr*)&serv_addr, sizeof(serv_addr)) == -1, "socket bind error");errif(listen(sockfd, SOMAXCONN) == -1, "socket listen error");struct sockaddr_in clnt_addr;socklen_t clnt_addr_len = sizeof(clnt_addr);bzero(&clnt_addr, sizeof(clnt_addr));int clnt_sockfd = accept(sockfd, (sockaddr*)&clnt_addr, &clnt_addr_len);errif(clnt_sockfd == -1, "socket accept error");printf("new client fd %d! IP: %s Port: %d\n", clnt_sockfd, inet_ntoa(clnt_addr.sin_addr), ntohs(clnt_addr.sin_port));while (true) {char buf[1024];bzero(&buf, sizeof(buf));ssize_t read_bytes = read(clnt_sockfd, buf, sizeof(buf));if(read_bytes > 0){printf("message from client fd %d: %s\n", clnt_sockfd, buf);write(clnt_sockfd, buf, sizeof(buf));} else if(read_bytes == 0){printf("client fd %d disconnected\n", clnt_sockfd);close(clnt_sockfd);break;} else if(read_bytes == -1){close(clnt_sockfd);errif(true, "socket read error");}}close(sockfd);return 0;
}

client.cpp:

#include <iostream>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "util.h"int main(){int sockfd = socket(AF_INET, SOCK_STREAM, 0);errif(sockfd == -1, "socket create error");struct sockaddr_in serv_addr;bzero(&serv_addr, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");serv_addr.sin_port = htons(8888);errif(connect(sockfd, (sockaddr*)&serv_addr, sizeof(serv_addr)) == -1,"socket connect error");while(true){char buf[1024];bzero(&buf, sizeof(buf));scanf("%s", buf);ssize_t write_bytes = write(sockfd, buf, sizeof(buf));if(write_bytes == -1){printf("socket already disconnected, can't write any more!\n");break;}bzero(&buf, sizeof(buf));ssize_t read_bytes = read(sockfd, buf, sizeof(buf));if(read_bytes > 0){printf("message from server: %s\n", buf);}else if(read_bytes == -1){close(sockfd);errif(true, "socket read error");}else if(read_bytes == 0){printf("server socket disconnected!\n");break;}}close(sockfd);return 0;
}

Makefile:

# shell和Makefile有点像
build:make client;make serverclient: client.cpp util.cppg++ -o client client.cpp util.cppserver: server.cpp util.cppg++ -o server server.cpp util.cppclean:rm -f server client

unistd.h

unistd.h 是C语言标准库中的一个头文件,它提供了对POSIX(可移植操作系统接口)的访问。其中包含了许多函数和符号常量,用于进程控制、文件操作、目录操作等方面的功能

函数
  • fork():创建一个新进程
  • exec()系列函数:用于执行新的程序文件。例如:execl(),execv(),execle()等
  • exit():使当前进程退出
  • sleep():使当前进程退出一段时间
  • access():检查文件的访问权限
  • chdir():改变当前工作目录
  • getpid():获取当前进程的PID
  • getuid():获取房前用户的用户ID
  • open():打开一个文件
  • close():关闭一个已打开的文件

由于在Linux中:”万事万物皆文件“,因此,我们操作进程间通信实际上就是对文件进行操作。
在上面的代码中:我们使用了该头文件中的writeread来进行网络接口的数据读写操作;使用了close来关闭网络连接文件、释放相关系统资源。

server和client需要做的操作

  • 服务端:
    1. 创建套接字socket
    2. bind()绑定IP地址,Port等相关信息,在本例中是使用的IPv4,因此使用的是sockaddr_in
    3. 开始监听listen
    4. 创建一个与客户端进行连接的socket,使用accept接收客户端的连接
    5. 使用read读取客户端传递的信息,并使用write向客户端返回信息
    6. 使用close断开连接(关闭套接字)
  • 客户端:
    1. 创建套接字socket
    2. 使用connect与服务端进行连接
    3. 使用write发送信息,使用read读取收到的信息
    4. 使用close关闭套接字

细节解析

阅读server.cpp(服务端)源码,我们能够发现:我们一开始创建的socket只用于监听,而在与客户端连接的时候我们并没有使用这个socket

	// bind绑定sockfd,并使其处于监听状态errif(bind(sockfd, (sockaddr*)&serv_addr, sizeof(serv_addr)) == -1, "socket bind error");errif(listen(sockfd, SOMAXCONN) == -1, "socket listen error")// 使用accept接收客户端发来的连接请求int clnt_sockfd = accept(sockfd, (sockaddr*)&clnt_addr, &clnt_addr_len);

可能我们需要看看accept的声明:

extern int accept (int __fd, __SOCKADDR_ARG __addr,socklen_t *__restrict __addr_len);

可以发现它的返回值是个int类型,而Linux中,文件描述符也正好是个int类型,在查询资料后证实,accept其实是新开了个文件描述符,用于维持和客户端的通信。我们需要通过这个文件描述符进行网络通信。
套接字可以分为两种类型:监听套接字连接套接字,我们bind的就是监听套接字,accept所创建的文件描述符就是连接套接字,连接套接字才是真正用来网络通信的的

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

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

相关文章

项目管理的细节:屁股决定脑袋

在我刚入行的时候&#xff0c;由于我勤奋好学&#xff0c;技术上钻研得比较深入&#xff0c;我曾一度自视甚高&#xff0c;甚至有些傲慢。那时&#xff0c;我年轻气盛&#xff0c;未能充分认识到自己的不足。我曾认为领导对我的评价不公&#xff0c;未能充分认识到我的能力和价…

SpringBoot和Vue接口调用传参方式

简单总结一下常用的传参方式&#xff0c;一些前后端分离项目接口调试时经常出现传参格式错误问题。 前后端进行交互时方法一般就分为get和post&#xff0c;至于后面的delete和put都是基于post进行封装而出的。 Http请求中不同的请求方式会设置不同的Content-Type,参数的传递方…

C#,获取与设置Windows背景图片的源代码

为了满足孩子们个性化桌面的需求。 这里发布获取与设置Windows背景图片的源代码。 1 文本格式 using System; using System.IO; using System.Data; using System.Linq; using System.Text; using System.Drawing; using System.Collections; using System.Collections.Gene…

ES数据处理方法

由于日志数据存在ES项目里&#xff0c;需要从ES中获取日志进行分析&#xff0c;使用SQL数据进行处理&#xff0c;如下&#xff1a; select traceid-- STRING COMMENT 流程id, ,appnum -- BIGINT COMMENT 迭代号, ,appversion --STRING COMMENT APP版本, ,appc…

JeecgBoot集成TiDB,打造高效可靠的数据存储解决方案

TiDB简介 TiDB是PingCAP公司自主设计、研发的开源分布式关系型数据库&#xff0c;同时支持在线事务处理与在线分析处理 (Hybrid Transactional and Analytical Processing, HTAP) 的融合型分布式数据库产品&#xff0c;具备水平扩容或者缩容、金融级高可用、实时 HTAP、云原生…

【学习笔记】CF1349F2 Slime and Sequences (Hard Version)

多项式工业警告&#xff01;&#xff01;&#xff01; 点击看题意 思路来自 这位大佬 。 为什么这么好的题解没人评论。 Part 1 前置知识&#xff1a;拉格朗日反演&#xff08;多项式复合&#xff09;&#xff0c;分式域&#xff08;引入负整数次项&#xff09;。 条件&a…

基数排序算法

1. 排序算法分类 十种常见排序算法可以分为两大类&#xff1a; 比较类排序&#xff1a; 通过比较来决定元素间的相对次序&#xff0c;由于其时间复杂度不能突破O(nlogn)&#xff0c;因此也称为非线性时间比较类排序。比较类排序算法包括&#xff1a;插入排序、希尔排序、选择…

Netty接收超长TCP数据时 使用按行分隔Decoder无法正确解码的问题解决

使用Netty实现的tcp服务端&#xff0c;由于tcp是流式传输的&#xff0c;故需要选用一个解码器对流式消息进行解码和包分隔&#xff0c;以防收到不正确的包。例如LineBasedFrameDecoder&#xff0c;LengthFieldBasedFrameDecoder&#xff0c;DelimiterBasedFrameDecoder等常用解…

第139期 做大还是做小-Oracle名称哪些事(20240125)

数据库管理139期 2024-01-25 第139期 做大还是做小-Oracle名称哪些事&#xff08;20240125&#xff09;1 问题2 排查3 扩展总结 第139期 做大还是做小-Oracle名称哪些事&#xff08;20240125&#xff09; 作者&#xff1a;胖头鱼的鱼缸&#xff08;尹海文&#xff09; Oracle A…

SQL - 事务控制

SQL - 事务控制 文章目录 SQL - 事务控制TCL - 事务事务的边界事务的特性事务的应用 事务隔离等级MySQL支持四种隔离级别 TCL - 事务 **模拟场景&#xff1a;**生活当中转账是转账方账户扣钱&#xff0c;收账方账户加钱。用数据库操作来模拟现实转账。 数据库模拟&#xff1a…

CI/CD

介绍一下CI/CD CI/CD的出现改变了开发人员和测试人员发布软件的方式,从最初的瀑布模型,到最后的敏捷开发(Agile Development),再到今天的DevOps,这是现代开发人员构建出色产品的技术路线 随着DevOps的兴起,出现了持续集成,持续交付和持续部署的新方法,传统的软件开发和交付方…

软件设计师——软件工程(五)

&#x1f4d1;前言 本文主要是【软件工程】——软件设计师——软件工程的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是听风与他&#x1f947; ☁️博客首页&#xff1a;CSDN主页听风与他 &#x1f304…

安全防御综合组网实验

题目 要求 生产区在工作时间可以访问服务器区&#xff0c;仅可以访问http服务器。办公区全天可以访问服务器区&#xff0c;其中10.0.2.20 可以访问FTP服务器和http服务器。10.0.2.10仅可以ping通10.0.3.10。办公区在访问服务器区时采用匿名认证的方式进行上网行为管理。办公区…

Vue内嵌套层级过深,el-input改变值视图无响应

出现场景 1.v-for内绑定复杂类型数据内部值&#xff0c;通过input更改其值时。 2.element表单或表格等组件内部&#xff0c;el-input绑定复杂类型数据内部值&#xff0c;通过input更改其值时。 解决思路 1.el-input加入 input“change($event)” … import { getCurrentInst…

【RabbitMQ】死信(延迟队列)的使用

目录 一、介绍 1、什么是死信队列(延迟队列) 2、应用场景 3、死信队列(延迟队列)的使用 4、死信消息来源 二、案例实践 1、案例一 2、案例二&#xff08;消息接收确认 &#xff09; 3、总结 一、介绍 1、什么是死信队列(延迟队列) 死信&#xff0c;在官网中对应的单词…

Unity学习之坦克游戏制作(2)游戏场景的制作

文章目录 1. 基础场景的搭建2. 游戏主面板2.1 拼出面板2.2 创建新面板2.3 设置面板复用2.4 退出界面 3. 坦克基类3.1 创建基类脚本3.1.1 基类基本属性3.1.2 抽象开火函数3.1.3 受伤虚函数3.1.4 死亡虚函数 4 玩家——基础移动旋转摄像机跟随4.1 玩家对象脚本4.2 控制坦克移动4.…

移动端打包成功后禁止生成 report.html 文件,并不自动打开该文件

目录 【问题】移动端 npm run build 打包后生成并打开 report.html 文件package.json 文件vue.config.js 代码 【解决】打包后去除 report.html 文件vue.config.js 代码 参考 【问题】移动端 npm run build 打包后生成并打开 report.html 文件 package.json 文件 {"name&…

蓝牙----蓝牙协议栈L2CAP

蓝牙协议栈----L2CAP L2CAP的功能术语介绍L2CAP信道L2CAP的工作模式经典蓝牙的分段和分解过程 L2CAP—逻辑链路控制和适配层协议 L2CAP的功能 经典蓝牙的L2CAP层实现了协议复用、数据分段与重组、封装调度等操作。BLE的L2CAP层是经典蓝牙L2CAP层的简化版本&#xff1a; 在基…

鸿蒙自定义组件内自定义构建函数

参数传递规则 自定义构建函数的参数传递有按值传递和按引用传递两种&#xff0c;均需遵守以下规则&#xff1a; 参数的类型必须与参数声明的类型一致&#xff0c;不允许undefined、null和返回undefined、null的表达式。在自定义构建函数内部&#xff0c;不允许改变参数值。如…

8-Docker网络命令之connect

1.connect介绍 Docker网络命令connect是用来将容器连接到网络。 2.connect用法 docker network connect [参数] NETWORK CONTAINER [root@centos79 ~]# docker network connect --helpUsage: docker network connect [OPTIONS] NETWORK CONTAINERConnect a container to a…