【计算机网络】什么是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)
相关推荐
JZZC21 天前
29. HTTP
计算机网络·http·ensp
报错小能手2 天前
计算机网络自顶向下方法33——网络层 路由器工作原理 输入端口处理和基于目的地转发 交换 输出端口处理
网络·计算机网络·智能路由器
Yurko132 天前
【计网】基于三层交换机和 RIP 协议的局域网组建
网络·学习·计算机网络·智能路由器
L.EscaRC2 天前
【复习408】计算机网络应用层协议详解
计算机网络
报错小能手3 天前
计算机网络自顶向下方法34——网络层 排队论 缓存大小调节 分组调度 网络中立性
计算机网络
0和1的舞者3 天前
网络通信的奥秘:HTTP详解 (六)
网络·网络协议·计算机网络·http·https·计算机科学与技术
甄心爱学习3 天前
计算机网络10
计算机网络
Wish3D3 天前
查看计算机网络端口是被哪个应用占用
计算机网络
磊 子3 天前
计算机网络概述
网络·计算机网络
yy17962610013 天前
计算机网络分层基础概念
计算机网络