目录

一、主机字节序列和网络字节序列

二、套接字地址结构

1、IPv4 地址结构 (sockaddr_in)

2、IPv6 地址结构 (sockaddr_in6)

3、通用套接字地址结构 (sockaddr)

4、Unix域套接字地址结构 (sockaddr_un)

5、专用 socket 地址结构

6、套接字地址结构的转换

字符串转二进制地址

二进制地址转字符串

7、端口号的转换

三、网络编程接口

socket

bind

listen

accept

TCP 数据读写:

UDP 数据读写:

close

connect

四、TCP编程流程

代码示例:

服务端

客户端


一、主机字节序列和网络字节序列

主机字节序列(Host Byte Order)指的是计算机在内存中存储多字节数据时的顺序。分为大端字节序和小端字节序,不同的主机采用的字节序列可能不同。

  • 大端字节序是指一个整数的高位字节存储在内存的低地址处,低位字节存储在内存的高地址处。
  • 小端字节序则是指整数的高位字节存储在内存的高地址处,而低位字节则存储在内存的低地址处。

在两台使用不同字节序的主机之间传递数据时,可能会出现冲突。所以,在将数据发送到网络时规定整形数据使用大端字节序,所以也把大端字节序成为网络字节序列。对方接收到数据后,可以根据自己的字节序进行转换。

Linux 系统提供如下 4 个函数来完成主机字节序和网络字节序之间的转换:

#include <arpa/inet.h>
#include <netinet/in.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);  // 网络到主机(短整型)短整型的网络字节序转主机字节序

不同架构的处理器可能使用不同的字节序。x86架构通常是小端序,而网络协议(如TCP/IP)要求使用大端序。因此,跨平台通信时必须进行字节序转换。

二、套接字地址结构

套接字地址结构用于在网络编程中存储通信所需的地址信息。常见的套接字地址结构包括IPv4、IPv6和Unix域套接字地址结构。

1、IPv4 地址结构 (sockaddr_in)

IPv4套接字地址结构定义在<netinet/in.h>中,用于存储IPv4地址和端口号。

struct sockaddr_in {sa_family_t    sin_family; // 地址族,如AF_INETin_port_t      sin_port;   // 16位端口号,网络字节序struct in_addr sin_addr;   // 32位IPv4地址,网络字节序char           sin_zero[8]; // 填充字段,通常置零
};struct in_addr {uint32_t s_addr; // IPv4地址,网络字节序
};

2、IPv6 地址结构 (sockaddr_in6)

IPv6套接字地址结构用于存储IPv6地址和端口号。

struct sockaddr_in6 {sa_family_t     sin6_family;   // 地址族,如AF_INET6in_port_t       sin6_port;     // 16位端口号,网络字节序uint32_t        sin6_flowinfo; // 流信息struct in6_addr sin6_addr;     // 128位IPv6地址,网络字节序uint32_t        sin6_scope_id; // 范围ID
};struct in6_addr {unsigned char s6_addr[16]; // IPv6地址,网络字节序
};

3、通用套接字地址结构 (sockaddr)

socket 网络编程接口中表示 socket 地址的是结构体 sockaddr,通用套接字地址结构用于在函数参数中传递不同类型的地址结构。其定义如下:

struct sockaddr {sa_family_t sa_family; // 地址族char        sa_data[14]; // 协议地址
};
sa_family 成员是地址族类型(sa_family_t)的变量。地址族类型通常与协议族类型对应。常见的协议族和对应的地址族如下图所示:

4、Unix域套接字地址结构 (sockaddr_un)

Unix域套接字用于本地进程间通信,其地址结构定义在<sys/un.h>中。

struct sockaddr_un {sa_family_t sun_family; // 地址族,AF_UNIXchar        sun_path[108]; // 文件路径名
};

5、专用 socket 地址结构

