linux下的网络编程

网络编程

  • 1. 网络基础编程知识
    • 1.1网络字节序问题
    • 1.2 常用socket编程接口
      • 1.2.1 sockaddr
      • 1.2.2 ip地址转换函数
      • 1.2.4 socket()
      • 1.2.3 bind()
      • 1.2.4 listen()
      • 1.2.5 accept()
      • 1.2.6 connect()
    • 1.3 以udp为基础的客户端连接服务器的demo
    • 1.4 以udp为基础的的服务器聊天室功能demo
    • 1.5 基于TCP连接的具有线程池功能的服务器客户端demo
      • tcp_test目录
      • sing_fock_test目录
      • thread_tcp目录
      • tcpthreadpool目录
  • 网络的理论部分

1. 网络基础编程知识

1.1网络字节序问题

已知计算机的数据存储有大小端之分,网络流数据同样有大小端之分。

  • 发送主机通常将发送缓冲区中的数据按内存地址从低到高的数据顺序发出
  • 接收主机把从网络上接到的字节依次保存在缓冲区中,也是按地址从低到搞的顺序保存
  • 因此网络数据流规定:先发出的数据是低地址,后发出的数据是高地址
  • TCP/IP协议规定:网络流数据应采取大端字节序,即低地址高字节(符合人类的阅读习惯)
  • 如果当前发送主机是小端,那么需要改成大端再发送!

为了解决这个问题,使网络具有可移植性,使同样的代码在大小端机器上都能运行,需要使用下面的库函数做网络字节序和主机字节序的转换。

# include<arpa/inet.h>

uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);

uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);

解释:

  • 其中h表示host,n表示network,l表示32位长整数,s表示16位短整数。
  • htonl 表示将32位的长整形数从主机字节序转化为网络字节序。例如:将IP地址转化后发送。
  • 若主机是小端字节序,这些函数将做大小端转换再返回;否则原封不动返回。

从参数和返回值可以看出,这个函数是转换整型的函数,比如说port接口转换就会用到该函数
如图:atoi函数把string转化成整形,然后交给htons转化成网络字节流的数据格式!
在这里插入图片描述

1.2 常用socket编程接口

socket API是一层抽象的网络编程接口,适用于各种底层网络协议。如IPv4、v6等。

//网络编程常用的四个接口
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include <arpa/inet.h>

// 创建 socket 文件描述符(TCP/UDP, 客户端+服务器)
int socket(int domain, int type, int protocol);

//绑定端口号(TCP/UDP, 服务器)
int bind(int socket, const struct sockaddr *address, socklen_t address_len);

// 开始监听socket (TCP,服务器)
int listen(int socket, int backlog);

//接受请求(TCP,服务器)
int accept(int socket, struct sockaddr* address, socklen_t* addrlen);

//建立连接(TCP,客户端)
int connect(int sockfd, const struct sockaddr* addr, socklen_t addrlen);

1.2.1 sockaddr

sockaddr可以认为是存放,将要访问的服务器的ip地址和端口号的结构体。

因为各种网络协议地址格式并不同,所以为了适配格式,产生了sockaddr(通用的地址结构)。
以bind为例(accept、connect都一样),AF_INET就指定了将要通信的地址类型,所以再传入sockaddr之后,程序会根据socket类型自动转化!
这个相当于c语言的多态。(调用同一个函数,会有不同的效果!)

在这里插入图片描述

  • IPv4和IPv6的地址格式定义在netinet/in.h中,IPv4地址用sockaddr_in结构体表示,包括16位地址类型,16位端口号地址和32位IP地址。
  • IPv4和IPv6地址类型分别定义为常数AF_INET、AF_INET6。这样,只要取得某种sockaddr结构体的首地址,不需要具体知道是哪种类型的sockaddr结构体,就可以根据16位类型字段确定结构体中的内容。
  • socket API可以都用struct sockaddr* 类型表示,在使用的时候需要强制转化成sockaddr_in;好处就是增加了程序的通用性。
    在这里插入图片描述
    下面是v4和v6的sockaddr地址结构:
    在这里插入图片描述
    在这里插入图片描述

1.2.2 ip地址转换函数

功能:实现点分十进制字符串和无符号32位整数之间相互转化!
在这里插入图片描述

inet_addr()函数功能介绍:
在这里插入图片描述

在1.2.1这一节我们发现,ipv4其实是无符号整数,然而我们在访问ip地址时,使用的是点分十进制的方式。这就要求我们把点分十进制的字符串转化成无符号整数。
比如:ip = “192.168.1.1” ->xxxxxxxx …xxxxxxxx 这种形式, 我们可以使用atoi这种函数一个一个转化。
但是可以使用inet_addr()接口,可以将点分十进制直接转化。
如下图所示:
在这里插入图片描述


