tcp提供了可靠传输,当tcp向另一端发送数据的时候,要求对端返回一个确认。如果没有接收到确认,tcp就重传数据并且等待更长时间,数次重传失败后,tcp才放弃。

建立一个tcp连接会发生如下事情:

  1. 服务器必须准备好接受外来的连接请求,通常通过socket,bind,listen这三个函数完成。
  2. 客户端通过connect连接服务器,主动打开,导致tcp客户端发送一个SYN字节,通常SYN不携带数据。
  3. 服务端必须确认客户的SYN字节,即ACK字节,同时服务端还会发送一个SYN字节给客户端
  4. 客户端必须确认服务端的SYN。

每一个SYN可以包含多个TCP选项:

  • MSS选项:向对端通告最大分节大小,即愿意接收的最大数据量。
  • 窗口规模选项:TCP首部中接收窗口首部为16位,意味着最大接收窗口为65535.
  • 时间戳选项:对于高速网络是必要的,可以防止失而复现的分组可能造成的数据损坏。

相关函数:

(1)socket()

#include <sys/socket.h>
int socket(int family, int type, int protocol);
  • family:指定协议族,也称为协议域。取值为下面中的一个

  • type,指明套接字类型

  • protocol:协议类型常值,设为0的话表示选择所给定的family和type组合的系统默认值。

(2)connect()

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

成功返回0,否则返回-1

  • sockfd:是由socket返回的套接字。

  • servaddr:包含服务器IP地址和端口号的套接字地址结构
  • addrlen:套接字地址结构的大小。

客户在调用connect之前不必一定需要调用bind函数,内核会确定源IP并选择一个临时端口作为源端口。如果是TCP套接字,调用connect将激发TCP三次握手过程。函数会阻塞进程,直到成功返回或者出错。

  1. 返回ETIMEDOUT错误:内核发送一个SYN,如没有相应,则等待6s后在发送一个,若仍无相应,则等待24秒再发送,总共等待75s后仍没有响应,则返回该错误。

  2. 返回ECONNREFUSED:服务器对客户端响应一个RST,表明服务器在客户指定的端口上没有进程在等待与之连接。产生RST还有两个条件:1)TCP想取消一个已有连接;2)TCP接收到一个根本不存在的连接上的分节
  3. 返回EHOSTUNREACH或ENETUNREACH错误:客户发出的SYN在中间的某个路由引发一个“目的地 不可达”ICMP错误,内核会报错该消息,并按情况1中的间隔继续发送SYN,若在规定时间内仍未收到响应,则把报错的信息作为这两种错误之一返回给进程。

connect若失败,则该套接字不可用,则必须先关闭该套接字,然后再次调用connect函数。

(3)bind函数:把一个本地协议地址赋予一个套接字

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

调用bind可以绑定IP地址或者端口,可以两者都指定,也可以两者都不指定。

  • 如果指定端口为0,那么内核在bind被调用得时候选择一个临时端口。

  • 如果指定IP地址为通配地址(对IPv4来说,通配地址由常值INADDR_ANY来指定,值一般为0),内核将等到套接字已连接(TCP)或已在套接字上发出数据报(UDP)时才选择一个本地IP地址

如果让内核来为套接字选择一个临时端口号,函数bind并不会返回所选择的值。第二个参数有const限定词,它无法返回所选的值。如果想得到内核所选择的临时端口值,必须调用getsockname函数

(4)listen函数

#include<sys/socket.h>
int listen(int sockfd, int backlog);

listen做两件事:

  1. 当socket创建一个套接字的时候,它被设定为一个主动套接字。listen将其转成一个被动套接字,指示内核应接受指向该套接字的连接请求

  2. 第二个函数规定了内核应该为相应套接字排队的最大连接个数。

内核为任一给定的监听套接字维护两个队列,两个队列之和不超过backlog

  • 未完成队列

  • 已完成队列

当进程调用accept函数的时候,如果已连接队列不为空,那么队头项将返回给进程。否则进程进入睡眠,直到TCP在该队列中放入一项才唤醒它;不要把backlog设置为0;设置backlog的一种方法是使用一个常值。SYN到达的时候,如果队列已满,TCP忽略该SYN分节。