TCP/IP 协议族有 sockaddr_in 和 sockaddr_in6 两个专用 socket 地址结构体,它们分别用于 IPV4 和 IPV6:
/*
sin_family: 地址族 AF_INET
sin_port: 端口号,需要用网络字节序表示
sin_addr: IPV4 地址结构:s_addr 以网络字节序表示 IPV4 地址
*/struct in_addr{u_int32_t s_addr;
};
struct sockaddr_in{sa_family_t sin_family;u_int16_t sin_port;struct in_addr sin_addr;
};
struct in6_addr{unsigned char sa_addr[16]; // IPV6 地址,要用网络字节序表示
};
struct sockaddr_in6{sa_family_t sin6_family; // 地址族:AF_INET6u_inet16_t sin6_port; // 端口号:用网络字节序表示u_int32_t sin6_flowinfo; // 流信息,应设置为 0struct in6_addr sin6_addr; // IPV6 地址结构体u_int32_t sin6_scope_id; // scope ID,尚处于试验阶段
};

6、套接字地址结构的转换

在网络编程中,通常需要将人类可读的IP地址和端口号转换为网络字节序的二进制形式。

字符串转二进制地址

使用inet_pton函数将点分十进制字符串转换为二进制地址。

#include <arpa/inet.h>const char *ip_str = "192.168.1.1";
struct in_addr addr;
inet_pton(AF_INET, ip_str, &addr);
二进制地址转字符串

使用inet_ntop函数将二进制地址转换为点分十进制字符串。

char ip_str[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &addr, ip_str, INET_ADDRSTRLEN);

7、端口号的转换

端口号需要转换为网络字节序(大端序)后使用。

#include <arpa/inet.h>uint16_t port = 8080;
uint16_t net_port = htons(port); // 主机字节序转网络字节序
uint16_t host_port = ntohs(net_port); // 网络字节序转主机字节序

三、网络编程接口

网络编程接口(API,Application Programming Interface)是不同软件系统间进行通信和数据交换的标准化方式。通过网络API,开发者可以调用远程服务、获取数据或执行特定操作,而无需了解底层实现细节。
  • socket

socket()创建套接字,成功返回套接字的文件描述符,失败返回-1

domain: 设置套接字的协议簇, AF_UNIX AF_INET AF_INET6 ;type: 设置套接字的服务类型 SOCK_STREAM SOCK_DGRAM ;protocol: 一般设置为 0,表示使用默认协议

int socket(int domain, int type, int protocol);
  • bind

bind()将 sockfd 与一个 socket 地址绑定,成功返回 0,失败返回-1

sockfd 是网络套接字描述符 ;addr 是地址结构 ;addrlen 是 socket 地址的长度

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
  • listen

listen()创建一个监听队列以存储待处理的客户连接,成功返回 0,失败返回-1

sockfd 是被监听的 socket 套接字 ;backlog 表示处于完全连接状态的 socket 的上限

int listen(int sockfd, int backlog);
  • accept

accept()从 listen 监听队列中接收一个连接,成功返回一个新的连接 socket,该 socket 唯一地标识了被接收的这个连接,失败返回-1

sockfd 是执行过 listen 系统调用的监听 socket ;addr 参数用来获取被接受连接的远端 socket 地址 ;addrlen 指定该 socket 地址的长度

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
  • TCP 数据读写:

recv()读取 sockfd 上的数据,buff 和 len 参数分别指定读缓冲区的位置和大小

send()往 socket 上写入数据,buff 和 len 参数分别指定写缓冲区的位置和数据长度

flags 参数为数据收发提供了额外的控制

ssize_t recv(int sockfd, void *buff, size_t len, int flags);ssize_t send(int sockfd, const void *buff, size_t len, int flags);
  • UDP 数据读写:

recvfrom()读取 sockfd 上的数据,buff 和 len 参数分别指定读缓冲区的位置和大小

src_addr 记录发送端的 socket 地址 ;addrlen 指定该地址的长度 ;sendto()往 socket 上写入数据,buff 和 len 参数分别指定写缓冲区的位置和数据长度 ;dest_addr 指定接收数据端的 socket 地址 ;addrlen 指定该地址的长度

ssize_t recvfrom(int sockfd, void *buff, size_t len, int flags,struct sockaddr* src_addr, socklen_t *addrlen);ssize_t sendto(int sockfd, void *buff, size_t len, int flags,struct sockaddr* dest_addr, socklen_t addrlen);
  • close