inet_aton()函数介绍:将ascii码形式的点分十进制转化成网络需要的无符号数。
在这里插入图片描述

void func()
    {
        char* _ip="192.168.1.1";
        struct sockaddr_in local;

		inet_aton(_ip, &(local.sin_addr));

        std::cout<<"aton转化前_ip: "<<_ip<<std::endl;
		std::cout<<"aton转化后无符号整数"<<local.sin_addr.s_addr<<std::endl;
    }

		//aton转化前_ip: 192.168.1.1
		//aton转化后无符号整数16885952

inet_ntoa()函数介绍:将网络的无符号数转化成点分十进制:
在这里插入图片描述

int main()
{
	// initServer();
	struct sockaddr_in local1;
	struct sockaddr_in local2;
	local1.sin_addr.s_addr = 0;
	local2.sin_addr.s_addr = 0xffffffff;

	char *result1 = inet_ntoa(local1.sin_addr);
	char *result2 = inet_ntoa(local2.sin_addr);

	std::cout << "第一次调用ntoa:restult1: " << result1 << std::endl;  
	std::cout << "第二次调用ntoa:restult2: " << result2 << std::endl;
	
	return 0;
	//第一次调用ntoa:restult1: 255.255.255.255
    //第二次调用ntoa:restult2: 255.255.255.255
}
  • 为什么result1和result2的结果一样?
    因为手册上说了,ntoa函数是系统申请了一个静态地址空间,存放了返回值。当再次调用时,静态地址被覆盖了,因此就被改变了。这个例子变相的说明了它可能不是一个线程安全的函数!!!

验证一下是不是线程安全的?

#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
void *Func1(void *p)
{
	struct sockaddr_in *addr = (struct sockaddr_in *)p;
	while (1)
	{
		char *ptr = inet_ntoa(addr->sin_addr);
		sleep(1);
		printf("addr1: %s\n", ptr);
	}
	return NULL;
}
void *Func2(void *p)
{
	struct sockaddr_in *addr = (struct sockaddr_in *)p;
	while (1)
	{
		char *ptr = inet_ntoa(addr->sin_addr);
		sleep(1);
		printf("addr2:%s\n", ptr);
	}
	return NULL;
}
int main()
{
	pthread_t tid1 = 0;
	struct sockaddr_in addr1;
	struct sockaddr_in addr2;
	addr1.sin_addr.s_addr = 0;
	addr2.sin_addr.s_addr = 0xffffffff;
	pthread_create(&tid1, NULL, Func1, &addr1);
	pthread_t tid2 = 0;
	pthread_create(&tid2, NULL, Func2, &addr2);
	pthread_join(tid1, NULL);
	pthread_join(tid2, NULL);

	return 0;
}

根据结果可知,在centos7上,该函数是线程安全的,内部应该加了锁。
在这里插入图片描述
建议:

  • 在多线程下,推荐使用inet_ntop函数,这个函数由调用者提供一个缓冲区保存结果,可以规避线程安全问题。

1.2.4 socket()

将本机的网络号和端口号
在这里插入图片描述

  • socket()打开一个网络通讯端口,如果成功的话,就像open()一样,返回一个文件描述符;
  • 应用程序可以像读写文件一样用read/write 在网络上收发数据;
  • 如果调用出错,socket返回-1;
  • 对于IPv4,domain参数为AF_INET;IPv6为AF_INET6。
  • 对于TCP协议,type参数可以指定为SOCK_STREAM,表示面向流的传输协议;对于UDP协议,指定为SOCK_DGRAM。
  • 第三个参数默认为0即可。

1.2.3 bind()

在这里插入图片描述

  • 服务器程序所监听的网络地址和端口号通常是固定不变的,客户端程序得知服务器程序的地址和端口号后就可以向服务器发起连接;服务器需要调用bind绑定一个固定的网络地址和端口号;
  • bind()成功返回0,失败返回-1。
  • bind()的作用是将参数sockfd和myaddr绑定在一起,使sockfd这个用于网络通讯的文件描述符监听myaddr所描述的地址和端口号。
  • sockaddr* 存的是一个通用指针类型,存的是本地的IP和port,第三个参数是结构体的长度;
    初始化sockaddr可以这样初始化:
	struct sockaddr_in local;        
	bzero(&local, sizeof local);    //初始化为0,类似于memset
	local.sin_family = AF_INET;     //指明famaily为ipv4地址协议
	//服务器的IP和端口未来也是要发送给对方主机的 ->先要将数据发送到网络!
	local.sin_port = htons(_port);    //将host的整形,转化为net的string类型
	//1.同上,将点分十进制字符串风格IP地址->4字节
	//2.  然后4字节主机序列->网络序列
	// 我们可以创建子进程帮我们完成这个工作,但是我们有一套接口,可以帮助我们完成这个工作
	local.sin_addr.s_addr = _ip.empty()?INADDR_ANY:inet_addr(_ip.c_str());    //如果我们没自己写ip地址,服务器会自动给分配一个!,这样,只要端口号正确,服务器就能收到消息!
  • INADDR_ANY,这个宏表示本地的任意IP地址,因为服务器可能有多个网卡,每个网卡也可能绑定多个IP地址,这样设置可以在所有的IP地址上监听,直到与某个客户端建立了连接时才确定下来到底用哪个IP地址;

