SOCKET编程(3):相关结构体与函数

相关结构体与函数

sockaddr、sockaddr_in结构体

sockaddr和sockaddr_in详解

struct sockaddr共16字节,协议族(family)占2字节,IP地址和端口号在sa_data字符数组中

/* Structure describing a generic socket address.  */
struct sockaddr
{__SOCKADDR_COMMON(sa_); /* Common data: address family and length.  */char sa_data[14];       /* Address data.  */
};#define	__SOCKADDR_COMMON(sa_prefix) \sa_family_t sa_prefix##family

struct sockaddr_in更细致地划分了协议族、端口号和IP地址,其中IP地址定义了新的结构体struct in_addr该结构体中宏定义了uint32_t类型的变量,sin_zero字符数组存在的意义是为了使struct sockaddr_instruct sockaddr 大小相等,便于进行强制类型转换(与bind()等函数的参数有关)

/* Structure describing an Internet socket address.  */
struct sockaddr_in
{__SOCKADDR_COMMON(sin_);in_port_t sin_port;      /* Port number.  */struct in_addr sin_addr; /* Internet address.  *//* Pad to size of `struct sockaddr'.  */unsigned char sin_zero[sizeof(struct sockaddr) -__SOCKADDR_COMMON_SIZE -sizeof(in_port_t) -sizeof(struct in_addr)];
};/* Internet address.  */
typedef uint32_t in_addr_t;
struct in_addr
{in_addr_t s_addr;
};

总结

  1. 二者长度一样,都是16个字节,即占用的内存大小是一致的,因此可以互相转化。二者是并列结构,指向sockaddr_in结构的指针也可以指向sockaddr
  2. sockaddr常用于bind、connect、recvfrom、sendto等函数的参数,指明地址信息,是一种通用的套接字地址
  3. sockaddr_in 是internet环境下套接字的地址形式
  4. 在网络编程中我们会对sockaddr_in结构体进行操作,使用sockaddr_in来建立所需的信息,最后使用类型转化
  5. 一般先把sockaddr_in变量赋值后,强制类型转换后传入用sockaddr做参数的函数:sockaddr_in用于socket定义和赋值;sockaddr用于函数参数

socket()函数

socket函数用于创建一个新的socket,也就是向系统申清一个socket资源

socket函数用户客户端和服务端

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

domain:协议域,又称协议族(family)。常用的协议族有AF INET、AF INET6、AF LOCAL(或称AF UNIX,Unix域Socket)、AF ROUTE等。协议族决定了socket的地址类型,在通信中必须采用对应的地址,如AF INET决定了要用ipv4地址(32位的)与端口号(16位的)的组合、AF_UNIX决定了要用一个绝对路径名作为地址

type:指定socket类型。常用的socket类型有SOCK_STREAM、SOCK_DGRAM、SOCK RAW、SOCK_PACKET、SOCK_SEQPACKET等。流式socket(SOCK_STREAM)是一种面向连接的socket,针对于面向连接的TCP服务应用。数据报式socket(SOCK_DGRAM)是一种无连接的socket,对应于无连接的UDP服务应用

protocol:指定协议。常用协议有IPPROTO_TCP、IPPROTO_UDP、IPPROTO_STCP、IPPROTO_TIPC等,分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议

💡 第一个参数只能填AF INET,第二个参数只能填SOCK STREAM,第三个参数只填0

除非系统资料耗尽,socket函数一般不会返回失败

返回值:成功则返回一个socket,失败返回-1,错误原因存于errno中

💡 “资源耗尽”即Linux对打开文件数的限制,见2022-06-10笔记

// 第1步:创建服务端的socket
int listenfd;
for (int i = 0; i < 2000; ++i)
{if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){perror("socket");return -1;}cout << "sock id:" << listenfd << endl;
}
sock id:1012
sock id:1013
sock id:1014
sock id:1015
sock id:1016
sock id:1017
sock id:1018
sock id:1019
sock id:1020
sock id:1021
sock id:1022
sock id:1023
socket: Too many open files

inet_addr() 和inet_ntoa()函数