close()关闭一个连接,实际上就是关闭该连接对应的 socket

int close(int sockfd);
  • connect

connect()客户端需要通过此系统调用来主动与服务器建立连接,成功返回 0,失败返回-1

sockfd 参数是由 socket()返回的一个 socket。 serv_addr 是服务器监听的 socket 地址 ;addrlen 则指定这个地址的长度

int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen);

四、TCP编程流程

TCP 提供的是面向连接的、可靠的、字节流服务。TCP 的服务器端和客户端编程流程如下:

  • socket()方法是用来创建一个套接字,有了套接字就可以通过网络进行数据的收发。这也是为什么进行网络通信的程序首先要创建一个套接字。创建套接字时要指定使用的服务类型,使用 TCP 协议选择流式服务(SOCK_STREAM)。
  • bind()方法是用来指定套接字使用的 IP 地址和端口。IP 地址就是自己主机的地址,如果主机没有接入网络,测试程序时可以使用回环地址“127.0.0.1”。端口是一个 16 位的整形值,一般 0-1024 为知名端口,如 HTTP 使用的 80 号端口。这类端口一般用户不能随便使用。其次,1024-4096 为保留端口,用户一般也不使用。4096 以上为临时端口,用户可以使用。在Linux 上,1024 以内的端口号,只有 root 用户可以使用。
  • listen()方法是用来创建监听队列。监听队列有两种,一个是存放未完成三次握手的连接,一种是存放已完成三次握手的连接。listen()第二个参数就是指定已完成三次握手队列的长度。
  • accept()处理存放在 listen 创建的已完成三次握手的队列中的连接。每处理一个连接,则accept()返回该连接对应的套接字描述符。如果该队列为空,则 accept 阻塞。
  • connect()方法一般由客户端程序执行,需要指定连接的服务器端的 IP 地址和端口。该方法执行后,会进行三次握手, 建立连接。
  • send()方法用来向 TCP 连接的对端发送数据。send()执行成功,只能说明将数据成功写入到发送端的发送缓冲区中,并不能说明数据已经发送到了对端。send()的返回值为实际写入到发送缓冲区中的数据长度。
  • recv()方法用来接收 TCP 连接的对端发送来的数据。recv()从本端的接收缓冲区中读取数据,如果接收缓冲区中没有数据,则 recv()方法会阻塞。返回值是实际读到的字节数,如果 recv()返回值为 0, 说明对方已经关闭了 TCP 连接。
  • close()方法用来关闭 TCP 连接。此时,会进行四次挥手。

代码示例:

服务端

编辑
​
​
代码示例:
服务端:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>int main(){
int sockfd = socket(AF_INET,SOCK_STREAM,0);//创建套接字
if( sockfd == -1 ){
exit(1);
}
struct sockaddr_in saddr;//定义ipv4地址,
memset(&saddr,0,sizeof(saddr));//清空
saddr.sin_family = AF_INET;//填充地址图
saddr.sin_port = htons(6000);//填充端口,大端
saddr.sin_addr.s_addr = inet_addr("127.0.0.1");//ip地址
int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));//绑定
if( res == -1){
printf("bind err\n");
exit(1);
}
res = listen(sockfd,5);//创建监听队列
if( res == -1){
exit(1);
}
while( 1 ){
struct sockaddr_in caddr;//记录客户端地址
socklen_t len = sizeof(caddr);//计算大小
int c = accept(sockfd,(struct sockaddr*)&caddr,&len);//接受套接字,阻塞
char buff[128] = {0};
int num = recv(c,buff,127,0);//read
printf("buff=%s\n",buff);//接收之后,打印
send(c,"ok",2,0);//write,描述符,带发送的数据,大小,标志位
close(c);
}
close(sockfd);
exit(0);
}

客户端