1.2.4 listen()

在这里插入图片描述

  • listen()声明sockfd处于监听状态,并且最多允许有backlog个客户端处于连接等待状态,如果接收到更多的连接请求就忽略,这里设置一般不会太大(一般是5)
  • listen() 成功返回0,失败返回-1;

1.2.5 accept()

在这里插入图片描述

  • 三次握手完成后,服务器调用accept()接受连接;
  • 如果服务器调用accept()时还没有客户端的连接请求,就阻塞等待直到有客户端连接上来;
  • addr是一个输出型参数,accept()返回时传出客户端的地址和端口号;
  • 如果给addr传NULL,表示不关心客户端的地址。
  • addrlen参数时一个传入传出参数,传入的是调用者提供的缓冲区addr的长度,传出的时客户端地址结构体的实际长度。

简单理解一下流程:
客户端输入listen激活的sock的IP和port,然后服务器accept后,再产生一个sockfd来为客户端服务。
相当于门口有人把你领进来了之后,又分配了一个服务员来服务你,以后有什么事就直接叫服务员就好了!

1.2.6 connect()

在这里插入图片描述

  • 客户端需要调用connect()连接服务器;
  • connect和bind的参数一致,区别在于bind的参数是自己的地址,而connect的参数是对方的地址;
  • connect()成功返回0,出错返回-1;

1.3 以udp为基础的客户端连接服务器的demo

功能1:客户端输入消息,服务器收到消息,并返回给客户端。
功能2:客户端输入linux指令,服务器收到指令,并返回给客户端结果消息。

  • 功能1使用:udp_server copy.hpp需要编译这个文件
./server 8080   运行服务器的可执行程序
./client 123.0.0.1 8080  连接服务器ip地址,和端口号, 然后输入消息即可!
  • 功能2使用:udp_server .hpp需要编译这个文件
./server 8080   运行服务器的可执行程序
./client 127.0.0.1 8080  连接服务器ip地址,和端口号, 输入linux命令即可

源码地址

1.4 以udp为基础的的服务器聊天室功能demo

功能1:chat_no_thread文件夹,实现的是可以多个客户端连接服务器,但是都是各发各的消息,客户端不互通。
功能2: chat_thread_success 文件夹,实现的是可以多个客户端连接服务器,客户端消息互通,相当于群聊功能。

./server 8080   运行服务器的可执行程序
./client 123.0.0.1 8080  连接服务器ip地址,和端口号, 然后输入消息即可!
  • 功能2使用:udp_server .hpp需要编译这个文件
./server 8080   运行服务器的可执行程序
./client 127.0.0.1 8080  连接服务器ip地址,和端口号, 然后输入消息即可!

源码地址

1.5 基于TCP连接的具有线程池功能的服务器客户端demo

目录结构:
在这里插入图片描述

tcp_test目录

该目录下实现了基本的TCP连接的服务器功能,但是客户端没有写。
可以编译运行服务器成功后,使用telnet命令进行测试!
功能:服务器接受消息,并且返回给客户端。

./server 8080   运行服务器的可执行程序
telnet 127.0.0.1 8080  连接服务器ip地址,和端口号, 然后输入消息测试即可!

sing_fock_test目录

服务器端版本有三种:

/*
主要包含两个版本
版本1:单进程版,会阻塞
版本2:多进程版,会阻塞
版本2.1:多进程版本,变成个孤儿进程,不会阻塞!
*/

其中版本1:是单进程版,意思就是说服务器一次只能建立一个链接,断开后才能建立第二个链接。
版本2是多进程版,虽然子进程直接退出了,但是父进程得阻塞等待,所以说服务器也会阻塞等待它。
版本2.1:子进程再fork后,子进程立马退出(父进程就不会阻塞了),就会编程孤儿进程,孤儿进程被OS领养,因此就不会阻塞父进程。

