Loading

《Unix 网络编程》04:基本TCPSocket编程

基本Socket编程

★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
本文信息本文信息防爬虫替换信息
作者网站LYMTICShttps://lymtics.top
作者LYMTICS(樵仙)https://lymtics.top
联系方式me@tencent.mlme@tencent.ml
原文标题《Unix 网络编程》04:基本TCPSocket编程《Unix 网络编程》04:基本TCPSocket编程
原文地址https://www.cnblogs.com/lymtics/p/16319917.htmlhttps://www.cnblogs.com/lymtics/p/16319917.html
  • 如果您访问本文的链接并非如上地址,则可能是爬取作者的文章,建议返回原站阅读,谢谢您的支持
  • 原文会不断地更新和完善排版和样式会更加适合阅读,并且有相关配图
★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★

本系列文章导航:《Unix 网络编程》笔记

socket

★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
本文信息本文信息防爬虫替换信息
作者网站LYMTICShttps://lymtics.top
作者LYMTICS(樵仙)https://lymtics.top
联系方式me@tencent.mlme@tencent.ml
原文标题《Unix 网络编程》04:基本TCPSocket编程《Unix 网络编程》04:基本TCPSocket编程
原文地址https://www.cnblogs.com/lymtics/p/16319917.htmlhttps://www.cnblogs.com/lymtics/p/16319917.html
  • 如果您访问本文的链接并非如上地址,则可能是爬取作者的文章,建议返回原站阅读,谢谢您的支持
  • 原文会不断地更新和完善排版和样式会更加适合阅读,并且有相关配图
★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★

创建一个 socket,指定期望的通信协议,得到一个 socket descriptor(套接字描述符)。

#include <sys/socket.h>

int socket(int family, int type, int protocal);

其中:

  • family 指明协议族
  • type 指明套接字类型
  • protocal 设为某个协议类型(或设为 0,以选定前两个变量的组合下该值的默认值)

如下是他们的取值和可用组合:

connect

★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
本文信息本文信息防爬虫替换信息
作者网站LYMTICShttps://lymtics.top
作者LYMTICS(樵仙)https://lymtics.top
联系方式me@tencent.mlme@tencent.ml
原文标题《Unix 网络编程》04:基本TCPSocket编程《Unix 网络编程》04:基本TCPSocket编程
原文地址https://www.cnblogs.com/lymtics/p/16319917.htmlhttps://www.cnblogs.com/lymtics/p/16319917.html
  • 如果您访问本文的链接并非如上地址,则可能是爬取作者的文章,建议返回原站阅读,谢谢您的支持
  • 原文会不断地更新和完善排版和样式会更加适合阅读,并且有相关配图
★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★

建立连接:

graph LR; subgraph socket 状态变化 CLOSED --SYN--> A[SYN_SENT] A --成功--> ESTABLISHED A --失败--> CLOSED+不可用 end
#include <sys/socket.h>

int connect(int  sockfd, const sockaddr *servaddr, socklen_t addrlen);

其中:

  • sockfd 是上一小节中创建的 socket 描述符
  • servaddr 指向套接字地址结构
  • addrlen 是 servaddr 结构的长度

Client 在 connect 前无需进行 bind,由系统确定源 IP 地址,并选择一个临时端口作为源端口。

TCP 下一些错误的情况:

TCP 调用 connect 会触发三次握手:

  • 发送方先发送一个 SYN,过一段时间没有收到响应则再发一个……直到若干次重试后仍没有收到 SYN 分节,则返回 E.TIMED.OUT 错误

  • 若 SYN 响应为 RST,说明该服务器主机在我们指定的端口没有进程在等待与之连接

    这是一种硬错误,一收到 RST 就返回 ECONNREFUESD 错误

  • 若 SYN 在中间某个路由器上引发一个 destination unreachable 错误,则认为是一种软错误,客户端继续尝试(类似第一种类型),直到失败,返回一个 E.NET.UNREACH 或 E.HOST.UNREACH

bind

