第56章:socket介绍

news/2024/5/18 12:36:05 标签: 网络, tcp/ip, udp

socket允许位于同一主机(计算机)或使用网络连接起来的不同主机上的应用程序之间交换数据

概述

在一个典型的客户端/服务器场景中,应用程序使用socket 进行通信的方式如下:

  • 各个应用程序创建一个socket。socket 是一个允许通信的“设备”,两个应用程序都需要用到它。
  • 服务器将自己的socket 绑定到一个众所周知的地址(名称)上使得客户端能够定位到它的位置。
    使用 socket()系统调用能够创建一个socket,它返回一个用来在后续系统调用中引用该socket 的文件描述符。
fd = socket(domain, type, protocol);

在本书介绍的所有应用程序中,protocol 参数总是被指定为0。

通信domain

通信domain可以确定:

  • 识别出一个 socket 的方法(即socket“地址”的格式);
  • 通信范围(即是在位于同一主机上的应用程序之间还是在位于使用一个网络连接起来的不同主机上的应用程序之间)。

现代操作系统支持的domain如下表:

Domain执行的通信应用程序间的通信地址格式地址结构
AF_UNIX内核中同一主机路径名sockaddr_un
AF_INET通过IPv4通过 IPv4 网络连接起来的主机32 位IPv4 地址+16 位端口号sockaddr_in
AF_INET6通过IPv6通过 IPv6 网络连接起来的主机128 位IPv4 地址+16 位端口号sockaddr_in6

socket类型

有流(SOCK_STREAM)和数据报(SOCK_DGRAM)两种类型,在UNIX和Internet domain中都得到支持,属性总结如下表:

属性数据报
可靠地递送?
消息边界保留?
面向连接?

流 socket(SOCK_STREAM)提供了一个可靠的双向的字节流通信信道,一个流socket 只能与一个对等socket 进行连接。

在 Internet domain 中,数据报socket 使用了用户数据报协议(UDP),而流socket 则(通常)使用了传输控制协议(TCP)。

socket系统调用

关键的调用包括以下几种:

  • socket()系统调用创建一个新socket。
  • bind()系统调用将一个socket 绑定到一个地址上。通常,服务器需要使用这个调用来将其socket 绑定到一个众所周知的地址上使得客户端能够定位到该socket 上。
  • listen()系统调用允许一个流socket 接受来自其他socket 的接入连接。
  • accept()系统调用在一个监听流socket 上接受来自一个对等应用程序的连接,并可选地返回对等socket 的地址。
  • connect()系统调用建立与另一个socket 之间的连接。

socket I/O 可以使用传统的read()和write()系统调用或使用一组socket 特有的系统调用(如send()、recv()、sendto()以及recvfrom())来完成。

在 Linux 上可以通过调用ioctl(fd, FIONREAD, &cnt)来获取文件描述符fd 引用的流
socket 中可用的未读字节数。对于数据报socket 来讲,这个操作会返回下一个未读数据报中的字节数(如果下一个数据报的长度为零的话就返回零)或在没有未决数据报的情况下返回0.

创建一个socket: socket()

#include <sys/socket.h>
int socket(int domain, int type, int protocol)
// Returns file descriptor on success, or -1 on error

将socket 绑定到地址:bind()

#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
// Returns 0 on success, or -1 on error
  • addr 参数是一个指针,它指向了一个指定该socket 绑定到的地址的结构。传入这个参数的结构的类型取决于socket domain。
  • addrlen 参数指定了地址结构的大小。addrlen 参数使用的socklen_t 数据类型在SUSv3 被规定为一个整数类型。

通用socket 地址结构:struct sockaddr

每种socket domain 都使用了不同的地址格式,但是诸如bind()之类的系统调用适用于所有socket domain,因此它们必须要能够接受任意类型的地址结构。

为支持这种行为,socket API 定义了一个通用的地址结构struct sockaddr。

这个类型的唯一用途是将各种domain 特定的地址结构转换成单个类型以供socket 系统调用中的各个参数使用。

struct sockaddr
{
	sa_family_t_sa_family; // addr family
	char sa_data[14]; // sock addr (size varies according to socket domain)
};

流socket

流socket系统调用流程如下图:
流socket系统调用

监听接入连接:listen()

#include <sys/socket.h>
int listen(int sockfd, int backlog);
// Returns 0 on success, or -1 on error

无法在一个已连接的socket(即已经成功执行connect()的socket 或由accept()调用返回的socket)上执行listen()。

客户端可能会在服务器调用accept()之前调用connect()。这种情况是有可能会发生的,如服务器可能正忙于处理其他客户端。这将会产生一个未决的连接。内核必须要记录所有未决的连接请求的相关信息,这样后续的accept()就能够处理这些请求了。

backlog 参数允许限制这种未决连接的数量。在这个限制之内的连接请求会立即成功。之外的连接请求就会阻塞直到一个未决的连接被接受(通过accept()),并从未决连接队列删除为止。

SUSv3 规定实现应该通过在<sys/socket.h>中定义SOMAXCONN常量来发布这个限制。在Linux 上,这个常量的值被定义成了128。但从内核2.4.25 起,Linux允许在运行时通过Linux 特有的/proc/sys/net/core/somaxconn 文件来调整这个限制。

接受连接: accept()

#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socktlen_t *addrlen);
// Returns file descriptor on success, or -1 on error

理解 accept()的关键点是它会创建一个新socket,并且正是这个新socket 会与执行connect()的对等socket 进行连接。accept()调用返回的函数结果是已连接的socket 的文件描述符。监听socket(sockfd)会保持打开状态,并且可以被用来接受后续的连接.

