【计算机网络】网络编程接口 Socket API 解读(5)

2023-09-12 18:22:08

         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 错误码):

EACCESUNIX 域套接字通过路径名唯一标识,并且是套接字文件是没有写权限的,路径中任何一级的搜索权限也是没有的,可以参考 path_resolution(7)
EACCES/EPERM用户尝试连接到一个广播地址,却没有设置套接字的广播标记,或者请求被防火墙规则拦截了
EACCES如果开启了 SELinux 策略,也可能会导致连接被拒绝(比如策略规定 HTTP 代理只能连接到 HTTP 服务器关联的端口,而 HTTP 代理却连接了其他端口)
EADDRINUSE本地地址已经在用了
EADDRNOTAVAIL(网络域套接字)sockfd 指定的套接字没有绑定到地址,并且在尝试将其绑定到临时端口时,临时端口用尽了
EAFNOSUPPORT地址家族不正确
EAGAIN对于非阻塞的 UNIX 域套接字,套接字是非阻塞的,连接无法立即完成。对于其他套接字家族,这个错误标识路由缓存没有足够的条目了
EALREADY套接字是非阻塞的,并且之前的连接尝试还没有完成
EBADFsockfd 不是一个打开的文件描述符
ECONNREFUSEDconnect() 操作的流套接字发现没有人在监听对应的远程地址
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            0

main (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 解读(6)​​​​​​​

更多推荐

每天一个面试题之类加载机制、spirngboot的启动机制

jvm类加载机制Java虚拟机(JVM)的类加载机制是Java的关键部分,它负责加载、链接和初始化类。类加载机制的主要任务是将Java类的字节码文件转换为可以在JVM上执行的运行时数据结构。这个过程包括以下三个主要步骤:加载(Loading):在此阶段,类加载器负责查找并加载类的字节码文件。这个过程通常从类路径(Cla

搭建私人图床结合内网穿透实现公网访问,让您的摄影作品连接世界

文章目录1.树洞外链网站搭建1.1下载安装树洞外链1.2树洞外链网页测试1.3cpolar的安装和注册2.本地网页发布2.1Cpolar临时数据隧道2.2Cpolar稳定隧道(云端设置)2.3Cpolar稳定隧道(本地设置)3.公网访问测试社交平台具有庞大的用户基础和活跃的社交功能,我们将图片发布到社交平台可以让照片更

Learn Prompt-ChatGPT 精选案例:内容总结

ChatGPT可以通过分析内容并生成一个浓缩版本来总结文本。这对节省时间和精力很有帮助,特别是在阅读长篇文章、研究论文或报告时。通用总结​你所要做的就是把具体的文字复制并粘贴到提示中,并要求ChatGPT对所选文本进行简化总结。这里我们参考openai官网提供的例子Summarizefora2ndgrader来总结一下

想学嵌入式开发,薪资怎么样?

想学嵌入式开发,薪资怎么样?对于嵌入式工程师来说呢,它重点学习内容就是首先一定要打好基础,如果从编程语言角度来讲,那么可以在语言上选C或者C++,你可以选择其中任何一门语言作为你的入门。最近很多小伙伴找我,说想要一些嵌入式机学习资料,然后我根据自己从业十年经验,熬夜搞了几个通宵,精心整理了一份「嵌入式入门到高级教程+工

思腾云计算

全新一代的Atlas是支持ARM架构和X86架构的,像Intel,AMD,海光,鲲鹏,飞腾的CPU都支持。Atlas300IPro,是基于昇腾310芯片开发推理卡,最高功耗72W,被动散热,半高半长单宽,达芬奇架构,作为推理卡需求比较简单,算力和显存平衡就可,所以它支持FP16*70TFLOPS和INT8*140TOP

扩展pytest接口自动化框架-MS数据解析功能

【软件测试行业现状】2023年了你还敢学软件测试?未来已寄..测试人该何去何从?【自动化测试、测试开发、性能测试】开篇MeterSphere的数据源通过html页面上传后,需要将请求方式进行拆分。get接口的参数,常以params的方式进行传参,也就是在url后带上参数。post接口一般是以json字符串的形式传参,也

有效的网络带宽监控策略

世界各地的企业正在采用多种策略来减少瓶颈、增强网络性能并最大限度地提高投资回报率,以跟上不断发展的混合基础架构的步伐。虽然这些策略因组织而异,并提供了自己的好处,但它们可能会使IT基础架构的监控方式复杂化。在设计有效的监控策略时,必须了解各个组件的网络吞吐量、带宽、流量活动、运行状况和性能以及整个网络。网络带宽监视使网

【前端知识】Three 学习日志(三)—— 光源对物体表面的影响

Three学习日志(三)——光源对物体表面的影响一、设置材质为受光照影响//MeshLambertMaterial受光照影响constmaterial=newTHREE.MeshLambertMaterial();此时,场景中一片漆黑,无法看到原来的物体,需要设置光源来照亮物体。二、设置点光源//点光源:两个参数分别表

华为云云耀云服务器L实例使用教学 | 访问控制-安全组配置规则 实例教学

文章目录访问控制-安全组什么叫安全组安全组配置默认安全组配置安全组配置实例安全组创建安全组模板配置安全组模板:通用Web服务器配置安全组规则安全组配置规则功能介绍修改允许特定IP地址访问Web80端口服务建立仅允许访问特定目的地址的安全规则配置网络ACL对实例应用安全组安全组和网络ACL规则验证测试总结华为云耀云服务器

嵌入式MCU学习利器-03-在线做RT-Thread实验

嵌入式MCU学习利器-03-在线做RT-Thread实验很多学生想要学习RT-Thread,但是苦于没有好的学习工具或者物理开发板而选择放弃。现在福利来了,同学们可以基于我们的仿真平台做嵌入式demo,通过调试功能深入学习RT-Thread的原理。本仿真平台基于STM32F103ZE芯片上线了一套RT-Thread课程

【深度学习】Pytorch 系列教程(一):PyTorch数据结构:1、Tensor(张量):维度(Dimensions)、数据类型(Data Types)

目录一、前言二、实验环境三、PyTorch数据结构0、分类1、Tensor(张量)1.维度(Dimensions)0维(标量)1维(向量)2维(矩阵)3维张量2.数据类型(DataTypes)一、前言ChatGPT:PyTorch是一个开源的机器学习框架,广泛应用于深度学习领域。它提供了丰富的工具和库,用于构建和训练各

热文推荐