★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
本文信息本文信息防爬虫替换信息
作者网站LYMTICShttps://lymtics.top
作者LYMTICS(樵仙)https://lymtics.top
联系方式me@tencent.mlme@tencent.ml
原文标题《Unix 网络编程》04:基本TCPSocket编程《Unix 网络编程》04:基本TCPSocket编程
原文地址https://www.cnblogs.com/lymtics/p/16319917.htmlhttps://www.cnblogs.com/lymtics/p/16319917.html
  • 如果您访问本文的链接并非如上地址,则可能是爬取作者的文章,建议返回原站阅读,谢谢您的支持
  • 原文会不断地更新和完善排版和样式会更加适合阅读,并且有相关配图
★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
#include <sys/socket.h>

int bind(int sockfd, const struct sockaddr * myaddr, socklen_t addrlen);

第二个参数是一个指向特定于协议的地址结构的指针。

关于绑定的若干说明:

端口:

  • 一般来说,客户端无需进行 bind,由内核分配临时端口;
  • 而服务器则需要 bind 一个众所周知端口来让别人访问。(这个规则的例外是 RPC 服务器)

IP 地址:

  • 对于客户端来说,可以自己指定一个自己的网络接口的地址,也可以由内核进行绑定
  • 对于服务器来说,一般是在连接到来时才确定绑定的地址,如果手动设置则意味着只接受该地址的请求

设置通配地址

// IPv4
struct sockaddr_in servaddr;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);

// IPv6
struct sockaddr_in6 serv;
serv.sin6_addr = in6addr_any;

listen

★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
本文信息本文信息防爬虫替换信息
作者网站LYMTICShttps://lymtics.top
作者LYMTICS(樵仙)https://lymtics.top
联系方式me@tencent.mlme@tencent.ml
原文标题《Unix 网络编程》04:基本TCPSocket编程《Unix 网络编程》04:基本TCPSocket编程
原文地址https://www.cnblogs.com/lymtics/p/16319917.htmlhttps://www.cnblogs.com/lymtics/p/16319917.html
  • 如果您访问本文的链接并非如上地址,则可能是爬取作者的文章,建议返回原站阅读,谢谢您的支持
  • 原文会不断地更新和完善排版和样式会更加适合阅读,并且有相关配图
★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★

只能由 TCP Server 调用,把套接字转换为被动套接字,指示内核接收该套接字的连接请求(默认为主动套接字),此时 socket 状态由 CLOSED 转换为 LISTEN。

本函数的调用时机:socket 和 bind 之后,accept 之前

#include <sys/socket.h>

int listen(int sockfd, int backlog);

backlog 的说明

★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
本文信息本文信息防爬虫替换信息
作者网站LYMTICShttps://lymtics.top
作者LYMTICS(樵仙)https://lymtics.top
联系方式me@tencent.mlme@tencent.ml
原文标题《Unix 网络编程》04:基本TCPSocket编程《Unix 网络编程》04:基本TCPSocket编程
原文地址https://www.cnblogs.com/lymtics/p/16319917.htmlhttps://www.cnblogs.com/lymtics/p/16319917.html
  • 如果您访问本文的链接并非如上地址,则可能是爬取作者的文章,建议返回原站阅读,谢谢您的支持
  • 原文会不断地更新和完善排版和样式会更加适合阅读,并且有相关配图
★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★

两个队列:

如图所示,为三次握手的场景:

  1. 服务器会在接收到第一次握手的 SYN 时将该请求的相关信息保存到一个未完成连接队列中,并返回第二次握手的信息
  2. 当客户端返回第三次握手的信息后,会将该请求的相关信息从未完成队列移动到已完成连接队列
  3. 当进程调用 accept 时,已完成连接队列的队头项将返回给进程,或者如果该队列为空,则进入睡眠,直到新的一项放入该队列才唤醒该线程

backlog 规定了内核应该为相应套接字排队的最大连接个数。

