对TCP套接字调用connect会激发三次握手,例如以下:

client是主动打开连接的一端,会发送第一个SYN分节,然后等待确认,此时连接状态为SYN_SENT,当收到服务端的确认后连接建立,状态变为ESTABLISHED

server是被动打开连接的一端,调用listen导致套接字从CLOSED状态变为LISTEN状态,当收到来自client的SYN分节以后状态变为SYN_RCVD,然后发送第二个SYN分节,等待client的确认,收到client的确认以后连接建立,状态变为ESTABLISHED

三次握手中的两个SYN分节都会告诉对端本端在同一连接中发送数据的初始序列号,ACK的确认号是本端所期待的下一个序列号,SYN和FIN都占领一个字节的序列号空间;

SYN中携带的TCP选项:

MSS:告知对端本端在本连接中得每一个TCP分节中愿意接受的最大数据量,发送端TCP使用接收端的MSS作为所发送分节的最大大小,我们能够通过TCP_MAXSEG套接字选项提取和设置这个TCP选项,TCP_MAXSEG选项原本是仅仅读选项,4.4BSD限制应用进程仅仅能降低其值,不能添加其值。

窗体规模选项:TCP连接不论什么一端可以通告对端的最大窗体大小是65535,由于在TCP首部中,对应地字段占16位;SO_RCVBUF套接字选项影响这个TCP选项,套接字接收缓冲区中可用空间的大小限定了TCP通告对端的窗体大小;

时间戳选项:这个选项对于快速网络连接是必要的,它能够防止由失而复得的分组可能造成的数据损坏,这个失而复得是指由临时的路由原因造成的迷途,路由稳定后又正常到达目的地,快速网络中32位的序列号非常快就可能循环一轮又一次使用,假设不用时间戳选项,失而复得的分组所承载的分节可能与再次使用同样序列号的真正分节发生混淆;

connect(套接字默认堵塞)出错返回的情况:

1. 调用connect时内核发送一个SYN分节,若无响应则等待6s后再次发送一个,仍无响应则等待24s再发送一个,若总共等了75s后仍未收到响应则返回ETIMEDOUT错误;

2. 若对客户的SYN的响应是RST,则表示该server主机在我们指定的port上面没有进程在等待与之连接,比如server进程没执行,客户收到RST就立即返回ECONNREFUSED错误;

3. 若客户发出的SYN在中间的某个路由上引发了一个“destination unreachable”(目的不可达)ICMP错误,客户主机内核保存该消息,并按1中所述的时间间隔发送SYN,在某个规定的时间(4.4BSD规定75s)仍未收到响应,则把保存的ICMP错误作为EHOSTUNREACHENETUNREACH错误返回给进程。

若connect失败则该套接字不再可用,必须关闭,我们不能对这种套接字再次调用connect函数。

在每次connect失败后,都必须close当前套接字描写叙述符并又一次调用socket。

我们重现一下这些错误:

首先看下下面系统定义:

#define
ENETUNREACH 51
/* Network is unreachable */

#define
ETIMEDOUT 60
/* Operation timed out */

#define
ECONNREFUSED 61
/* Connection refused */

client:

<span style="font-size:12px;">int main(int argc, const char * argv[])
{ struct sockaddr_in serverAdd; bzero(&serverAdd, sizeof(serverAdd));
serverAdd.sin_family = AF_INET;
serverAdd.sin_addr.s_addr = inet_addr(SERV_ADDR);
serverAdd.sin_port = htons(SERV_PORT); int connfd = socket(AF_INET, SOCK_STREAM, 0); int connResult = connect(connfd, (struct sockaddr *)&serverAdd, sizeof(serverAdd));
if (connResult < 0) {
printf("连接失败,errno = %d\n",errno);
close(connfd);
return -1;
}
else
{
printf("连接成功\n");
}
close(connfd);
return 0;
}
</span>

服务端:

