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,类似于memsetlocal.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.rhkb.cn/news/370303.html

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

相关文章

网安小贴士(6)TCP/IP分层

一、前言 1983年&#xff0c;美国国防部决定将TCP/IP作为所有计算机网络的标准协议&#xff0c;这标志着TCP/IP正式成为互联网的基础协议。随着个人计算机的普及和网络技术的发展&#xff0c;TCP/IP模型被广泛应用于各种网络环境中&#xff0c;包括局域网&#xff08;LAN&#…

Linux-DNS

DNS域名解析服务 1.DNS介绍 DNS 是域名系统 (Domain Name System) 的缩写&#xff0c;是因特网的一项核心服务&#xff0c;它作为可以将域名和IP地址相互映射的一个分布式数据库&#xff0c;能够使人更方便的访问互联网&#xff0c;而不用去记住能够被机器直接读取的IP数串。…

优化路由,优化请求url

1、使用父子关系调整下使其更加整洁 2、比如说我修改了下url,那所有的页面都要更改 优化&#xff1a;把这个url抽出来&#xff0c;新建一个Api文件夹用于存放所有接口的url&#xff0c;在业务里只需要关注业务就可以 使用时 导包 发请求 如果想要更改路径&#xff0c;在这里…

ctfshow web sql注入 web242--web249