使用socket进行通信的时候,我们需要指定三个元素:通信域(地址族)、IP地址、端口号,这三个元素由SOCKADDR_IN结构体定义

为了简化编程一般将IP地址设置为INADDR_ANY,如果需要使用特定的IP地址则需要使用inet_addr()** 和**inet_ntoa()函数

inet_addr()inet_ntoa()完成字符串和in_addr结构体的互换

inet_addr()函数参数cp代表点分十进制的IP地址,如1.2.3.4,返回值为in_addr_t 类型

/* Convert Internet host address from numbers-and-dots notation in CPinto binary data in network byte order.  */
extern in_addr_t inet_addr (const char *__cp) __THROW;

inet_ntoa() 函数输入为in_addr结构体而输出为字符串

/* Convert Internet number in IN to ASCII representation.  The return valueis a pointer to an internal array containing the string.  */
extern char *inet_ntoa (struct in_addr __in) __THROW;

hostent结构体

hostent实例详解

/* Description of data base entry for a single host.  */
struct hostent
{char *h_name;			/* Official name of host.  */char **h_aliases;		/* Alias list.  */int h_addrtype;		/* Host address type.  */int h_length;			/* Length of address.  */char **h_addr_list;		/* List of addresses from name server.  */
#ifdef __USE_MISC
# define	h_addr	h_addr_list[0] /* Address, for backward compatibility.*/
#endif
};
struct hostent{char *h_name;         //正式主机名char **h_aliases;     //主机别名int h_addrtype;       //主机IP地址类型:IPV4-AF_INETint h_length;		      //主机IP地址字节长度,对于IPv4是四字节,即32位char **h_addr_list;	  //主机的IP地址列表};#define h_addr h_addr_list[0]   //保存的是IP地址
  • h_name:官方域名(Official domain name)。官方域名代表某一主页,但实际上一些著名公司的域名并未用官方域名注册
  • h_aliases:别名,可以通过多个域名访问同一主机。同一 IP 地址可以绑定多个域名,因此除了当前域名还可以指定其他域名
  • h_addrtype:gethostbyname() 不仅支持 IPv4,还支持 IPv6,可以通过此成员获取IP地址的地址族(地址类型)信息,IPv4 对应 AF_INET,IPv6 对应 AF_INET6
  • h_length:保存IP地址长度。IPv4 的长度为 4 个字节,IPv6 的长度为 16 个字节
  • h_addr_list:这是最重要的成员。通过该成员以整数形式保存域名对应的 IP 地址。对于用户较多的服务器,可能会分配多个 IP 地址给同一域名,利用多个服务器进行均衡负载

在这里插入图片描述

在实际的应用中,一台服务器往往有好几个IP地址,而域名只有一个,这样设计的好处是,可以使系统分布设计,提升服务器的稳定性和抗灾难能力

一般对服务器的访问,则是先经过DNS(Domain Name System)服务器,DNS通过均衡设计,返回合适的IP与客户端进行交互,避免客户端只连接一个IP,导致网络拥堵

gethostbyname()函数

gethostbyname()函数:通过域名获取IP地址

gethostbyname()函数详解

客户端中直接使用IP地址会有很大的弊端,一旦IP地址变化(IP地址会经常变动),客户端软件就会出现错误
而使用域名会方便很多,注册后的域名只要每年续费就永远属于自己的,更换IP地址时修改域名解析即可,不会影响软件的正常使用

域名仅仅是 IP 地址的一个助记符,目的是方便记忆,通过域名并不能找到目标计算机,通信之前必须要将域名转换成 IP 地址

gethostbyname() 函数可以完成这种转换

/* Return entry from host data base for host with NAME.This function is a possible cancellation point and therefore notmarked with __THROW.  */
extern struct hostent *gethostbyname (const char *__name);

bind()函数

服务端用于将把用于通信的地址和端口绑定到socket上

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

sockfd:需要绑定的socket

addr:存放了服务端用于通信的地址和端口

addrlen:表示addr结构体的大小

返回值:成功则返回0,失败返回-1,错误原因存于errno中

如果绑定的地址错误,或端口已被占用,bind函数一定会报错,否则一般不会返回错误

💡 注意第二个参数为sockaddr结构体指针

在这里插入图片描述

listen()函数

listen函数使用主动连接套接口变为被连接套接口,使得一个进程可以接受其它进程的请求,从而成为一个服务器进程

在TCP服务器编程中listen函数把进程变为一个服务器,并指定相应的套接字变为被动连接

/* Prepare to accept connections on socket FD.N connection requests will be queued before further requests are refused.Returns 0 on success, -1 for errors.  */
extern int listen (int __fd, int __n) __THROW;

_fd:服务端的socket,标识绑定的,未连接的套接字的描述符

-n:挂起的连接队列的最大长度