(5)accept()

#include<sys/socket.h>
int accept(int sockfd, struct sockaddr *cliaddr, socklen_t addrlen);
  • sockfd:监听套接字描述符

  • cliaddr:已连接的对端客户的套接字地址结构
  • addrlen:调用时指示内核向cliaddr写入的最大字节数。

如果对返回客户协议地址不感兴趣,可以把cliaddr和addrlen均置为空指针。

(6)close()

#include<unistd.h>
int close(int sockfd);

close一个TCP套接字的默认行为是套接字标记关闭,立即返回调用进程。然后TCP将尝试发送已排队等待发送到对端的任何数据。close会将套接字描述符的引用计数减1,如果引用计数仍大于0,则不会引起TCP的四次挥手终止序列。

(7)shutdown()

#include<sys/socket.h>
int shutdown(int sockfd, int howto);
  • howto

    • SHUT_RD:关闭连接的读这一半,套接字接收缓冲区中的现有数据都被丢弃。进程不能再对这样的套接字调用任何读函数(对一个TCP套接字这样调用shutdown函数后,由该套接字接收的来自对端的任何数据都被确认,然后悄然丢弃)

    • SHUT_WR:关闭连接的写这一半对于TCP,称为半关闭),套接字发送缓冲区中的数据将被发送掉,后跟TCP的正常连接终止序列。进程不能再对这样的套接字调用任何写函数
    • SHUT_RDWR:连接的读半部和写半部都关闭。等价于调用2次shutdown,分别指定SHUT_RD与SHUT_WR

shutdown和close的区别:

  • 关闭套接字的时机不同:close把描述符的引用计数减一,仅在引用计数为0的时候,才会关闭套接字,而shutdown可直接关闭套接字

  • 全关闭和半关闭:close终止读和写两个方向的数据传送。shutdown可以关闭一个也可以关闭两个。

(8)getsockname()和getpeername()

这两个函数与TCP连接建立过程中套接字地址结构的信息获取相关

#include <sys/socket.h>
int getsockname(int sockfd, struct sockaddr* localaddr, socklen_t *adrlen);
int getpeername(int sockfd, struct sockaddr* peeraddr, socklen_t *adrlen);
  • getsockname用于返回与某个套接字关联的本地协议地址
    • 没调用bind的TCP客户上,connect成功返回后,获取由内核赋予该连接的本地IP地址和端口号
    • 在以端口号0调用bind后,返回由内核赋予的本地端口号。
    • 获取某个套接字的地址簇
    • 绑定通配IP的服务器上,accept返回后,获取由内核赋予该连接的本地IP地址,此时sockfd不是监听套接字描述符
  • getpeername用于返回与某个套接字关联的外地协议地址
    • 当一个服务器是由调用过accept的某个进程通过调用exec执行程序时,它能获取客户身份的唯一途径便是调用getpeername

(9)fork()

#include <unistd.h>
pid_t fork(void);
  • fork():调用进程(父进程)中返回一次,返回值是新建的子进程的进程ID号,在子进程又返回一次。返回值为0.返回值本身告知当前进程是父进程还是子进程。

