【计算机网络】什么是socket编程?以及相关接口详解

💐 🌸 🌷 🍀 🌹 🌻 🌺 🍁 🍃 🍂 🌿 🍄🍝 🍛 🍤

📃个人主页 :阿然成长日记 👈点击可跳转

📆 个人专栏: 🔹数据结构与算法🔹C语言进阶🔹C++🔹Liunx

🚩 不能则学,不知则问,耻于问人,决无长进

🍭 🍯 🍎 🍏 🍊 🍋 🍒 🍇 🍉 🍓 🍑 🍈 🍌 🍐 🍍

文章目录

一、 什么是socket套接字

我们使用一张图片很形象的可以看出socket所处的位置。


从用户层面来看:

从上图看出,Socket处于应用层传输层 的中间软件抽象层,它是一组接口。并且前面我们也学过,运输层和网络层其实属于操作系统内层面,我们只给用户提供使用的接口即可,不可能让用户能直接访问到内核部分。

在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。

从操作系统层面来看:
linux下一切皆文件!!!所以本质来说Socket即是一种特殊的文件,一些socket函数【bind()、listen()等等】,就是对这个文件进行的操作(读/写IO、打开、关闭)

二、Socket函数接口详解

cpp 复制代码
// 创建 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* address_len);

// 建立连接 (TCP, 客户端)
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

上面的参数中带有 struct sockaddr*结构体,这是个什么呢?

首先我们要知道socket的英文为插座,而我们的ip+端口号可以决定是哪一台主机上的哪一个进程,相当于一个插头,谁插入这个socket插槽,谁就可以与之通信。

套接字的分类及struct sockaddr*

1.网络套接字

2.原始套接字

3.unix域间套接字

这个struct sockaddr*结构体是一个父类一般还有两个衍生的子类,他们一个sockaddr_in(inet,网络通信)主要用于网络间的通信同时也支持本地,另一个sockaddr_un(域间套接字)只能在本地通信。而原始套接字可以跨过传输层(TCP/IP协议)访问底层的数据

cpp 复制代码
网络套接字
struct sockaddr_in {
    short int sin_family;  // 地址族,一般为AF_INET(IPv4)和AF_INET6(IPv6)
    unsigned short int sin_port; // 端口号,网络字节序
    struct in_addr sin_addr; // IP地址
    unsigned char sin_zero[8];// 用于填充,使sizeof(sockaddr_in)等于16
};


原始套接字
struct sockaddr {
    sa_family_t sa_family; 		/* address family, AF_xxx */
    char sa_data[14];//sa_data是一个字节数组,用于存储地址和端口号信息的具体内容,具体内容的长度和格式依赖于协议族的不同。
};

unix域间套接字
struct sockaddr_un {
    sa_family_t sun_family;       /* AF_UNIX */
    char sun_path[108];    /* 带有路径的文件名 */
};//通过同一个文件的路径来让进程看到同一份资源

其中还有一个结构体是struct in_addr,这个函数内部如下:

cpp 复制代码
struct in_addr {
  uint32_t    s_addr;   /* address in network byte order */
};

简单来说原始套接字使用了一个sa_data数组将IP和端口号存储在了一起,很多网络编程函数诞生早于IPv4协议,那时候都使用的是sockaddr结构体。但是sockaddr的缺陷是:sa_data把目标地址和端口信息混在一起了。IPv4为了解决这个问题,于是便设计了sockaddr_in专门用于网络通信结构体,把port和ip分开来存储。

sockaddr_in 是用于 IPv4 地址的特定地址结构体。它扩展了 sockaddr,并提供了 IPv4 地址(通过 sin_addr 字段存储)和端口号(通过 sin_port 字段存储)的字段。sockaddr_in 使用的是网络字节序(大端字节序)来存储这些值。如果不知道网络字节序请搜索一下。

1.socket

格式:

cpp 复制代码
int socket(int domain, int type, int protocol);

