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. SAP MM ME57 把PR转成PO

    SAP MM ME57 把PR转成PO 加入P3项目里,笔者负责采购和IM库存管理domain.采购环节里的PR转PO,客户global template里建议使用ME57或者在MD04里做.笔者在项 ...

  2. 8.Odoo产品分析 (二) – 商业板块(3) –CRM(2)

    查看Odoo产品分析系列--目录 接上一篇Odoo产品分析 (二) – 商业板块(3) –CRM(1) 4. 设置 在配置–>设置中:    在分析"销售"模块时已经将其他的 ...

  3. 通过源码分析View的测量

    要理解View的测量,首先要了解MeasureSpec,系统在测量view的宽高时,要先确定MeasureSpec. MeasureSpec(32为int值)由两部分组成: SpecMode(高2位) ...

  4. Android的ToolBar

    ToolBar比ActionBar更加可控,自由.因此,Google 逐渐使用ToolBar来代替ActionBar. 使用ToolBar 1.要引入appCompat_v7支持 2.主题设置为NoA ...

  5. IE打开https网站时,取消证书问题提示

    上面介绍了,调用IE来打开对应的网页问题,但是在实际测试中,有些网站是采用https协议的,这时候IE浏览器会弹出如下窗口,一般手动选择后,才可进入登录界面,那么该如何解决呢? 1.点击[继续浏览此网 ...

  6. ueditor在线编辑器的简单使用-上传图片

    由于我的项目个人博客网站需要用到在线编辑器,百度的ueditor编辑器就是一个很好的编辑器.开始比较迷茫的使用,各种百度,没有我满意的答案,明明可以很简单的使用. 1.首先进入ueditor官网下载, ...

  7. 初学ubuntu命令

    将我第一天学的内容做个笔记 ,方便日后查看 命令的使用: ls     ls -a 显示所有文件  ls -l 显示文件的所有信息 cp   注意命令格式  /home/../temp/a   /ho ...

  8. c函数指针

    #include <stdio.h> int max(int a, int b){ return a > b ? a : b; } int min(int a, int b){ re ...

  9. C# -- 内插字符串的使用

    C# -- 内插字符串的使用 (1) 字符串文本以 $ 字符开头,后接左双引号字符. $ 符号和引号字符之间不能有空格.(2) 内插字符串表达式的结果可以是任何数据类型.(3) 可通过在内插表达式后接 ...

  10. 如何写 go 代码 (How to Write Go Code 翻译)

    目录 1. 写在前面的话 2. 介绍 3. 代码组织 3.1. 工作区 3.2. GOPATH 环境变量 3.3. Package 路径 3.4. 第一个 GO 程序 3.5. 第一个 GO 库 3. ...