backlog 的若干说明

  • 这个参数曾被规定为两个队列总和的大小,尽管其没有被明确定义
  • Berkeley 的实现给其增设了一个模糊因子:把他乘以 1.5 以得到队列的最大长度
  • 之前,backlog 的值总是 5,因为这是 4.2BSD 支持的最大值,如今此值自然不够用;当前许多系统允许修改其最大值
  • 当一个客户 SYN 到达时,如果已经满了,则什么也不做;因为这种状态一般都是暂时的,通过此来触发客户端的重传机制从而解决暂时的问题;如果发送 RST,则客户端对其理解有歧义,而且也会加重网络负担,客户端也会触发错误处理机制。

SYN 泛洪攻击

Nginx 的配置参数中有 backlog 这个参数

accept

★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
本文信息本文信息防爬虫替换信息
作者网站LYMTICShttps://lymtics.top
作者LYMTICS(樵仙)https://lymtics.top
联系方式me@tencent.mlme@tencent.ml
原文标题《Unix 网络编程》04:基本TCPSocket编程《Unix 网络编程》04:基本TCPSocket编程
原文地址https://www.cnblogs.com/lymtics/p/16319917.htmlhttps://www.cnblogs.com/lymtics/p/16319917.html
  • 如果您访问本文的链接并非如上地址,则可能是爬取作者的文章,建议返回原站阅读,谢谢您的支持
  • 原文会不断地更新和完善排版和样式会更加适合阅读,并且有相关配图
★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★

accept 函数由 TCP server 调用,用于从已完成连接队列队头返回下一个已完成连接;如果该队列为空,则睡眠(阻塞)。

#include <sys/socket.h>

int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);

这个函数的三个参数:

  1. 第一个传入的是监听套接字的描述符
  2. 第二个和第三个参数传入的是结果的存放的地方,建立连接后,函数会把相关的信息保存到该指针指向的结构中。如果我们不感兴趣则可以设为 null。原书中把 Value-Result Arguments 翻译为值结果参数,属实是有点不好理解。

函数的返回值是已连接套接字描述符(或错误信息-1)。

fork/exec

★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
本文信息本文信息防爬虫替换信息
作者网站LYMTICShttps://lymtics.top
作者LYMTICS(樵仙)https://lymtics.top
联系方式me@tencent.mlme@tencent.ml
原文标题《Unix 网络编程》04:基本TCPSocket编程《Unix 网络编程》04:基本TCPSocket编程
原文地址https://www.cnblogs.com/lymtics/p/16319917.htmlhttps://www.cnblogs.com/lymtics/p/16319917.html
  • 如果您访问本文的链接并非如上地址,则可能是爬取作者的文章,建议返回原站阅读,谢谢您的支持
  • 原文会不断地更新和完善排版和样式会更加适合阅读,并且有相关配图
★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★

fork

★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
本文信息本文信息防爬虫替换信息
作者网站LYMTICShttps://lymtics.top
作者LYMTICS(樵仙)https://lymtics.top
联系方式me@tencent.mlme@tencent.ml
原文标题《Unix 网络编程》04:基本TCPSocket编程《Unix 网络编程》04:基本TCPSocket编程
原文地址https://www.cnblogs.com/lymtics/p/16319917.htmlhttps://www.cnblogs.com/lymtics/p/16319917.html
  • 如果您访问本文的链接并非如上地址,则可能是爬取作者的文章,建议返回原站阅读,谢谢您的支持
  • 原文会不断地更新和完善排版和样式会更加适合阅读,并且有相关配图
★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★

fork 函数是 Unix 中派生新进程的唯一方法。关于这个函数的具体内容在操作系统相关课程中有过学习。

该函数的一个特性是:父进程调用 fork 前打开的所有描述符在 fork 返回之后和子线程共享,因此我们通常会:

  • 父进程调用 accept 后调用 fork,然后关闭这个 Socket
  • 子进程接着读写这个 Socket

fork 的典型用法:

  • 创建自身的副本,让副本处理某项工作,如网络服务器
  • 创建自身的副本,然后调用 exec 执行另一个程序,如 Shell

exec