客户端:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
int main(){
int sockfd = socket(AF_INET,SOCK_STREAM,0);//创建套接字
if( sockfd == -1){
exit(1);
}
struct sockaddr_in saddr;//ipv4地址
memset(&saddr,0,sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(6000);
saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
int res = connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
if( res == -1){
printf("connect err\n");
exit(1);
}
printf("input:\n");
char buff[128] = {0};
fgets(buff,128,stdin);//从键盘获取数据
send(sockfd,buff,strlen(buff)-1,0);
memset(buff,0,128);
recv(sockfd,buff,127,0);
printf("buff=%s\n",buff);
close(sockfd);
exit(0);
}

执行结果:

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

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

相关文章

网页操作自动化解决方案:如何用Browser-Use+CPolar提升企业运营效率

文章目录前言1. 安装Ollama2. Gemma3模型安装与运行3. 虚拟环境准备3.1 安装Python3.2. 安装conda4. 本地部署Brower Use WebUI4.1 创建一个新conda环境4.2 克隆存储库4.3 安装依赖环境4.4 安装浏览器自动化工具4.5 修改配置信息5. 本地运行测试6. 安装内网穿透6.1 配置公网地址…

Pycharm的设置过程

20250802 用于记录pycharm的设置过程 编辑器相关 python语言设置文件注释 在设置的编辑器部分&#xff0c;按照需求设置模板&#xff01; 函数生成注释

GaussDB as的用法

通过使用 SQL&#xff0c;可以为表名称或列名称指定别名&#xff08;Alias&#xff09;。1 别名的作用SQL 别名用于为表或表中的列提供临时名称。 SQL 别名通常用于使列名更具可读性。 SQL 一个别名只存在于查询期间。 提高SQL执行效率与编写SQL代码效率。2 使用别名的场景在下…

Prim算法

一&#xff0c;prim算法逻辑1.理解&#xff1a;克鲁斯卡尔算法关注的是边&#xff0c;普里姆算法关注的是点把图中每个顶点比作孤岛&#xff0c;点亮一座孤岛就可以解锁附近的孤岛每次解锁的点都是离自身最近的点2.普里姆算法流程a.采用邻接矩阵表示&#xff0c;考虑要查找最小…

嵌入式学习之硬件——51单片机 1.0

一、基础知识1.什么是嵌入式&#xff1f;嵌入式以应用为中心&#xff0c;计算机技术为基础&#xff0c;软硬件可裁剪的专用计算机系统&#xff1b;2.嵌入式的应用&#xff1f;消费电子、无人驾驶、储能、新能源........3.嵌入式发展&#xff1f;&#xff08;1&#xff09;第一阶…

51c大模型~合集161

自己的原文哦~ https://blog.51cto.com/whaosoft/14079111 #这家国内公司&#xff0c;在给xx智能技术栈做「通解」 打通机器人智能化的关键&#xff1a;眼脑手。 xx智能&#xff08;Embodied Intelligence&#xff09;是 AI 领域里热度极高的赛道&#xff1a;给大模型…

Linux9 root密码修改

开机按e进入在linux行即quiet后面输入rd.break ctrlx进入内核输入mount -o remount,rw /sysrootchroot /sysrootpasswd root即可修改密码输入touch /.autorelabelexitexit等待即可

提示词增强工程(Prompt Enhancement Engineering)白皮书草稿

提示词增强工程&#xff08;Prompt Enhancement Engineering&#xff09;白皮书草稿 作者&#xff1a; 技术人进化社 Email&#xff1a;2819699195qq.com 日期&#xff1a; 2025年7月30日 1. 引言 随着大型语言模型&#xff08;LLM&#xff09;能力的飞速发展&#xff0c;如何高…

电路元器件

电流单位 电压 电阻单位 电阻的决定式 欧姆定律 交流电和直流电 交流电 串联电路 并联电路 在线模拟器 Circuitjs web 在线电路模拟器 下载

广泛分布于内侧内嗅皮层全层的速度细胞(speed cells)对NLP中的深层语义分析的积极影响和启示

速度细胞&#xff08;Speed Cells&#xff09;作为内侧内嗅皮层&#xff08;MEC&#xff09;的核心神经元&#xff0c;通过编码运动速度信息与网格细胞协同实现动态路径整合。这一神经机制为自然语言处理&#xff08;NLP&#xff09;的深层语义分析提供了以下关键启示和影响&am…

sql中的多表查询

在SQL中&#xff0c;多表查询用于从多个表中组合数据&#xff0c;常见的方法包括 ​连接查询&#xff08;JOIN&#xff09;​​ 和 ​子查询。以下是详细说明和示例&#xff1a;一、连接查询&#xff08;JOIN&#xff09;通过关联字段将多个表的数据合并&#xff0c;分为以下几…

Ruby 面向对象编程深入解析

Ruby 面向对象编程深入解析 引言 Ruby 作为一种动态、解释型、面向对象的语言,自1995年由日本程序员Yukihiro Matsumoto创造以来,凭借其简洁、灵活和强大的面向对象特性,在全球范围内获得了广泛的认可。本文将深入探讨Ruby的面向对象编程(OOP)特性,帮助读者更好地理解和…

Baumer工业相机堡盟工业相机如何通过YoloV8深度学习模型实现围栏羊驼的检测识别(C#代码,UI界面版)

Baumer工业相机堡盟工业相机如何通过YoloV8深度学习模型实现围栏羊驼的检测识别&#xff08;C#代码&#xff0c;UI界面版&#xff09;工业相机使用YoloV8模型实现围栏羊驼的检测识别工业相机通过YoloV8模型实现围栏羊驼的检测识别的技术背景在相机SDK中获取图像转换图像的代码分…

如何利用 rowid 在OceanBase 中处理大表时提效

本文作者&#xff1a;张瑞远&#xff0c;现主要从事电信级IT系统及数据库的规划设计、架构设计、运维实施、运维服务、故障处理、性能优化等工作&#xff0c;曾经从事银行、证券数仓设计、开发、优化类工作&#xff0c;持有Orale OCM,MySQL OCP及国产代表数据库认证。 获得包括…

【从0开始学习Java | 第4篇】类和对象

文章目录&#x1f44f;类和对象的概念什么是类&#xff1f;什么是对象&#xff1f;&#x1f95d;构造方法如何创建一个对象&#xff1f;&#x1f95d;对象内存布局完整应用 - 编写一个类&#xff1a;人&#xff0c;其具备年龄、性别、姓名等基础属性&#xff0c;并实例化一个人…

Synopsys:默认报告精度(report_default_significant_digits变量)

相关阅读 Synopsyshttps://blog.csdn.net/weixin_45791458/category_12812219.html?spm1001.2014.3001.5482 在使用report_timing之类的报告命令时&#xff0c;可以使用-significant_digits选项指定报告的精度&#xff0c;在不使用该选项的情况下&#xff0c;命令使用由repor…

2025年蓝桥杯青少图形化编程国考真题——摆放玩具

编程实现摆放玩具。&#xff08;角色非源素材&#xff09;摆放规则&#xff1a;在方格中摆放玩具&#xff0c;每个方格只能摆放一个&#xff0c;并且如果某个方格中已经摆放了玩具&#xff0c;那么与之上、下、左、右相邻的四个方格中无法再摆放同种玩具。具体要求1&#xff09…

Android 应用的安装流程

安装流程总览&#xff1a; 用户触发安装->系统验证APK的合法性->解析APK元数据->检查权限和存储空间->复制APK到目标位置->生成应用私有数据->注册组件到系统->安装完成 关键步骤&#xff1a; 1.用户触发安装&#xff1a;a.通过应用商店b.通过adb命令c.通…

基于 Amazon Bedrock 与 Anthropic Claude 3 智能文档处理方案:从扫描件提取到数据入库全流程实践

基于 Amazon Bedrock 与 Anthropic Claude 3 智能文档处理方案&#xff1a;从扫描件提取到数据入库全流程实践 文章目录基于 Amazon Bedrock 与 Anthropic Claude 3 智能文档处理方案&#xff1a;从扫描件提取到数据入库全流程实践方案架构前提准备&#xff1a;亚马逊云科技注册…

深入浅出设计模式——创建型模式之单例模式 Singleton

文章目录“天上天下&#xff0c;唯我独尊”——单例模式单例模式简介单例模式结构饿汉式懒汉式客户端示例运行结果单例模式总结构建型模式 Creational Patterns 小结 Summary代码仓库“天上天下&#xff0c;唯我独尊”——单例模式 你能在电脑上调出两个Windows任务管理器吗&a…