使用:

./server 8080   运行服务器的可执行程序
./client 123.0.0.1 8080  连接服务器ip地址,和端口号, 然后输入消息即可!

源码地址

thread_tcp目录

线程版本,服务器端通过线程来为客户端提供服务建立TCP链接,这样不会阻塞主进程,主进程只管监听,线程管进行和客户端通信。

使用:

./server 8080   运行服务器的可执行程序
./client 123.0.0.1 8080  连接服务器ip地址,和端口号, 然后输入消息即可!

源码地址

tcpthreadpool目录

该服务是线程池版本的,预先申请好线程,然后等待使用。这样可以降低频繁申请的时间。
客户端版本有三种:

/*
主要包含三个版本
版本1(tcp_client copy 2.cc):发消息就建立连接,发完自动断开,客户主动断开,服务器不会断开!
版本2(tcp_client copy.cc):常链接,一个线程为一个人服务,不会自动断开。
版本3(tcp_client.cc):发消息就建立连接,发完自动断开,change和英汉互译服务,客户主动断开,服务器也会主动断开!
*/

服务器有三个功能:小写转大写,英汉互译功能,都在server函数里面。
源码地址

网络的理论部分

理论部分介绍

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

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

相关文章

解决vscode配置C++编译带有中文名称报错问题

在新电脑上安装vscode运行带有中文路径和中文名称的C代码时遇到报错 根据别人的教程将laugh.json文件中"program": "${fileDirname}\\${fileBasenameNoExtension}.exe",改成了"program": "${fileDirname}\\output\\test.exe",&#x…

聊天广场(Vue+WebSocket+SpringBoot)

由于心血来潮想要做个聊天室项目 &#xff0c;但是仔细找了一下相关教程&#xff0c;却发现这么多的WebSocket教程里面&#xff0c;很多都没有介绍详细&#xff0c;代码都有所残缺&#xff0c;所以这次带来一个比较完整得使用WebSocket的项目。 目录 一、效果展示 二、准备工…

大数据中的常见数据问题:独断脏

想象你刚刚入职一家声称正在进行"数字化转型"的大型企业,担任大数据开发工程师。在入职的第一周,你满怀热情,迫不及待地想要大展拳脚,用你的技能来推动公司的数据驱动决策。 目录 大数据中的常见数据问题1. 独 - 数据孤岛2. 断 - 数据价值链断层3. 缺 - 标准、治理…

并口、串口和GPIO口区别

并口 并行接口,简称并口。并口采用的是25针D形接头。所谓“并行”,是指8位数据同时通过并行线进行传送,这样数据传送速度大大提高,但并行传送的线路长度受到限制,因为长度增加,干扰就会增加,数据也就容易出错,目前,并行接口主要作为打印机端口等。 并口的工作模式 …

ctfshow web 36d 练手赛

不知所措.jpg 没啥用然后测试了网站可以使用php伪达到目的 ?filephp://filter/convert.base64-encode/resourcetest/../index.<?php error_reporting(0); $file$_GET[file]; $file$file.php; echo $file."<br />"; if(preg_match(/test/is,$file)){inclu…

安全测试之使用Docker搭建SQL注入安全测试平台sqli-labs

1 搜索镜像 docker search sqli-labs 2 拉取镜像 docker pull acgpiano/sqli-labs 3 创建docker容器 docker run -d --name sqli-labs -p 10012:80 acgpiano/sqli-labs 4 访问测试平台网站 若直接使用虚拟机&#xff0c;则直接通过ip端口号访问若通过配置域名&#xff0…

【论文笔记】UniST:通用预训练城市时空预测模型

目录 写在前面1. 通用时空模型的挑战与能力特性2. 构建通用时空模型UniST2.1 大规模时空预训练2.2 时空知识规则引导提示学习 3. UniST的实验与分析3.1 模型预测效果3.2其他实验分析 写在前面 文章标题&#xff1a;UniST: A Prompt-Empowered Universal Model for Urban Spati…

基于SpringBoot+Vue的招生管理系统(带1w+文档)