  • 比如有100个用户链接请求,但是系统一次只能处理20个,那么剩下的80个不能不理人家,所以系统就创建个队列记录这些暂时不能处理,一会儿处理的连接请求,依先后顺序处理,那这个队列到底多大?就是这个参数设置,比如2,那么就允许两个新链接排队。这个不能无限大,那内存就不够了
  • 可以手动设置这个参数,但是别太大
  • 我们一般填写这个参数为SOMAXCONN ,让系统自动选择最合适的个数

connect()函数

#include <sys/types.h> 					
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);参数:
第一个参数:int sockdf:socket文件描述符
第二个参数: const struct sockaddr *addr:传入参数,指定服务器端地址信息,含IP地址和端口号
第三个参数:socklen_t addrlen:传入参数,传入sizeof(addr)大小
返回值:成功: 0失败:-1,设置errno

当客户端调用 connect()函数之后,发生一下情况之一才会返回(完成函数调用)

  • 服务器端接收连接请求
  • 发生断网的异常情况而终端连接请求

需要注意的是,所谓的“接收连接”并不意味着服务器调用 accept()函数,其实是服务器端把连接请求信息记录到等待队列,因此 connect()函数返回后并不进行数据交换,而是要等服务器端 accept 之后才能进行数据交换(read、write)

客户端端需要调用connect()连接服务器,connect和bind的参数形式一致,区别在于bind的参数是自己的地址,而connect的参数是对方的地址

accept()函数

socket的accept函数解析

socket中accept()函数的理解

/* Await a connection on socket FD.When a connection arrives, open a new socket to communicate with it,set *ADDR (which is *ADDR_LEN bytes long) to the address of the connectingpeer and *ADDR_LEN to the address's actual length, and return thenew socket's descriptor, or -1 for errors.This function is a cancellation point and therefore not marked with__THROW.  */
extern int accept (int __fd, __SOCKADDR_ARG __addr,socklen_t *__restrict __addr_len);

在这里插入图片描述

send()函数

send函数用于把数据通过socket发送给对端

不论是客户端还是服务端,应用程序都用send函数来向TCP连接的另一端发送数据

ssize_t send(int sockfd, const void *buf, size_t len, int flags);
  • sockfd为已建立好连接的socket
  • buf为需要发送的数据的内存地址,可以是C语言基本数据类型变量的地址,也可以数组、结构体、字符串,内存中有什么就发送什么
  • len需要发送的数据的长度,为buf中有效数据的长度
  • flags填0,其他数值意义不大
  • 函数返回已发送的字符数,出错时返回-1,错误信息errno被标记
  • 注意,就算是网络断开,或socket已被对端关闭,send函数不会立即报错,要过几秒才会报错
  • 如果send函数返回的错误(<=0),表示通信链路已不可用

recv()函数

recv函数用于接收对端通过socket发送过来的数据

不论是客户端还是服务端,应用程序都用recv函数接收来自TCP连接的另一端发送过来数据

ssize_t recv(int sockfd, void *buf, size_t len, int flags);

参数信息同send()函数

函数返回已接收的字符数,出错时返回-1,失败时不会设置errno的值

  • 如果socket的对端没有发送数据,recv函数就会等待
  • 如果对端发送了数据,函数返回接收到的字符数
  • 出错时返回-1,如果socket被对端关闭,返回值为0
  • 如果recv函数返回的错误(<=0),表示通信通道已不可用