★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
本文信息本文信息防爬虫替换信息
作者网站LYMTICShttps://lymtics.top
作者LYMTICS(樵仙)https://lymtics.top
联系方式me@tencent.mlme@tencent.ml
原文标题《Unix 网络编程》04:基本TCPSocket编程《Unix 网络编程》04:基本TCPSocket编程
原文地址https://www.cnblogs.com/lymtics/p/16319917.htmlhttps://www.cnblogs.com/lymtics/p/16319917.html
  • 如果您访问本文的链接并非如上地址,则可能是爬取作者的文章,建议返回原站阅读,谢谢您的支持
  • 原文会不断地更新和完善排版和样式会更加适合阅读,并且有相关配图
★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★

exec 是存放在硬盘上的可执行程序文件被 Unix 执行的唯一方法;有 6 个不同的 exec 函数,他们的参数有所不同:

  • 传入的第一个参数是文件名还是路径名
  • 新程序的参数一一列出还是指针数组引用
  • 把调用进程的环境传递给新程序还是指定新的环境

一般来说,只有 execve 是内核中的系统调用,其他 5 个都是调用 execve 的库函数。

并发服务器

★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
本文信息本文信息防爬虫替换信息
作者网站LYMTICShttps://lymtics.top
作者LYMTICS(樵仙)https://lymtics.top
联系方式me@tencent.mlme@tencent.ml
原文标题《Unix 网络编程》04:基本TCPSocket编程《Unix 网络编程》04:基本TCPSocket编程
原文地址https://www.cnblogs.com/lymtics/p/16319917.htmlhttps://www.cnblogs.com/lymtics/p/16319917.html
  • 如果您访问本文的链接并非如上地址,则可能是爬取作者的文章,建议返回原站阅读,谢谢您的支持
  • 原文会不断地更新和完善排版和样式会更加适合阅读,并且有相关配图
★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★

上述服务器只能在同一时间服务一个客户。可以采用 fork 的方法来让子进程处理具体的请求,而父进程专心监听请求的到来。

大致框架

★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
本文信息本文信息防爬虫替换信息
作者网站LYMTICShttps://lymtics.top
作者LYMTICS(樵仙)https://lymtics.top
联系方式me@tencent.mlme@tencent.ml
原文标题《Unix 网络编程》04:基本TCPSocket编程《Unix 网络编程》04:基本TCPSocket编程
原文地址https://www.cnblogs.com/lymtics/p/16319917.htmlhttps://www.cnblogs.com/lymtics/p/16319917.html
  • 如果您访问本文的链接并非如上地址,则可能是爬取作者的文章,建议返回原站阅读,谢谢您的支持
  • 原文会不断地更新和完善排版和样式会更加适合阅读,并且有相关配图
★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
listenfd = Socket(...);
Bind(listenfd, ...);
Listen(listenfd, LISTENQ);

for (;;) {
  connfd = Accept(listenfd, ...);
  if ( (pid = Fork()) == 0) {
    Close(listenfd);
    doSomeService(connfd);
    Close(connfd);
    exit(0);
  }
  Close(connfd);
}

引用计数的理解

★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
本文信息本文信息防爬虫替换信息
作者网站LYMTICShttps://lymtics.top
作者LYMTICS(樵仙)https://lymtics.top
联系方式me@tencent.mlme@tencent.ml
原文标题《Unix 网络编程》04:基本TCPSocket编程《Unix 网络编程》04:基本TCPSocket编程
原文地址https://www.cnblogs.com/lymtics/p/16319917.htmlhttps://www.cnblogs.com/lymtics/p/16319917.html
  • 如果您访问本文的链接并非如上地址,则可能是爬取作者的文章,建议返回原站阅读,谢谢您的支持
  • 原文会不断地更新和完善排版和样式会更加适合阅读,并且有相关配图
★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★

每个文件或套接字都有一个引用计数,引用计数在文件表项中维护。它是当前打开着的引用该文件或套接字的描述符的个数。

当 fork 时,原本父进程持有的两个描述符 listenfd、connfd 的引用值都会 +1,而调用 close 方法只会让相应的引用值 -1。

该套接字真正的清理和资源释放要到其引用计数值到达 0 时才发生。