<网络编程>基本TCP套接字编程的更多相关文章

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

    一.基于TCP协议的网络程序 下图是基于TCP协议的客户端/服务器程序的一般流程: 服务器调用socket().bind().listen()完成初始化后,调用accept()阻塞等待,处于监听端口的 ...

  2. 【UNIX网络编程(四)】TCP套接字编程具体分析

    引言: 套接字编程事实上跟进程间通信有一定的相似性,可能也正由于此.stevens这位大神才会将套接字编程与进程间的通信都归为"网络编程",并分别写成了两本书<UNP1> ...

  3. UNP学习笔记1——基本TCP套接字编程

    1 套接字地址结构 大多数套接字函数都需要一个指向套接字地址结构的指针作为参数.每个协议族都定义了自己的套接字结构.这些套接字的结构以sockaddr_开头,以每个协议族唯一的后缀名结尾. 1.1 I ...

  4. TCP套接字编程模型及实例

    摘要:     本文讲述了TCP套接字编程模块,包括服务器端的创建套接字.绑定.监听.接受.读/写.终止连接,客户端的创建套接字.连接.读/写.终止连接.先给出实例,进而结合代码分析. PS:本文权当 ...

  5. Python黑帽编程2.8 套接字编程

    Python黑帽编程2.8 套接字编程 套接字编程在本系列教程中地位并不是很突出,但是我们观察网络应用,绝大多数都是基于Socket来做的,哪怕是绝大多数的木马程序也是如此.官方关于socket编程的 ...

  6. Python网络编程之TCP套接字简单用法示例

    Python网络编程之TCP套接字简单用法示例 本文实例讲述了Python网络编程之TCP套接字简单用法.分享给大家供大家参考,具体如下: 上学期学的计算机网络,因为之前还未学习python,而jav ...

  7. 初探网络编程--TCP套接字编程演示

    今天看了一下<计算机网络:自顶向下方法>,也就是计算机网络的教材的应用层一章,决定实现以下后面的Java C/S应用程序的例子,用来演示TCP和UDP套接字编程. 程序流程如下: 1.一台 ...

  8. 【UNIX网络编程(二)】基本TCP套接字编程函数

    基于TCP客户/server程序的套接字函数图例如以下: 运行网络I/O.一个进程必须做的第一件事就是调用socket函数.指定期望的通信协议类型. #include <sys/socket.h ...

  9. unix网络编程——TCP套接字编程

    TCP客户端和服务端所需的基本套接字.服务器先启动,之后的某个时刻客户端启动并试图连接到服务器.之后客户端向服务器发送请求,服务器处理请求,并给客户端一个响应.该过程一直持续下去,直到客户端关闭,给服 ...

随机推荐

  1. JS ES6中的箭头函数(Arrow Functions)使用

    转载这篇ES6的箭头函数方便自己查阅. ES6可以使用“箭头”(=>)定义函数,注意是函数,不要使用这种方式定义类(构造器). 一.语法 基础语法 (参数1, 参数2, …, 参数N) => ...

  2. BZOJ3028: 食物(生成函数)

    题意 链接 Sol 生成函数入门题. 对每个物品分别列一下,化到最后是\(\frac{x}{(1-x)^4}\) 根据广义二项式定理,最后答案是\(C_{(N - 1) + 4 - 1}^{4-1} ...

  3. 2018-11-29 VS Code英汉词典插件v0.0.6-改为TS实现, 加测试

    如前文VS Code英汉词典插件v0.0.4-驼峰下划线命名打算, 首先将JS源码改为TypeScript实现, 并添加了必要的测试. 昨天得知vue.js 3.0会用TypeScript实现, 正好 ...

  4. 中国 AI 天才养成计划:清华姚班和 100 个「张小龙」

    https://daily.zhihu.com/story/9653612?from=timeline&isappinstalled=0   AI财经社,专注未来,以及更好的生活 真正的 AI ...

  5. Linux查看机器的硬件信息

    转载:https://linux.cn/article-9932-1.html

  6. JavaScript面向对象编程指南(四) 对象

    第4章 对象 4.1 从数组到对象 对象的组成:变量名.{}.用逗号分割的属性.用冒号分割的键/值对. var f={ name:'alen', // 可以在属性名上加引号 age:12 }; 对象文 ...

  7. Windows Zip/CentOS/Radhat系统安装Mysql5.7.x方法

    CentOS/Redhat 安装: wget http://dev.mysql.com/get/mysql57-community-release-el7-9.noarch.rpm rpm -Uvh ...

  8. MySQL 查看用户授予的权限

      在MySQL中,如何查看一个用户被授予了那些权限呢? 授予用户的权限可能分全局层级权限.数据库层级权限.表层级别权限.列层级别权限.子程序层级权限.具体分类如下: 全局层级 全局权限适用于一个给定 ...

  9. Node 各个版本支持ES2015特性的网站

    如果想了解Node 各个版本支持ES2015到那个程度,可以看下面网站. https://node.green/

  10. 基数排序python实现

    基数排序python实现 基数排序 基数排序(英语:Radix sort)是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较.由于整数也可以表达字符串(比如名字或 ...