UNIX网络编程——基本TCP套接字编程

  • socket 函数
  • connect 函数
  • bind 函数
  • listen 函数
  • accept 函数
  • fork 和 exec 函数
  • 并发服务器
  • close 函数
  • getsockname 和 getpeername 函数

socket 函数

socket 函数,指定期望的通信协议类型(使用IPv4的TCP、使用 IPv6 的UDP、Unix域字节流协议等)。

#include <sys/socket.h>
int socket(int family, int type, int protocol);返回:若成功则为非负描述符,若出错则为-1

family 参数指明协议族,它是图4-2中所示的某个常值。该参数也往往被称为协议域。
type 参数指明套接字类型,它是图4-3中所示的某个常值。
protocol 参数应设为图 4-4所示的某个协议类型常值,或者设为0,以选择所给定 family 和 type 组合的系统默认值。




socket 函数在成功时返回一个小的非负整数值,它与文件描述符类似,我们把它称为套接字描述符,检查 sockfd

为得到这个套接字描述符,只是指定了协议族(IPv4、IPv6或 Unix)和套接字类型(字节流、数据报或原始套接字)。并没有指定本地协议地址或远程协议地址。

AF_ 前缀表示地址族,PE_ 前缀表示协议族。

connect 函数

TCP 客户用 connect 函数来建立与TCP服务器的连接。

#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr* servaddr, socklen_t addrlen);返回:若成功则为0,若出错则为-1

sockfd 是由 socket 函数返回的套接字描述符,第二个、第三个参数分别是一个指向套接字地址结构的指针和该结构的大小。
套接字地址结构必须含有服务器的IP地址和端口号。

客户在调用函数 connect 前不必非得调用 bind 函数,因为如果需要的话,内核会确定源IP地址,并选择一个临时端口作为源端口。

TCP套接字,调用 connect 函数将激发TCP的三路握手过程,而且仅在连接建立成功或出错时才返回,其中出错返回可能有以下几种情况。

  • (1)若TCP客户没有收到SYN分节的响应,则返回 ETIMEDOUT 错误。

  • (2)若对客户的SYN的响应是RST(表示复位),则表明该服务器主机在我们指定的端口上没有进程在等待与之连接。这是一种硬错误,客户一接收到RST就马上返回 ECONNREFUSED 错误。
    RST是TCP在发生错误时发送的一种TCP分节。
    产生RST的三个条件是:

    • 目的地为某端口的SYN到达,然而该端口上没有正在监听的服务器;
    • TCP想取消一个已有连接;
    • TCP接收到一个根本不存在的连接上的分节。
  • (3)若客户发生的SYN在中间的某个路由器上引发一个 “destination unreachable”(目的地不可达) ICMP错误,则认为是一种软错误(soft error)。
    以下两种情形也是有可能的:一是按照本地系统的转发表,根本没有到达远程系统的路径;二是 connect 调用根本不等待就返回。

bind 函数

bind 函数把一个本地协议地址赋予一个套接字。

#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr* myaddr, socklen_t addrlen);返回:若成功则为0,若出错则为-1

第二个参数是一个指向特定于协议的地址结构的指针,第三个参数是该地址结构的长度。
对于TCP,调用bind函数可以指定一个端口号,或指定一个IP地址,也可以两者都指定,还可以都不指定。

一个主机有多个网络接口,每个网络接口有一个IP地址。

  • 对TCP客户,执行bind指定IP地址,则为后续sockfd上发出的IP数据报限定了源IP地址;
  • 对TCP客户,执行bind不指定IP地址,则为后续sockfd上发出的IP数据报的源IP地址采取发出的网络接口的IP地址。
  • 对TCP服务器,执行bind指定IP地址,则限定后续sockfd监听时只接收目的IP为这里指定IP地址的连接请求;
  • 对TCP服务器,执行bind不指定IP地址,则限定后续sockfd监听时接收目的IP为主机上任意网络接口IP地址的连接请求。

且采用客户发来SYN的目的IP地址作为服务器上对应已经连接套接字的本端IP地址。

IP地址为INADDR_ANY表不指定,端口为0表不指定;端口未指定时,由内核为此sockfd指定。

主机可收取IP地址为其任一网络接口IP地址的数据报,数据报递送到进程,则需要按数据报的(源IP地址,源端口,目的IP地址,目的端口),递送到本机中与其匹配的套接字的缓冲区。
随后,可通过匹配套接字调read读入该套接字缓冲区的数据到进程。

从 bind 函数返回的一个常见错误是 EADDRINUSE (“Address already in use”,地址已使用)。

listen 函数

#include <sys/socket.h>
int listen(int sockfd, int backlog);返回:若成功则为0,若出错则为-1

内核为任何一个给定的监听套接字维护两个队列:

  • (1)未完成连接队列
    收到SYN,尚未完成三路握手的连接。
    一般在未完成连接队列已经满,接着收到SYN,后续收到的SYN将忽略(对端会超时重传)。
  • (2)已完成连接队列
    已经完成三路握手。
    两队列维护套接字个数之和原则上不超过参数2。