参数:

  • domain:指定套接字的协议族,如AF_INET(IPv4)或AF_INET6(IPv6)。
    type:指定套接字的类型。
       SOCK_STREAM(面向连接的流套接字)
      SOCK_DGRAM(无连接的数据报套接字)。
    protocol:指定使用的传输协议,通常可以设置为0以自动选择合适的协议。

返回值:返回一个int型的数字来表示套接字

作用创建一个套接字,将domain、type和protocol参数传递给socket()函数以指定套接字的特性,并返回一个int型套接字描述符,用于后续的操作。

2.bind

格式:

cpp 复制代码
int bind(int socket, const struct sockaddr *addr,socklen_t addrlen);

参数:

  • sockfd:要绑定的套接字描述符,也就是socket函数的返回值
  • addr:是一个指向 sockaddr 结构的指针,该结构包含了要绑定的IP地址和端口号。
  • addrlen:指定addr结构体的大小。

返回值

  • bind函数的返回值用于指示操作是否成功:
    如果 bind 函数成功执行,它将返回 0。
    如果 bind 函数执行失败,它将返回 -1,并设置全局变量 errno 以指示错误的具体原因。

作用:bind函数用于将一个套接字(socket)与特定的IP地址和端口号绑定

3.listen

格式:

cpp 复制代码
int listen(int socket, int backlog)

参数:

  • socket:要设置为监听状态的套接字描述符。
  • backlog:指定等待连接队列的最大长度,用于限制同时可以等待处理的连接请求的数目

返回值

  • 成功时:当listen函数成功地将套接字置于监听状态时,它返回0。这表示服务器已经准备好接受客户端的连接请求。
  • 失败时:如果listen函数执行失败,它将返回-1,并设置全局变量errno以指示错误的具体原因。此时,可以通过检查errno的值来确定错误类型,并据此采取相应的错误处理措施。

错误码:

EADDRINUSE: 表示地址已被使用,即尝试监听的端口已被其他进程占用。
EINVAL: 表示无效的参数,可能是因为套接字未绑定地址,或者该套接字已被连接(对于某些类型的套接字,如SOCK_DGRAM,监听操作可能不被允许)。
ENOTSOCK: 表示文件描述符不是一个套接字。
WSAENETDOWN(Windows特有): 表示网络子系统失效。
WSAEINPROGRESS(Windows特有): 表示一个阻塞的套接字调用正在运行中。
WSAEMFILE(Windows特有): 表示无可用文件描述符。
WSAENOBUFS(Windows特有): 表示无可用缓冲区空间。

作用: 将套接字设置为监听状态,开始接受客户端的连接请求。通过指定backlog参数,可以控制连接队列的长度。

4.accept

格式:

cpp 复制代码
int accept(int socket, struct sockaddr* address,
socklen_t* address_len)

参数:

  • sockfd:监听 套接字描述符。
  • addr:用于存储客户端的地址信息的结构体指针,可以是struct sockaddr、struct sockaddr_in或struct sockaddr_in6等类型的指针。
  • addrlen:指向一个整数变量,用于传递addr结构体的大小,并在接受连接后更新为实际的地址长度。

返回值

  • 成功时的返回值

    当accept函数成功接受一个客户端的连接请求时,它会返回一个非负值。这个非负值就是新创建的socket文件描述符或对象的标识符,用于后续的通信操作。

  • 失败时的返回值

    如果accept函数在尝试接受连接请求时遇到错误(例如,监听socket已经被关闭),它将返回-1,并设置全局变量errno以指示错误的具体原因。此时,可以通过检查errno的值来确定错误类型,并据此采取相应的错误处理措施。

作用: 等待并接受客户端的连接请求,并返回一个新的套接字描述符,该描述符用于与客户端进行通信。同时,可以获取客户端的地址信息

5.connet

格式:

cpp 复制代码
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

参数:

  • sockfd:要进行连接的套接字描述符。
  • addr:要连接的目标地址的结构体指针,该结构包含了要绑定的IP地址和端口号。
  • addrlen:指定addr结构体的大小。