连接到对等socket:connect()

#include <sys/socket.h>
int connect(int fd, const struct sockaddr *addr, socklen_t addrlen);
// Returns 0 on success, or -1 on error

流socket I/O

要执行 I/O 需要使用read()和write()系统调用(或在61.3 节中描述的socket 特有的send()和recv()调用)。由于socket 是双向的,因此在连接的两端都可以使用这两个调用。

一个 socket 可以使用close()系统调用来关闭或在应用程序终止之后关闭。

连接终止:close()

终止一个流socket 连接的常见方式是调用close()。如果多个文件描述符引用了同一个socket,那么当所有描述符被关闭之后连接就会终止。

数据报socket

交换数据报:recvfrom 和sendto()

#include <sys/socket.h>

ssize_t recvfrom(int sockfd, void *buffer, size_t length, int flags, struct sockaddr *src_addr, socklen_t *addrlen);

ssize_t sendto(int sockfd, const void *buffer, size_t length, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
// Returns number of bytes sent, or -1 on error

不管 length 的参数值是什么,recvfrom()只会从一个数据报socket 中读取一条消息。如果消息的大小超过了length 字节,那么消息会被静默地截断为length 字节。

在数据报socket 上使用connect()

尽管数据报socket 是无连接的,但在数据报socket 上应用connect()系统调用仍然是起作用的。在数据报socket 上调用connect()会导致内核记录这个socket 的对等socket 的地址。

当一个数据报 socket 已连接之后:

  • 数据报的发送可在socket 上使用write()(或send())来完成并且会自动被发送到同样的对等socket 上。与sendto()一样,每个write()调用会发送一个独立的数据报;
  • 在这个 socket 上只能读取由对等socket 发送的数据报。

注意 connect()的作用对数据报socket 是不对称的。上面的论断只适用于调用了connect()数据报socket,并不适用于它连接的远程socket(除非对等应用程序在其socket 上也调用了connect())

为一个数据报socket 设置一个对等socket的优势:在该socket 上传输数据时可以使用更简单的I/O 系统调用,无需使用指定了dest_addr 和addrlen 参数的sendto(),而只需要使用write()即可

对等socket的应用场景: 主要对那些需要向单个对等socket(通常是某种数据报客户端)发送多个数据报的应用程序是比较有用的。

总结


http://www.niftyadmin.cn/n/359095.html

相关文章

SSM(Spring篇)

Spring Spring的IOC和DI Spring简介 介绍 Spring的分层Java SE\EE应用full-stack轻量级开源框架&#xff0c;以IOC&#xff08;Inverse Of Control&#xff1a;反转控制&#xff09;和AOP&#xff08;Aspect Oriented Programing&#xff1a;面向切面编程&#xff09;为内核…

网络是信息传输网络3

网络是信息传输、接收、共享的虚拟平台&#xff0c;通过它把各个点、面、体的信息联系到一起&#xff0c;从而实现这些资源的共享。网络是人类发展史来最重要的发明&#xff0c;提高了科技和人类社会的发展。 网络会借助文字阅读、图片查看、影音播放、下载传输、游戏、聊天等…

zabbix自定义监控

一、案例操作&#xff1a;自定义监控内容 案列&#xff1a;自定义监控客户端服务器登录的人数 需求&#xff1a;限制登录人数不超过 3 个&#xff0c;超过 3 个就发出报警信息 1、自定义监控内容的操作步骤 1.1 在客户端创建自定义 key 明确需要执行的 linux 命令 who | …

如何优化供应商采购系统,提升供应商管理和采购流程效能

随着企业采购向数字化转型的发展&#xff0c;供应商采购系统的使用也越来越广泛。如何优化供应商采购系统&#xff0c;提升供应商管理和采购流程效能&#xff0c;已成为企业面临的重要问题。本文将为大家介绍一些优化供应商采购系统的方法&#xff0c;以提升采购效率和管理水平…

Faster R-CNN网络架构详解和TensorFlow Hub实现(附源码)

文章目录 一、RPN网络1. RPN网络简介2. backbone网络简介 二、Faster R-CNN网络架构1. Faster R-CNN网络简介2. 基于TensorFlow Hub实现Faster R-CNN 前言&#xff1a;Faster R-CNN的简介见 上一篇文章 一、RPN网络 1. RPN网络简介 RPN网络全称Region Proposal Network&#…

如何下载外文期刊文献,怎么下载又快又省力!

文章开头我们先了解一下下面这些查找外文期刊文献的数据库: 1、Web of Science&#xff1a;是获取全球学术信息的重要数据库。它收录了全球13000多种权威的、高影响力的学术期刊&#xff0c;内容涵盖自然科学、工程技术、生物医学、社会科学、艺术与人文等领域。其中以SCIE、S…

QSerialPort基操

以下是使用QSerialPort的基本步骤&#xff1a;1. 引入QSerialPort头文件 #include <QSerialPort>2. 创建QSerialPort对象 QSerialPort serialPort;3. 设置串口参数 serialPort.setPortName("COM1"); // 设置串口名称 serialPort.setBaudRate(QSerialPort::Baud…

卓越讲坛:当数学遇见话剧——从话剧《无以复伽》谈起

卓越讲坛&#xff1a;当数学遇见话剧——从话剧《无以复伽》谈起 学习过程 刘攀老师在报告中谈及了华东师范大学独具文化教育特色的”数学话剧“&#xff0c;并对其中的两部数学话剧《无以复伽》和《代数旋律的星空》背后的故事进行了解读。 我校从2012年至今已经创作了多部…