<span style="font-size:12px;">int main(int argc, const char * argv[])
{ struct sockaddr_in serverAdd;
struct sockaddr_in clientAdd; bzero(&serverAdd, sizeof(serverAdd));
serverAdd.sin_family = AF_INET;
serverAdd.sin_addr.s_addr = htonl(INADDR_ANY);
serverAdd.sin_port = htons(SERV_PORT); socklen_t clientAddrLen; int listenfd = socket(AF_INET, SOCK_STREAM, 0);
int yes = 1;
setsockopt(listenfd,
SOL_SOCKET, SO_REUSEADDR,
(void *)&yes, sizeof(yes)); if (listenfd < 0) {
printf("创建socket失败\n");
close(listenfd);
return -1;
} int bindResult = bind(listenfd, (struct sockaddr *)&serverAdd, sizeof(serverAdd));
if (bindResult < 0) {
close(listenfd);
printf("绑定port失败,errno = %d\n",errno);
return -1;
}
else
{
printf("绑定port成功\n");
} // listen(listenfd, 20);
sleep(60*5); return 0;
}
</span>

先执行服务端,再执行client,client在休眠一段时间以后会复现第一个错误,client打印例如以下信息:

连接失败,errno
60

不执行服务端,直接执行client会复现另外一种情况,直接打印例如以下信息:

连接失败,errno
61

关掉wifi,直接执行会复现第三种情况,直接打印例如以下信息:

连接失败,errno
 51

这里有个疑问,要是服务端打开屏蔽listen的那行代码会怎么样,再执行,client打印:

连接成功

我们服务端没有调用accept代码呀,这是由于调用listen方法后,内核为不论什么一个给定的监听套接字维护两个队列:未完毕连接队列和已完毕连接队列例如以下图所看到的;当客户SYN到达时,假设队列是满的,TCP就忽略该分节,但不会发送RST;当进程调用accept时,已完毕队列的对头项将返回给进程,假设队列是空,则堵塞(套接字默认堵塞);

也就是说仅仅要我调用了listen方法后,服务端就打开了三次握手的开关,可以处理来自client的SYN分节了,仅仅要三次握手完毕,client就会connect成功,而跟服务端调用accept没不论什么关系,accept仅仅是去取已完毕连接队列的对头项。

如图为TCP监听套接字的两个队列:

參考:

《UNIX Network ProgrammingVolume 1, Third Edition: TheSockets Networking API》