返回值

  • 成功时的返回值
    当connect函数成功地将客户端套接字与服务器套接字连接起来时,它返回0。这表示连接已经成功建立,客户端可以继续通过该套接字发送和接收数据。
  • 失败时的返回值
    如果connect函数在尝试建立连接时遇到错误(如网络不可达、服务器未运行、服务器拒绝连接等),它将返回-1,并设置全局变量errno以指示错误的具体原因。此时,可以通过检查errno的值来确定错误类型,并据此采取相应的错误处理措施。

错误码

ECONNREFUSED: 表示连接被远程计算机拒绝。这通常意味着没有服务在目标端口上监听,或者服务器程序拒绝连接请求。
ENETUNREACH: 表示网络不可达。这可能是因为网络线路故障、路由表配置错误或远程主机不可达等原因。
ETIMEDOUT: 表示连接超时。这可能是因为远程主机没有响应连接请求,或者连接请求在传输过程中被延迟或丢失。
EINPROGRESS(在非阻塞模式下): 表示连接操作正在进行中。在非阻塞模式下,connect函数可能无法立即完成连接操作,此时将返回-1,并将errno设置为EINPROGRESS。在这种情况下,需要调用其他函数(如select、poll或epoll)来检查连接是否成功建立。

作用: 与另一个套接字建立连接,通常用于客户端连接服务器。通过指定目标地址和端口号,使套接字能够与目标进行通信。

6.读写操作

以下函数均可读取Socket数据

cpp 复制代码
   ssize_t read(int fd, void *buf, size_t count);
   ssize_t write(int fd, const void *buf, size_t count);

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

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

   ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
   ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);

主要来说说recvfrom和sendto函数

recvfrom参数中的addr的意义就是获取发送发放的信息,便于之后使用sento发送时使用。

7.close()函数

在服务器与客户端建立连接之后,会进行一些读写操作,完成了读写操作就要关闭相应的socket描述字,好比操作完打开的文件要调用fclose关闭打开的文件。

8.字节序转换接口

#include<arpa/inet.h>

cpp 复制代码
uint32_t    htonl(uint32_t hostlong);    //32位整数从主机字节序转换为网络字节序
uint16_t    htons(uint16_t hostshort);   //16位整数从主机字节序转换为网络字节序
uint32_t    ntohl(uint32_t netlong):     //32位整数从网络字节序转换为主机字节序
uint16_t    ntohs(uint16_t netshort);    //16位整数从网络字节序转换为主机字节序

9.IP地址格式转换函数

cpp 复制代码
//点分十进制的IP地址字符串转换成in_addr_t类型
in_addr_t inet_addr(const char *cp)
//将结构struct in_addr中的二进制IP地址转换为一个点分十进制
char *inet_ntoa(struct in_addr in)
相关推荐
幺零九零零6 小时前
【计算机网络】TCP协议面试常考(一)
服务器·tcp/ip·计算机网络
长安初雪10 小时前
Request 和 Response 万字详解
计算机网络·servlet
ZachOn1y12 小时前
计算机网络:运输层 —— 运输层概述
网络·tcp/ip·计算机网络·运输层
黎鹤舞82812 小时前
计算机网络易混淆知识点串记
计算机网络
乌龟跌倒14 小时前
网络层3——IP数据报转发的过程
网络·tcp/ip·计算机网络·智能路由器
程序员小予15 小时前
如何成为一名黑客?小白必学的12个基本步骤
计算机网络·安全·网络安全
安徽京准16 小时前
京准同步:GPS北斗卫星授时服务器发展趋势介绍
服务器·网络·计算机网络·授时服务器·gps北斗卫星授时·北斗授时服务器·ntp网络授时服务器
原野心存16 小时前
网络模型——二层转发原理
网络·计算机网络·智能路由器·二层转发原理
乌龟跌倒20 小时前
网络层5——IPV6
网络·计算机网络·智能路由器
努力当一个优秀的程序员20 小时前
第一章·计算机网络体系结构
计算机网络