因此,父进程 Close(connfd) 和子进程 Close(listenfd) 都不会导致套接字真正被关闭。

如果我们确实想触发关闭流程,可以改用 shutdown 函数。

另外,如果不关闭的话,会造成资源的泄漏,耗尽可用的描述符。

close

★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
本文信息本文信息防爬虫替换信息
作者网站LYMTICShttps://lymtics.top
作者LYMTICS(樵仙)https://lymtics.top
联系方式me@tencent.mlme@tencent.ml
原文标题《Unix 网络编程》04:基本TCPSocket编程《Unix 网络编程》04:基本TCPSocket编程
原文地址https://www.cnblogs.com/lymtics/p/16319917.htmlhttps://www.cnblogs.com/lymtics/p/16319917.html
  • 如果您访问本文的链接并非如上地址,则可能是爬取作者的文章,建议返回原站阅读,谢谢您的支持
  • 原文会不断地更新和完善排版和样式会更加适合阅读,并且有相关配图
★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★

作用:关闭 Socket,终止 TCP 连接。

  • 一个 TCP Socket 的默认行为是把该 Socket 标记成已关闭,然后立即返回到调用进程
  • 该 Socket 描述符不能再发送新的消息
  • 尝试发送已经在发送队列的消息
  • TCP 终止
#include <unistd.h>

int close(int sockfd);

getsockname/getpeername

★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
本文信息本文信息防爬虫替换信息
作者网站LYMTICShttps://lymtics.top
作者LYMTICS(樵仙)https://lymtics.top
联系方式me@tencent.mlme@tencent.ml
原文标题《Unix 网络编程》04:基本TCPSocket编程《Unix 网络编程》04:基本TCPSocket编程
原文地址https://www.cnblogs.com/lymtics/p/16319917.htmlhttps://www.cnblogs.com/lymtics/p/16319917.html
  • 如果您访问本文的链接并非如上地址,则可能是爬取作者的文章,建议返回原站阅读,谢谢您的支持
  • 原文会不断地更新和完善排版和样式会更加适合阅读,并且有相关配图
★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★

这两个函数返回与某个套接字关联的本地协议地址或外地协议地址:

#include <sys/socket.h>

int getsockname(int sockfd, struct sockaddr *localaddr, socklen_t *addrlen);

int getpeername(int sockfd, struct sockaddr *peeraddr, socklen_t *addrlen);

后两个参数用来接收结果

getsockname 的若干应用场景:

  • 客户端,没有 bind 端口,可以用 getsockname 获取内核赋予该连接的本地 IP 和端口号

  • 客户端以端口号 0 为参数调用 bind,让内核选择端口号,可以用 getsockname 返回本地端口号

  • 获取某个套接字的地址簇

    int main(int argc, char** argv) {
        int sockfd, n;
        char recvline[MAXLINE + 1];
        socklen_t len;
        struct sockaddr_storage ss;
    
        if (argc != 3)
            err_quit("usage: daytimetcpcli <hostname/IPaddress> <service/port#>");
    
        sockfd = Tcp_connect(argv[1], argv[2]);
    
        len = sizeof(ss);
        Getpeername(sockfd, (SA*)&ss, &len);
        printf("connected to %s\n", Sock_ntop_host((SA*)&ss, len));
    
        while ((n = Read(sockfd, recvline, MAXLINE)) > 0) {
            recvline[n] = 0; /* null terminate */
            Fputs(recvline, stdout);
        }
        exit(0);
    }
    
  • 服务端,accept成功返回后,可以调用 getsockname 获取该连接的本地 IP 和端口号

getpeername 的若干应用场景:

服务端,accept 进程后并 fork、exec 一个子进程进行处理,执行 exce 后内存映射会被替换成真正的服务器程序(如 Telnet),包含客户端地址的套接字地址结构就此丢失,不过已连接的套接字描述符跨 exec 开放;可以调用 getpeername 获取客户的 IP 地址和端口号(如下图所示)

posted @ 2022-05-28 10:00  樵仙  阅读(85)  评论(0编辑  收藏  举报