基于SpringBootVue的招生管理系统(带1w文档&#xff09; 通过招生管理系统的研究可以更好地理解系统开发的意义&#xff0c;而且也有利于发展更多的智能系统&#xff0c;解决了人才的供给和需求的平衡问题&#xff0c;招生管理系统的开发建设&#xff0c;由于其开发周期短&…

linux centos 安装niginx并且添加ssl(https)模块

文章目录 前言一、nginx安装教程1.流程步骤 总结 前言 一、nginx安装教程 1.流程步骤 代码如下&#xff08;示例&#xff09;&#xff1a; 1.先下载linux安装包 2.解压安装命令 sudo tar -zxvf nginx-1.20.1.tar.gz3.进入解压后的目录 sudo cd nginx-1.20.14.安装 sudo y…

AntDesign上传组件upload二次封装+全局上传hook使用

文章目录 前言a-upload组件二次封装1. 功能分析2. 代码详细注释3. 使用到的全局上传hook代码4. 使用方式5. 效果展示 总结 前言 在项目中&#xff0c;ant-design是我们常用的UI库之一&#xff0c;今天就来二次封装常用的组件a-upload批量上传组件,让它用起来更方便。 a-uploa…

C++ | Leetcode C++题解之第213题打家劫舍II

题目&#xff1a; 题解&#xff1a; class Solution { public:int robRange(vector<int>& nums, int start, int end) {int first nums[start], second max(nums[start], nums[start 1]);for (int i start 2; i < end; i) {int temp second;second max(fi…

Nacos服务注册总流程(源码分析)

文章目录 服务注册NacosClient找看源码入口NacosClient服务注册源码NacosServer处理服务注册 服务注册 服务注册 在线流程图 NacosClient找看源码入口 我们启动一个微服务&#xff0c;引入nacos客户端的依赖 <dependency><groupId>com.alibaba.cloud</groupI…

工作两年后,我如何看待设计模式

在软件工程中&#xff0c;设计模式是经过反复验证的最佳实践&#xff0c;用于解决在软件设计中经常遇到的一类问题。它们为开发者提供了一种通用的解决方案和语言&#xff0c;使得复杂的编程问题得以简化&#xff0c;代码结构更加清晰&#xff0c;可维护性大大提高。简而言之&a…

PostgreSQL 如何优化存储过程的执行效率?

文章目录 一、查询优化1. 正确使用索引2. 避免不必要的全表扫描3. 使用合适的连接方式4. 优化子查询 二、参数传递1. 避免传递大对象2. 参数类型匹配 三、减少数据量处理1. 限制返回结果集2. 提前筛选数据 四、优化逻辑结构1. 分解复杂的存储过程2. 避免过度使用游标 五、事务处…

隐私计算实训营第二期第七课:XGB算法与SGB算法开发实践

隐私计算实训营第二期-第七课 第七课&#xff1a;XGB算法与SGB算法开发实践1 决策树模型1.1 决策树的训练和预测过程1.2 决策树的发展过程 2 GBDT模型2.1 Boosting核心思想2.2 GBDT原理 3 XGB模型3.1 XGB核心思想3.2 XGB优点 3 隐语纵向树模型3.1 数据纵向分割3.2 隐私保护的树…

本地部署到服务器上的资源路径问题

本地部署到服务器上的资源路径问题 服务器端的源代码的静态资源目录层级 当使用Thymeleaf时&#xff0c;在templates的目录下为返回的html页面&#xff0c;下面以两个例子解释当将代码部署到tomcat时访问资源的路径配置问题 例子一 index.html&#xff08;在templates的根目录…

EtherCAT转Profinet网关配置说明第二讲:上位机软件配置

EtherCAT协议转Profinet协议网关模块&#xff08;XD-ECPNS20&#xff09;&#xff0c;不仅可以实现数据之间的通信&#xff0c;还可以实现不同系统之间的数据共享。EtherCAT协议转Profinet协议网关模块&#xff08;XD-ECPNS20&#xff09;具有高速传输的特点&#xff0c;因此通…

安卓安全概述

安卓安全概述 1.Android系统概述2.Android系统安全概述3.Android系统的安全机制应用程序框架安全机制内核安全机制运行环境安全机制 4.Android反编译工具 1.Android系统概述 Android采用层次化系统架构&#xff0c;Google官方公布的标准架构如图所示&#xff0c;自顶而下划分为…

vue事件处理v-on或@

事件处理v-on或 我们可以使用v-on指令&#xff08;简写&#xff09;来监听DOM事件&#xff0c;并在事件触发时执行对应的Javascript。用法&#xff1a;v-on:click"methodName"或click"hander" 事件处理器的值可以是&#xff1a; 内敛事件处理器&#xff1…

【MindSpore学习打卡】应用实践-自然语言处理-基于RNN的情感分类:使用MindSpore实现IMDB影评分类

情感分类是自然语言处理&#xff08;NLP&#xff09;中的一个经典任务&#xff0c;广泛应用于社交媒体分析、市场调研和客户反馈等领域。本篇博客将带领大家使用MindSpore框架&#xff0c;基于RNN&#xff08;循环神经网络&#xff09;实现一个情感分类模型。我们将详细介绍数据…