💡 数据收发时的数据量会受到发送缓冲区和接收缓冲区大小的限制
数据收发时要注意字节序的问题(不同主机字节序的问题)

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

基于Spring Cloud的房产销售平台设计与实现

基于Spring Cloud的房产销售平台设计与实现 开发语言&#xff1a;Java 框架&#xff1a;SpringCloud JDK版本&#xff1a;JDK1.8 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea 系统部分展示 前台首页界面&#xff0c;前台首页包括房源信息、…

LabVIEW学习记录4-局部变量、全局变量、共享变量

【LabVIEW】局部变量、全局变量、共享变量 一、变量定义二、内存分配三、竞争状态四、变量创建及简单使用示例4.1 局部变量4.1.1 局部变量的创建4.1.2 局部变量的编程实例 4.2 全局变量4.2.1 创建4.2.2 调用4.2.3 编程实例 4.3 共享变量 一、变量定义 LabVIEW&#xff08;Labor…

三层交换机静态路由连通实验

静态路由是一种手动配置路由表的方式&#xff0c;网络管理员需要手动指定网络中的每一个路由器下一跳路由器的地址&#xff0c;以及到达目的网络的最短路径。静态路由的路由表不会自动更新&#xff0c;如果网络拓扑发生了变化&#xff0c;管理员需要手动更改路由表。 实验拓扑图…

如何使用Vite快速构建vue项目

1、在自己定义的目录下打开cmd命令窗口&#xff1a;如文件夹目录上面输入cmd回车就可以打开 2、检查 node环境&#xff1a;通过node --version看版本号表示安装好了 3、 使用Vite 快速构建Vue项目 npm init vitelatest qiuqiu.admin 注意&#xff1a;如何你电脑没有装vite首…

python 根据网址和关键词批量下载影像

最近用到了GLASS的LAI产品&#xff0c;但这个产品的文件夹分得很细&#xff0c;我需要的影像又有8个瓦片&#xff0c;一个一个点击很麻烦&#xff0c;于是探索了批量下载的方法 一、下载1幅 import requests import re import os import requests import re# 网页URLurl &…

数据挖掘(二)数据预处理

前言 基于国防科技大学 丁兆云老师的《数据挖掘》 数据挖掘 数据挖掘&#xff08;一&#xff09;数据类型与统计 2、数据预处理 2.1数据清理 缺失值处理&#xff1a; from sklearn.impute import SimpleImputer# 创建一个SimpleImputer对象&#xff0c;指定缺失值的处理策略…

螺栓扭矩如何设计?——SunTorque智能扭矩系统

螺栓扭矩设计的大小是一个涉及工程实践的重要问题&#xff0c;它直接关系到螺栓连接的紧固质量和安全性。螺栓扭矩是工程领域中常用的一个概念&#xff0c;用来描述螺栓在连接过程中所需的旋转力矩。正确的螺栓扭矩可以确保螺栓和螺母之间的紧密连接&#xff0c;避免由于松动而…

Java基于Spring Boot+Vue框架的大学生就业招聘系统(附源码,说明文档)

博主介绍&#xff1a;✌IT徐师兄、7年大厂程序员经历。全网粉丝15W、csdn博客专家、掘金/华为云//InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#x1f3…

crmeb的分销推广如何用

CRMBE分销推广说明 1、CRMEB分销模式 分销模式&#xff1a; 指定分销、人人分销、满额分销 指定分销&#xff1a; 用户默认无分销权限&#xff0c;需要后台开通分销权限后&#xff0c;才可以通过推广下级获得返佣&#xff1b; 人人分销&#xff1a; 用户在商城注册后自动获得分…

防雷防浪涌电路设计

通信线路或者电源线路通常会铺设到户外&#xff0c;一旦线路铺到户外后&#xff0c;就需要考虑防雷的问题了&#xff0c;那么怎么设计保护电路&#xff0c;能够防止雷电等浪涌对电路的破坏呢&#xff1f; 通信线路或者电源线路通常会铺设到户外&#xff0c;一旦线路铺到户外后&…