已完成连接队列中套接字关联的数据到达主机,即使尚未accept,该套接字的数据也会放入该套接字的接收缓冲区;待其后续读取。


每当在未完成连接队列创建一项时,来自监听套接字的参数就复制到即将建立的连接中。
连接的创建机制是完全自动的,无需服务器进程插手。

accept 函数

accept 函数由TCP服务器调用,用于从已完成连接队列队头返回下一个已完成连接。

#include <sys/socket.h>
int accept(int sockfd, struct sockaddr* cliaddr,  socklen_t* addrlen);

参数 cliaddr 和 addrlen 用来返回已连接的对端进程(客户)的协议地址。
addrlen 是值-结果参数:调用前,将由 *addrlen 所引用的整数值置为由 cliaddr 所指的套接字地址结构的长度,返回时,该整数值即为由内核存放在该套接字地址结构内的确切字节数。

如果 accept 成功,那么其返回值是由内核自动生成的一个全新描述符,代表与所返回客户的TCP连接。

accept 函数的第一个参数为监听套接字(listening socket)描述符(由 socket 创建,随后用作 bind 和 listen 的第一个参数的描述符),称它的返回值为已连接套接字(connected socket)描述符。

区分两个套接字:
一个服务器通常仅仅创建一个监听套接字,它在该服务器的生命期内一直存在。
内核为每个服务器进程接受的客户创建一个已连接套接字(TCP三路握手已完成)。
当服务器完成对某个给定客户的服务时,相应的已连接套接字就被关闭。

本函数最多返回三个值:一个既可能是新套接字描述符也可能是出错指示的整数、客户进程的协议地址(由 cliaddr 指针所指)以及该地址的大小(由 addrlen 指针所指)。

fork 和 exec 函数

fork 函数是Unix 中派生新进程的唯一方法。

#include <unistd.h>pid_t fork(void);返回:在子进程中为0,在父进程中为子进程ID,若出错则为-1

fork在子进程返回0而不是父进程的进程ID的原因在于:

  • 任何子进程只有一个父进程,而且子进程总是可以通过调用 getppid 取得父进程的进程ID。
  • 相反,父进程可以有许多子进程,而且无法获取各个子进程的进程ID。如果父进程想要跟踪所有子进程的进程ID,那么它必须记录每次调用 fork 函数的返回值。

父进程调用fork之前打开的所有描述符在 fork 返回之后由子进程分享。

fork 的两个典型用法:

  • (1)一个进程创建一个自身的副本,这样每个副本都可以在另一个副本执行其他任务的同时处理各自的某个操作。(王丽丽服务器的典型用法)
  • (2)一个进程想要执行另一个程序。既然创建新进程的唯一办法是调用 fork ,该进程于是首先调用 fork 创建一个自身的副本,然后其中一个副本(通常为子进程)调用 exec 把自身替换成新的程序。(shell之类程序的典型用法)

存放在硬盘上的可执行程序文件能够被Unix执行的唯一方法是:由一个现有进程调用六个exec函数中的某一个。

exec把当前进程映像替换成新的程序文件,而且该进程通常从 main 函数开始执行。进程ID并不改变。
成调用exec的进程为调用进程,成新执行的程序为新程序


并发服务器

当一个连接建立时,accept 返回,服务器接着调用 fork ,然后有子进程服务客户(通过连接套接字 connfd),父进程则等待另一个连接(通过监听套接字 listenfd)。既然新的客户由子进程提供服务,父进程就关闭已连接套接字。

close 函数

close 函数也用来关闭套接字,并终止TCP连接。

#include <unistd.h>
int close(int sockfd);返回:若成功则为0,若出错则为-1

close 一个TCP套接字的默认行为是把该套接字标记成已关闭,然后立即返回到调用进程。

描述符引用计数

每个文件或套接字有一个引用计数,在文件表项中维护;代表引用此文件或套接字的描述符的个数。

fork造成父进程打开的描述符被复制到子进程,相应的描述符指向文件或套接字的引用计数+1。
对描述符执行close时,先将其指向的文件或套接字引用计数-1。

getsockname 和 getpeername 函数

这两个函数或者返回与某个套接字关联的本地协议地址(getsockname),或者返回与某个套接字关联的外地协议地址(getpeername)。

#include <sys/socket.h>
int getsockname(int sockfd, struct sockaddr*,socklen_t*);
int getpeername(int sockfd, struct sockaddr*,socklen_t*);均返回:若成功则为0,若出错则为-1

这两个函数的最后一个参数都是值-结果参数。

学习参考资料:

《UNIX网络编程 卷1:套接字联网API》 第3版

