【计算机网络】网络编程接口 Socket API 解读(5)
Socket 是网络协议栈暴露给编程人员的 API,相比复杂的计算机网络协议,API 对关键操作和配置数据进行了抽象,简化了程序编程。
本文讲述的 socket 内容源自 Linux man。本文主要对各 API 进行详细介绍,从而更好的理解 socket 编程。
connect
connect() 遵循 POSIX.1 - 2008
1.库
标准 c 库,libc, -lc
2.头文件
<sys/socket.h>
3.接口定义
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
4.接口描述
connect() 系统调用在 sockfd 指定的 socket 上连接 addr 指定的地址,addrlen 参数指定了 addr 的大小,addr 地址格式取决于 socket 的地址空间,可以参考 socket(2)。
如果 socket 是 SOCK_DGRAM 类型,那么 addr 是发送报文的默认地址,也是唯一接收报文的地址。如果 socket 类型是 SOCK_STREAM 或者 SOCK_SEQPACKET,那么这个调用就是尝试和绑定了 addr 地址的 socket 建立连接。
一些协议套接字(比如 UNIX 流套接字)只能成功连接一次。
一些协议套接字(比如 UNIX TCP 套接字和网络数据报套接字)可以多次 connect() 来修改连接。
一些协议套接字(比如 UNIX TCP 套接字和网络数据报套接字)可以通过将 sockaddr 的 sa_family 设置为 AF_UNSPEC 来消除连接,之后 socket 就可以连接到其他地址了。(AF_UNSPEC 在 Linux 2.2 之后支持)。
5.返回值
如果连接或者绑定成功,那么返回 0。
发生错误时,返回 -1,并设置errno 来指示错误类型。
错误值定义如下(这里指示普通 socket 的错误,还可能存在 domain-specific 错误码):
| EACCES | UNIX 域套接字通过路径名唯一标识,并且是套接字文件是没有写权限的,路径中任何一级的搜索权限也是没有的,可以参考 path_resolution(7) |
| EACCES/EPERM | 用户尝试连接到一个广播地址,却没有设置套接字的广播标记,或者请求被防火墙规则拦截了 |
| EACCES | 如果开启了 SELinux 策略,也可能会导致连接被拒绝(比如策略规定 HTTP 代理只能连接到 HTTP 服务器关联的端口,而 HTTP 代理却连接了其他端口) |
| EADDRINUSE | 本地地址已经在用了 |
| EADDRNOTAVAIL | (网络域套接字)sockfd 指定的套接字没有绑定到地址,并且在尝试将其绑定到临时端口时,临时端口用尽了 |
| EAFNOSUPPORT | 地址家族不正确 |
| EAGAIN | 对于非阻塞的 UNIX 域套接字,套接字是非阻塞的,连接无法立即完成。对于其他套接字家族,这个错误标识路由缓存没有足够的条目了 |
| EALREADY | 套接字是非阻塞的,并且之前的连接尝试还没有完成 |
| EBADF | sockfd 不是一个打开的文件描述符 |
| ECONNREFUSED | connect() 操作的流套接字发现没有人在监听对应的远程地址 |
| EFAULT | 套接字结构地址超出用户地址空间 |
| EINPROGRESS | 套接字是非阻塞的,连接不能立即完成。(UNIX 域套接字会返回 EAGAIN)。可以通过 select(2) 或者 poll(2) 查看套接字的可写事件,来确定连接完成。select(2) 指示可写后,使用 getsockopt(2) 来读取 SOL_SOCKET 级的 SO_ERROR 选项,来确定连接完全成功(SO_ERROR 为 0)或者未成功(SO_ERROR 为这里列出来的普通错误)。 |
| EINTR | 系统调用被信号打断 |
| EISCONN | 套接字已经连接 |
| ENETUNREACH | 网络不可达 |
| ENOTSOCK | 文件描述符并没有指向一个套接字 |
| EPROTOTYPE | 该套接字不支持指定的通信协议。这个错误可能在出现在连接一个 UNIX 域报文套接字到一个流套接字 |
| ETIMEDOUT | 连接超时。可能是服务器太忙了以至于无法接收新的连接。注意:当服务器开启 syncookies 时,IP 套接字的超时可能会非常长。 |
6.注意
如果 connect() 失败,那么套接字的状态是未知的。一个易于移植的程序应该关闭该套接字应该再创建一个新套接字,重新连接。
7.代码
这里我们展示下 select() 的用法示例,来将最近几篇内容串起来:
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <errno.h>#define SERVER_PORT 12345#define TRUE 1
#define FALSE 0main (int argc, char *argv[])
{int i, len, rc, on = 1;int listen_sd, max_sd, new_sd;int desc_ready, end_server = FALSE;int close_conn;char buffer[80];struct sockaddr_in6 addr;struct timeval timeout;struct fd_set master_set, working_set;/*************************************************************//* Create an AF_INET6 stream socket to receive incoming *//* connections on *//*************************************************************/listen_sd = socket(AF_INET6, SOCK_STREAM, 0);if (listen_sd < 0){perror("socket() failed");exit(-1);}/*************************************************************//* Allow socket descriptor to be reuseable *//*************************************************************/rc = setsockopt(listen_sd, SOL_SOCKET, SO_REUSEADDR,(char *)&on, sizeof(on));if (rc < 0){perror("setsockopt() failed");close(listen_sd);exit(-1);}/*************************************************************//* Set socket to be nonblocking. All of the sockets for *//* the incoming connections will also be nonblocking since *//* they will inherit that state from the listening socket. *//*************************************************************/rc = ioctl(listen_sd, FIONBIO, (char *)&on);if (rc < 0){perror("ioctl() failed");close(listen_sd);exit(-1);}/*************************************************************//* Bind the socket *//*************************************************************/memset(&addr, 0, sizeof(addr));addr.sin6_family = AF_INET6;memcpy(&addr.sin6_addr, &in6addr_any, sizeof(in6addr_any));addr.sin6_port = htons(SERVER_PORT);rc = bind(listen_sd,(struct sockaddr *)&addr, sizeof(addr));if (rc < 0){perror("bind() failed");close(listen_sd);exit(-1);}/*************************************************************//* Set the listen back log *//*************************************************************/rc = listen(listen_sd, 32);if (rc < 0){perror("listen() failed");close(listen_sd);exit(-1);}/*************************************************************//* Initialize the master fd_set *//*************************************************************/FD_ZERO(&master_set);max_sd = listen_sd;FD_SET(listen_sd, &master_set);/*************************************************************//* Initialize the timeval struct to 3 minutes. If no *//* activity after 3 minutes this program will end. *//*************************************************************/timeout.tv_sec = 3 * 60;timeout.tv_usec = 0;/*************************************************************//* Loop waiting for incoming connects or for incoming data *//* on any of the connected sockets. *//*************************************************************/do{/**********************************************************//* Copy the master fd_set over to the working fd_set. *//**********************************************************/memcpy(&working_set, &master_set, sizeof(master_set));/**********************************************************//* Call select() and wait 3 minutes for it to complete. *//**********************************************************/printf("Waiting on select()...\n");rc = select(max_sd + 1, &working_set, NULL, NULL, &timeout);/**********************************************************//* Check to see if the select call failed. *//**********************************************************/if (rc < 0){perror(" select() failed");break;}/**********************************************************//* Check to see if the 3 minute time out expired. *//**********************************************************/if (rc == 0){printf(" select() timed out. End program.\n");break;}/**********************************************************//* One or more descriptors are readable. Need to *//* determine which ones they are. *//**********************************************************/desc_ready = rc;for (i=0; i <= max_sd && desc_ready > 0; ++i){/*******************************************************//* Check to see if this descriptor is ready *//*******************************************************/if (FD_ISSET(i, &working_set)){/****************************************************//* A descriptor was found that was readable - one *//* less has to be looked for. This is being done *//* so that we can stop looking at the working set *//* once we have found all of the descriptors that *//* were ready. *//****************************************************/desc_ready -= 1;/****************************************************//* Check to see if this is the listening socket *//****************************************************/if (i == listen_sd){printf(" Listening socket is readable\n");/*************************************************//* Accept all incoming connections that are *//* queued up on the listening socket before we *//* loop back and call select again. *//*************************************************/do{/**********************************************//* Accept each incoming connection. If *//* accept fails with EWOULDBLOCK, then we *//* have accepted all of them. Any other *//* failure on accept will cause us to end the *//* server. *//**********************************************/new_sd = accept(listen_sd, NULL, NULL);if (new_sd < 0){if (errno != EWOULDBLOCK){perror(" accept() failed");end_server = TRUE;}break;}/**********************************************//* Add the new incoming connection to the *//* master read set *//**********************************************/printf(" New incoming connection - %d\n", new_sd);FD_SET(new_sd, &master_set);if (new_sd > max_sd)max_sd = new_sd;/**********************************************//* Loop back up and accept another incoming *//* connection *//**********************************************/} while (new_sd != -1);}/****************************************************//* This is not the listening socket, therefore an *//* existing connection must be readable *//****************************************************/else{printf(" Descriptor %d is readable\n", i);close_conn = FALSE;/*************************************************//* Receive all incoming data on this socket *//* before we loop back and call select again. *//*************************************************/do{/**********************************************//* Receive data on this connection until the *//* recv fails with EWOULDBLOCK. If any other *//* failure occurs, we will close the *//* connection. *//**********************************************/rc = recv(i, buffer, sizeof(buffer), 0);if (rc < 0){if (errno != EWOULDBLOCK){perror(" recv() failed");close_conn = TRUE;}break;}/**********************************************//* Check to see if the connection has been *//* closed by the client *//**********************************************/if (rc == 0){printf(" Connection closed\n");close_conn = TRUE;break;}/**********************************************//* Data was received *//**********************************************/len = rc;printf(" %d bytes received\n", len);/**********************************************//* Echo the data back to the client *//**********************************************/rc = send(i, buffer, len, 0);if (rc < 0){perror(" send() failed");close_conn = TRUE;break;}} while (TRUE);/*************************************************//* If the close_conn flag was turned on, we need *//* to clean up this active connection. This *//* clean up process includes removing the *//* descriptor from the master set and *//* determining the new maximum descriptor value *//* based on the bits that are still turned on in *//* the master set. *//*************************************************/if (close_conn){close(i);FD_CLR(i, &master_set);if (i == max_sd){while (FD_ISSET(max_sd, &master_set) == FALSE)max_sd -= 1;}}} /* End of existing connection is readable */} /* End of if (FD_ISSET(i, &working_set)) */} /* End of loop through selectable descriptors */} while (end_server == FALSE);/*************************************************************//* Clean up all of the sockets that are open *//*************************************************************/for (i=0; i <= max_sd; ++i){if (FD_ISSET(i, &master_set))close(i);}
}
相关文章:
【计算机网络】网络编程接口 Socket API 解读(5)
Socket 是网络协议栈暴露给编程人员的 API,相比复杂的计算机网络协议,API 对关键操作和配置数据进行了抽象,简化了程序编程。 本文讲述的 socket 内容源自 Linux man。本文主要对各 API 进行详细介绍,从而更好的理解 socket 编程。…...
手动实现一个bind函数!
原文地址:手动实现一个bind函数! - 知乎 1.bind函数用法 bind()方法用于创建一个新的函数,这个新函数接收的第一个参数代表的就是this,利用bind()函数我就就可以任意改变函数内部的this指向了。 官网的解释: bind()…...
数据结构-时间复杂度/空间复杂度
Hello,好久没有更新了哦,已经开始学习数据结构了,这篇文章呢就是对刚学数据结构所接触到的时间复杂度进行一个分享哦,如果有错误之处,大家记得拍拍我哦~ 既然要讨论时间/空间复杂度,那我们就得知道时间/空…...
英语写作中“展示”、“表明”demonstrate、show、indicate、illustrate的用法
一、demonstrate、show、indicate在论文写作中主要用法是:demonstrate/show/indicate 从句: Sb./Sth. demonstrates/shows/indicates that ……从句中一般表达事实、观点和结论等。 例句: The authors demonstrated/showed/indicated that…...
Redis的java客户端
在Redis官网中提供了各种语言的客户端,地址:https://redis.io/resources/clients/ redis的java客户端 https://redis.io/resources/clients/#java 1.jedis使用 引入依赖 <dependency><groupId>redis.clients</groupId><artifac…...
Android环境配置笔记
文章目录 一、各环境文档二、参考 一、各环境文档 Gradle官方的兼容性文档:Java Compatibility 更新日期:2023.9.12 Android Gradle插件版本:Android Gradle Plugin 二、参考 参考文章:Android问题记录...
element-table 行的拖拽更改顺序(无需下载sortableJs
样例展示:vueelement 通过阅读element文档我们发现element并不提供拖拽相关的api 本博客通过element提供的行类名 注册函数 实现行与行的拖拽 1.设置el-table 的行样式类名 这里是用的是 function <el-table:data"outputData":row-class-name&qu…...
Docker部署jenkins
目录 一、jenkins原理二、Docker部署jenkins1.下载jenkins镜像文件2.查看下载的jenkins镜像3.创建Jenkins挂载目录并授权权限4.创建并启动Jenkins容器5.查看jenkins是否启动成功6.查看docker容器日志7.配置镜像加速8.访问Jenkins页面,输入ip地址加上9000端口9.获取管…...
从0到1学会Git(第三部分):Git的远程仓库链接与操作
写在前面:前面两篇文章我们已经学会了git如何在本地进行使用,这篇文章将讲解如何将本地的git仓库和云端的远程仓库链接起来并使用 为什么要使用远程仓库:因为我们需要拷贝我们的代码给别人以及进行协同开发,就需要有一个云端仓库进行代码的存储和同步&a…...
虚拟机Ubuntu操作系统常用终端命令(1)(详细解释+详细演示)
虚拟机Ubuntu操作系统常用终端命令 本篇讲述了Ubuntu操作系统常用的三个功能,即归档,软链接和用户管理方面的相关知识。希望能够得到大家的支持。 文章目录 虚拟机Ubuntu操作系统常用终端命令二、使用步骤1.归档1.1创建档案包1.2还原档案包1.3归档并压缩…...
redis实战-redis实现异步秒杀优化
秒杀优化-异步秒杀思路 未优化的思路 当用户发起请求,此时会请求nginx,nginx会访问到tomcat,而tomcat中的程序,会进行串行操作,分成如下几个步骤 1、查询优惠卷 2、判断秒杀库存是否足够 3、查询订单 4、校验是否是一…...
Python爬虫-IP隐藏技术与代理爬取
前言 在进行爬虫程序开发和运行时,常常会遇到目标网站的反爬虫机制,最常见的就是IP封禁,这时需要使用IP隐藏技术和代理爬取。 一、IP隐藏技术 IP隐藏技术,即伪装IP地址,使得爬虫请求的IP地址不被目标网站识别为爬虫。…...
二刷力扣--链表
链表 链表类型: 单链表(可以访问后面的一个节点) 双链表(可以访问前后节点) 循环链表(最后一个节点指向首节点) 在Python中定义单链表节点: class ListNode:def __init__(self, v…...
返回值加const ,为了不拷贝得到成员的值,但被赋值的左值也要const
1. getA 函数返回值 什么都不加,也改不了c里面a的指针指向 why?返回成员变量时,会复制一下。 返回成员变量时,一般会赋值一下没有RVO_地摊书贩的博客-CSDN博客 2. getA 函数返回值 加了引用, 就没有复制 3. getA 函数…...
本地如何使用HTTPS进行调试
在现代前端开发中,HTTPS已经成为不可或缺的一部分,因为它在保护用户数据和确保网站安全性方面发挥着关键作用。然而,有时在本地开发过程中启用HTTPS可能会变得有些复杂。在本文中,我们将介绍如何轻松地在本地进行HTTPS调试&#x…...
观察者模式:对象之间的订阅机制
欢迎来到设计模式系列的第十三篇文章!在之前的文章中,我们学习了许多常用的设计模式,今天我们将介绍观察者模式,它是一种行为型设计模式,用于定义对象之间的一对多依赖关系,当一个对象的状态发生变化时&…...
【1462. 课程表 IV】
来源:力扣(LeetCode) 描述: 你总共需要上 numCourses 门课,课程编号依次为 0 到 numCourses-1 。你会得到一个数组 prerequisite ,其中 prerequisites[i] [ai, bi] 表示如果你想选 bi 课程,你…...
Kerberos 身份验证
简介 Kerberos 是一种由 MIT(麻省理工大学)提出的一种基于加密 Ticket 的身份认证协议。它旨在通过使用密钥加密技术为客户端/服务器应用程序提供强身份验证,用于验证用户或主机的标识。。 适用范围:Windows Server 2022、Window…...
R语言贝叶斯METROPOLIS-HASTINGS GIBBS 吉布斯采样器估计变点指数分布分析泊松过程车站等待时间...
原文链接:http://tecdat.cn/?p26578 指数分布是泊松过程中事件之间时间的概率分布,因此它用于预测到下一个事件的等待时间,例如,您需要在公共汽车站等待的时间,直到下一班车到了(点击文末“阅读原文”获取…...
通付盾入选2023年度“上市苗圃工程”重点企业
近日,2023年度苏州工业园区企业上市苗圃工程认定名单公示,江苏通付盾科技有限公司成功入选园区“上市苗圃工程”重点企业。 2023年第一批次苗圃企业认定结果: 企业上市苗圃工程 上市企业是衡量地方综合经济实力的重要标尺,也是区…...
生成xcframework
打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式,可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...
iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘
美国西海岸的夏天,再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至,这不仅是开发者的盛宴,更是全球数亿苹果用户翘首以盼的科技春晚。今年,苹果依旧为我们带来了全家桶式的系统更新,包括 iOS 26、iPadOS 26…...
盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来
一、破局:PCB行业的时代之问 在数字经济蓬勃发展的浪潮中,PCB(印制电路板)作为 “电子产品之母”,其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透,PCB行业面临着前所未有的挑战与机遇。产品迭代…...
三维GIS开发cesium智慧地铁教程(5)Cesium相机控制
一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点: 路径验证:确保相对路径.…...
MySQL 隔离级别:脏读、幻读及不可重复读的原理与示例
一、MySQL 隔离级别 MySQL 提供了四种隔离级别,用于控制事务之间的并发访问以及数据的可见性,不同隔离级别对脏读、幻读、不可重复读这几种并发数据问题有着不同的处理方式,具体如下: 隔离级别脏读不可重复读幻读性能特点及锁机制读未提交(READ UNCOMMITTED)允许出现允许…...
(二)TensorRT-LLM | 模型导出(v0.20.0rc3)
0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述,后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作,其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...
使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装
以下是基于 vant-ui(适配 Vue2 版本 )实现截图中照片上传预览、删除功能,并封装成可复用组件的完整代码,包含样式和逻辑实现,可直接在 Vue2 项目中使用: 1. 封装的图片上传组件 ImageUploader.vue <te…...
Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配
AI3D视觉的工业赋能者 迁移科技成立于2017年,作为行业领先的3D工业相机及视觉系统供应商,累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成,通过稳定、易用、高回报的AI3D视觉系统,为汽车、新能源、金属制造等行…...
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南 在数字化营销时代,邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天,我们将深入解析邮件打开率、网站可用性、页面参与时…...