Istio 流量管理(请求路由、流量转移、请求重试、流量镜像、故障注入、熔断等)介绍及使用

一、Istio 流量管理 Istio是一个开源的服务网格&#xff0c;它为分布式微服务架构提供了网络层的抽象。它使得服务之间的通信变得更为可靠、安全&#xff0c;并且提供了细粒度的流量管理、监控和策略实施功能。Istio通过在服务之间插入一个透明的代理&#xff08;Envoy&#x…

数据库入门(sql文档+命令行)

一.基础知识 1.SQL&#xff08;Structured Query Language&#xff09;结构化查询语言分类&#xff1a; DDL数据定义语言用来定义数据库对象&#xff1a;数据库、表、字段DML数据操作语言对数据库进行增删改查DQL数据查询语言查询数据库中表的信息DCL数据控制语言用来创建数据…

Yolov8目标检测——在Android上部署Yolov8 tflite模型

1. 简介 YOLOv8 是一种用于目标检测的深度学习模型&#xff0c;它是 YOLO&#xff08;You Only Look Once&#xff09;系列的最新版本之一。YOLO 系列因其高效和准确性而在计算机视觉领域非常受欢迎&#xff0c;特别是在需要实时目标检测的应用中&#xff0c;如视频监控、自动…

白银基础知识:现货白银的单位是什么?

近期现货白银行情暂停了上涨&#xff0c;进入了回调阶段&#xff0c;这反而让更多人寻求买入白银的机会。如果是我国的投资者&#xff0c;要购买现货白银、进行现货白银投资&#xff0c;有一些问题就要注意的。就计算单位来说&#xff0c;现货白银就和我国的不同。 在我国买白银…

CUDA C编程:第一个程序 向量相加点积

我的电脑没有装CUDA&#xff0c;所以使用租了带GPU的云服务器&#xff0c;然后使用vscode SSH远程连接云服务器。云GPU使用的是智星云&#xff0c;0.8元/h。 智星云 可以使用nvcc --version查看系统中安装的CUDA版本。 然后写第一个CUDA程序&#xff0c;两个向量相加结果给到…

Windows:管理用户账户,密码策略和安全配置

在Windows操作系统中&#xff0c;管理用户账户和密码策略是确保系统安全的关键步骤。本文将探讨如何通过PowerShell和其他Windows工具管理用户账户&#xff0c;包括查看和设置密码策略、检查用户状态&#xff0c;以及导出和导入安全策略。这些管理任务对于系统管理员尤其重要&a…

动态表名 的使用方法

动态表名插件的底层是 拦截器 1&#xff0c;创建一个拦截器 Configuration public class MybatisConfiguration {Beanpublic DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor() {// 准备一个Map&#xff0c;用于存储TableNameHandlerMap<String, Table…

centos7.9升级4.19内核

centos默认的内核版本是3.10 通过命令 uname -a 输出系统的详细信息 在部署k8s集群时使用默认的3.10版本的内核&#xff0c;容易出各种奇奇怪怪的问题、可以理解为docker和k8s与该内核版本不兼容&#xff0c;所以在部署k8s集群时&#xff0c;务必要升级内核&#xff0c;这里…

【用文本生成歌声】Learn2Sing 2.0——歌声转换算法及梅尔频谱详解

一. 频谱图与梅尔谱图的介绍 频谱图&#xff1a;频谱图可以理解为一堆垂直堆叠在一起的快速傅里叶变换结果。 1.1 信号 在进入频谱图模块之前&#xff0c;首先我们需要了解信号是什么。 信号就是某一特定量随时间变化&#xff0c;对于音频来说&#xff0c;这个特定的变化量就…

死锁的概念与处理策略

目录 一. 死锁的概念1.1 什么是死锁1.2 死锁、饥饿、死循环的区别1.3 死锁产生的条件*1.4 什么情况下会导致死锁 二. 死锁的处理策略- -预防死锁2.1 破坏互斥条件2.2 破坏不剥夺条件2.3 破坏 请求和保持条件2.4 破坏循环等待条件 三. 死锁的处理策略- -避免死锁(重要)3.1 什么是…