《UNIX网络编程 卷1:套接字联网API》学习笔记——基本TCP套接字编程相关推荐

  1. 《UNIX网络编程 卷1:套接字联网API(第3版)》——第2章 传输层:TCP、UDP和SCTP 2.1概述...

    本节书摘来自异步社区<UNIX网络编程 卷1:套接字联网API(第3版)>一书中的第2章,第2.1节,作者:[美]W. Richard Stevens , Bill Fenner , An ...

  2. 图灵社区 : 图书 : UNIX网络编程 卷1:套接字联网API(英文版•第3版)

    图灵社区 : 图书 : UNIX网络编程 卷1:套接字联网API(英文版•第3版) 图灵社区 : 图书 : UNIX网络编程 卷1:套接字联网API(英文版•第3版) UNIX网络编程 卷1:套接字联 ...

  3. UNIX网络编程.卷1,套接字联网API(第3版)(中文版)(Stevens经典著作,两位顶级网络编程专家应邀执笔修订)...

    UNIX网络编程.卷1,套接字联网API(第3版)(中文版)(Stevens经典著作,两位顶级网络编程专家应邀执笔修订) 基本信息 原书名: Unix Network Programming, Vol ...

  4. 《Unix网络编程卷1-套接字联网API》第一个例子编译 不通过问题解决

    <Unix网络编程卷1-套接字联网API>是本好书. 但是第一个例子不是很好编译. 需要如下步骤: 本人机器CentOS 5.4 1.下载源码 unpv13e解压到任意目录 然后按其rea ...

  5. TCP/IP网络编程 学习笔记_3 --给套接字分配IP地址和端口号

    IP地址和端口号 1,IP地址:为使计算机连接到网络并收发数据,必须为其分配IP地址.IP地址分为两类:IPv4(4字节地址族)和IPv6(16字节地址族).它们主要区别就是在表示IP地址所用的字节数 ...

  6. 面向对象的编程思想写单片机程序——(3)学习笔记 之 程序分层、数据产生流程

    系列文章目录 面向对象的编程思想写单片机程序--(1)学习笔记 之 程序设计 面向对象的编程思想写单片机程序--(2)学习笔记 之 怎么抽象出结构体 面向对象的编程思想写单片机程序--(3)学习笔记 ...

  7. 《Unix网络编程卷1:套接字联网API》读书笔记

    第一部分:简介和TCP/IP 第1章:简介 第2章:传输层:TCP.UDP和SCTP TCP:传输控制协议,复杂.可靠.面向连接协议 UDP:用户数据报协议,简单.不可靠.无连接协议 SCTP:流控制 ...

  8. 《UNIX网络编程:套接字联网API》啃书笔记(第8UDP套接字编程、11章地址转换)

    基本UDP套接字编程 下图为UDP客户/服务器程序的函数调用: 注意客户不与服务器建立连接,而是只管使用sendto函数给服务器发送数据报,其中必须指定目的地的地址作为参数.类似的,服务器不接受来自客 ...

  9. 网络学习笔记:TCP/IP连网和Internet

    1.网关 由硬件和软件组成,实现不同网段间的数据传送. 常用路由器充当网关. 网关通常维护一份路由表,但只有少量的编址信息.它用这些信息把数据转发到知道更多信息的网关. 组成互联网骨干的网关称为核心网 ...

最新文章

  1. c语言aba字母塔,打印字母金字塔,昨晚看到某个帖子的题目
  2. Yslow on Nodejs server
  3. 装linux系统时键盘用不了怎么办_图吧垃圾佬的LINUX体验
  4. Julia OpenCV绑定简介
  5. NOIP2008 普及组T1 ISBN号码 解题报告-S.B.S.
  6. location.pathname;outline:medium;undefined不能加引号
  7. Java基础之多态深入解析
  8. [SDK2.2]Windows Azure Storage (16) 使用WCF服务,将本地图片上传至Azure Storage (上) 客户端代码...
  9. bzoj 2850 巧克力王国 —— K-D树
  10. 【渝粤教育】国家开放大学2018年春季 0234-22T公务员制度讲座 参考试题
  11. 华为U2000云平台和APP管理系统建设
  12. 经典算法分析:n与lgn
  13. mac设置windows文件服务器,苹果MAC访问Windows共享文件夹的技巧
  14. 架构设计(5)-架构愿景分析
  15. linux shc shell脚本_Linux系统中Shell脚本使用Shc和gzexe加密/解密
  16. U盘文件夹变成exe应用程序
  17. 免费的人脸识别SDK(基于 Java 实现的人脸识别功能)
  18. react 实现图片的滚动缩放和按住鼠标左键移动图片效果
  19. Samsung Electronics (三星电子)
  20. Codeblocks-20.03下载及安装教程(面向小白的保姆级教程)

热门文章

  1. Elasticsearch文档高级操作
  2. 雷鸟thunderbird最小化插件--bosskey 0.1.8
  3. 推荐一个无缝背景花纹资源站-AVA7 PATTERNS
  4. 联想yoga710评测_YOGA新本性能担当 联想YOGA 710 14评测
  5. Elasticsearch8.6启动异常:fatal exception while booting Elasticsearch
  6. 《百年捷克》读书笔记及读后感作文2200字
  7. uni-app横竖屏锁定
  8. 华为ensp PBR策略路由相关协议
  9. mysql中flush用法_mysql flush用法
  10. NPDP门径管理流程