网络编程Socket之TCP之connect具体解释的更多相关文章

  1. java网络编程socket\server\TCP笔记(转)

    java网络编程socket\server\TCP笔记(转) 2012-12-14 08:30:04|  分类: Socket |  标签:java  |举报|字号 订阅     1 TCP的开销 a ...

  2. 网络编程Socket之TCP之close/shutdown具体解释(续)

    接着上一篇网络编程Socket之TCP之close/shutdown具体解释 如今我们看看对于不同情况的close的返回情况和可能遇到的一些问题: 1.默认操作的close 说明:我们已经知道writ ...

  3. python网络编程(Socket、TCP、UDP)

    Socket 是网络编程的一个抽象概念,通常我们用一个Socket表示 "打开了一个网络链接",而打开一个Socket 需要知道目标计算机的IP 地址和端口号,再指定协议类型即可. ...

  4. IPv6下网络编程socket, TCP和UDP例子,以及兼容IPV4和IPV6的类

    一.TCP socket ipv6与ipv4的区别 服务器端源代码如下: #include <stdio.h> #include <stdlib.h> #include < ...

  5. 网络编程Socket之TCP

            服务端: 1. 创建 ServerSocket 对象并监听一个端口 2. 调用accept()方法等待客户端的连接(阻塞式) 3. 输入流(记取客户端发送过来的数据) 4. 输出流(响 ...

  6. 网络编程Socket它TCP它TIME_WAIT国家具体解释

    下面我们用最简单的一对一的客户server编程模型重现遇到的一些问题: 初学者socket当写作socket名其妙的问题.比方说bind函数返回的常见错误是EADDRINUSE 使用以下的程序重现这个 ...

  7. 二、网络编程-socket之TCP协议开发客户端和服务端通信

    知识点:之前讲的udp协议传输数据是不安全的,不可靠不稳定的,tcp协议传输数据安全可靠,因为它们的通讯机制是不一样的.udp是用户数据报传输,也就是直接丢一个数据包给另外一个程序,就好比寄信给别人, ...

  8. python_网络编程socket(TCP)

    服务端: import socket sk = socket.socket() #创建对象 sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) ...

  9. Python网络编程02 /基于TCP、UDP协议的socket简单的通信、字符串转bytes类型

    Python网络编程02 /基于TCP.UDP协议的socket简单的通信.字符串转bytes类型 目录 Python网络编程02 /基于TCP.UDP协议的socket简单的通信.字符串转bytes ...

随机推荐

  1. FragmentPagerAdapter和FragmentStatePagerAdapter的差别

    ViewPager同意用户通过左右滑动显示不同页面的数据.而这些页面须要PagerAdapter管理. 经常使用的有FragmentPagerAdapter和FragmentStatePagerAda ...

  2. php实现变态跳台阶(记忆化递归)

    php实现变态跳台阶(记忆化递归) 一.总结 1.本题思路(分类讨论思路,注意初始值和边界值):第一步如果1,那剩下的就是jumpFloorII($number-1)(下面jumpFloorII以j表 ...

  3. NoSql中的B-tree、B+tree和LSM-tree 分类: B7_HBASE 2015-03-15 18:27 85人阅读 评论(0) 收藏

    总结: 1.B+树将数据完全排序,读数据时很快,但当要修改数据时,就需要将新入数据下面的数据重新排位,特别是当写入的数据排在较高的位置时,需要大量的移位操作才能完成写入. 2.SLM牺牲部分的读性能, ...

  4. 【u123】最大子段和

    Time Limit: 1 second Memory Limit: 128 MB [问题描述] 给出一段序列,选出其中连续且非空的一段使得这段和最大. [输入格式] 输入文件maxsum1.in的第 ...

  5. SAP 中的popup dialog (弹出对话框) 常见实现方法

      方法1: FM:POPUP_TO_CONFIRM(标准对话弹出消息) 有三个button:YES-NO-CANL,可进行对应的逻辑推断 可设定标题,描写叙述问题,不方便对文本进行换行等排版,不能改 ...

  6. Android 为开发者准备的最佳 Android 函数库(2016 年版)

    本文是翻译自 CloudRAIL 的官方博客(https://cloudrail.com/best-android-libraries-for-developers/),本文中分享的 Android ...

  7. 【AJAX】AJAX实现搜索信息自己主动推荐并补全

    好久没有继续看AJAX的视频教程了,今天就将最后一个教程案例做完.我们在搜索引擎中输入文字时文本框下会提示对应的信息,这个案例就是实现这样的基本功能,代码比較粗糙还须要进一步完好,当中有些地方也须要向 ...

  8. js进阶 11-9/10/11 jquery创建和插入节点

    js进阶 11-9/10/11 jquery创建和插入节点 一.总结 一句话总结: 1.jquery插入节点8个方法? 内部之前,内部之后,之前,之后:各两个 append()和appendTo() ...

  9. Erlang基础知识集锦

    http://wenku.baidu.com/link?url=or-8mkUYUM0uVeqCYESGe93YIlh2IDLP7lFOwRlwr8Syf3PeHbwJC5DPCErs4NFrb1p4 ...

  10. 不使用left-join等多表关联查询,只用单表查询和Java程序,简便实现“多表查询”效果

    上次我们提到,不使用left-loin关联查询,可能是为了提高效率或者配置缓存,也可以简化一下sql语句的编写.只写单表查询,sql真得太简单了.问题是,查询多个表的数据还是非常需要的. 因此,存在这 ...