web242 into outfile 的使用 SELECT ... INTO OUTFILE file_name[CHARACTER SET charset_name][export_options]export_options:[{FIELDS | COLUMNS}[TERMINATED BY string]//分隔符[[OPTIONALLY] ENCLOSED BY char][ESCAPED BY char]][LINES[STARTING BY string][TERMINATED…

View->裁剪框View的绘制,手势处理

XML文件 <?xml version"1.0" encoding"utf-8"?> <RelativeLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"match_parent"android:layout_height"match_parent"android…

Linux系统的基础知识和常用命令

1、什么是Linux&#xff1f; 是一种免费使用和自由传播的类UNIX操作系统&#xff0c;其内核由林纳斯本纳第克特托瓦兹于1991年10月5日首次发布&#xff0c;它主要受到Minix和Unix思想的启发&#xff0c;是一个基于POSIX的多用户、多任务、支持多线程和多CPU的操作系统。它能运行…

Flink实现准确和高效流处理的关键问题

时间相关: Watermark 水位线 水位线是插入到数据流中的一个标记,可以认为是一个特殊的数据。水位线主要的内容是一个时间戳,用来表示当前事件时间的进展。水位线是基于数据的时间戳生成的。水位线的时间戳必须单调递增,以确保任务的事件时间时钟一直向前推进,进展。水位线…

Git 运用小知识

1.Git添加未完善代码的解决方法 1.1 Git只是提交未推送 把未完善的代码提交到本地仓库 只需点击撤销提交&#xff0c;提交的未完善代码会被撤回 代码显示未提交状态 1.2 Git提交并推送 把未完善的代码提交并推送到远程仓库 点击【未完善提交并推送】的结点选择还原提交&#x…

【后端面试题】【中间件】【NoSQL】MongoDB查询优化2(优化排序、mongos优化)

优化排序 在MongoDB里面&#xff0c;如果能够利用索引来排序的话&#xff0c;直接按照索引顺序加载数据就可以了。如果不能利用索引来排序的话&#xff0c;就必须在加载了数据之后&#xff0c;再次进行排序&#xff0c;也就是进行内存排序。 可想而知&#xff0c;如果内存排序…

【Oracle】Oracle常用函数

目录 聚合函数数字函数1. ABS函数&#xff1a;返回一个数的绝对值。2. CEIL函数&#xff1a;返回大于等于给定数的最小整数。3. FLOOR函数&#xff1a;返回小于等于给定数的最大整数。4. ROUND函数&#xff1a;将一个数四舍五入到指定的小数位。5. MOD函数&#xff1a;返回两个…

Vue 数据大屏适配

1、准备俩个盒子 .dataScreen-content 盒子内容根据设计稿给的px单位进行正常的布局就行 2、盒子的CSS样式 .dataScreen-container {width: 100%;height: 100%;// 有背景图需要的样式background: url("./images/bg.png") no-repeat;background-repeat: no-repeat;b…

推荐算法学习笔记2.1:基于深度学习的推荐算法-基于共线矩阵的深度推荐算法-AutoRec模型

AutoRec模型 前置知识&#xff1a;推荐算法学习笔记1.1:传统推荐算法-协同过滤算法 AutoRec模型通过引入自编码器结构&#xff0c;将共线矩阵中的用户向量&#xff08;基于用户的U-AutoRec&#xff09;或物品向量&#xff08;基于物品的I-AutoRec&#xff09;嵌入到低维空间后还…

在 PostgreSQL 中,如何处理大规模的文本数据以提高查询性能?

文章目录 一、引言二、理解 PostgreSQL 中的文本数据类型三、数据建模策略四、索引选择与优化五、查询优化技巧六、示例场景与性能对比七、分区表八、数据压缩九、定期维护十、总结 在 PostgreSQL 中处理大规模文本数据以提高查询性能 一、引言 在当今的数据驱动的世界中&…

HashMap中的put()方法

一. HashMap底层结构 HashMap底层是由哈希表(数组),链表,红黑树构成,哈希表存储的类型是一个节点类型,哈希表默认长度为16,它不会每个位置都用,当哈希表中的元素个数大于等于负载因子(0.75)*哈希表长度就会扩容到原来的2倍 二. 底层的一些常量 三. HashMap的put()方法 当插入一…

Linux 系统管理4——账号管理

一、用户账号管理 1、用户账号概述 &#xff08;1&#xff09;用户账号的常见分类&#xff1a; 1>超级用户&#xff1a;root uid0 gid0 权限最大。 2>普通用户&#xff1a;uid>500 做一般权限的系统管理&#xff0c;权限有限。 3>程序用户&#xff1a;1<uid&l…

3.python

闯关 3作业 本节关卡&#xff1a; 学习 python 虚拟环境的安装 Python 的基本语法 学会 vscode 远程连接 internstudio 打断点调试 python 程序

生物化学笔记:电阻抗基础+电化学阻抗谱EIS+电化学系统频率响应分析

视频教程地址 引言 方法介绍 稳定&#xff1a;撤去扰动会到原始状态&#xff0c;反之不稳定&#xff0c;还有近似稳定的 阻抗谱图形&#xff08;Nyquist和Bode图&#xff09; 阻抗谱图形是用于分析电化学系统和材料的工具&#xff0c;主要有两种类型&#xff1a;Nyquist图和B…

Drools开源业务规则引擎(三)- 事件模型(Event Model)

文章目录 Drools开源业务规则引擎&#xff08;三&#xff09;- 事件模型&#xff08;Event Model&#xff09;1.org.kie.api.event2.RuleRuntimeEventManager3.RuleRuntimeEventListener接口说明示例规则文件规则执行日志输出 4.AgentaEventListener接口说明示例监听器实现类My…

Java 7新特性深度解析:提升效率与功能

文章目录 Java 7新特性深度解析&#xff1a;提升效率与功能一、Switch中添加对String类型的支持二、数字字面量的改进三、异常处理&#xff08;捕获多个异常&#xff09;四、增强泛型推断五、NIO2.0&#xff08;AIO&#xff09;新IO的支持六、SR292与InvokeDynamic七、Path接口…

WordPress网站添加插件和主题时潜在危险分析

WordPress 最初只是一个简单的博客软件&#xff0c;现在据估计为全球前 1000 万个网站中的 30% 提供支持。WordPress受欢迎的因素之一是可以轻松创建插件和主题来扩展它并提供比默认设置更多的功能。 目前&#xff0c;WordPress 网站列出了 56,000 多个插件以及数千个主题。插件…