对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. UIActionSheet上加入UIPickerView iOS8替换方案

    此套替换方案採用"UIView+动画"方式实现(将UIActionSheet替换为UIView) 界面层级例如以下: 第一层:view(这一层充满整个屏幕,初始化时颜色为透明.us ...

  2. UI组件之TextView及其子类(三)ToggleButton和Switch

    ToggleButton.Switch.CheckBox和RadioButton都是继承自android.widget.CompoundButton,意思是可选择的,因此它们的使用方法都非常类似. C ...

  3. 【LeetCode-面试算法经典-Java实现】【104-Maximum Depth of Binary Tree(二叉树的最大深度)】

    [104-Maximum Depth of Binary Tree(二叉树的最大深度)] [LeetCode-面试算法经典-Java实现][全部题目文件夹索引] 原题 Given a binary t ...

  4. css3-11 如何设置文字的阴影

    css3-11 如何设置文字的阴影 一.总结 一句话总结:text-shadow属性.text-shadow:3px 3px 3px #f0f; 1.text-shadow属性的参数依次是什么意思? ...

  5. 关于JavaScript概念的总结

    原文 https://www.jianshu.com/p/1e8d8a691aa8 大纲 1.JavaScript的概念 2.JavaScript 特点 3.JavaScript是弱类型语言 4.Ja ...

  6. 我眼中的c++编程总结-20150602

    断断续续的学习了非常多东西,有51.Avr.ARM.PLC.C\C++.C#.TB.MC.mql4.linux....等等,近乎填鸭或者囫囵吞枣的.甚至饿狼般的扑到里面,慢慢的积累和理解中,非常多知识 ...

  7. 关于 rman duplicate from active database 搭建dataguard--系列一

    关于 rman duplicate from active database.详细操作实际为backup as copy .会拷贝非常多空块.对于那些数据库数据文件超过100G的都不是非常建议用:在非 ...

  8. JS调用ATL COM中的C++接口的做法

    作者:朱金灿 来源:http://blog.csdn.net/clever101 首先创建一个ATL COM对象,其过程参考下面文章: C#调用ATLCOM 其实给COM对象添加方法和属性可以不用界面 ...

  9. 【BZOJ 1008】[HNOI2008]越狱

    [题目链接]:http://www.lydsy.com/JudgeOnline/problem.php?id=1008 [题意] [题解] 相邻就会犯罪的话; 可以考虑它的反面; 即让所有相同信仰的人 ...

  10. 如何在使Xcode打包iOS应用时自动增加编译号

    在红框标注的输入框中输入:真机调试编译成功增加 echo $CONFIGURATION if [ "Release" == "${CONFIGURATION}" ...