本文共 5354 字,大约阅读时间需要 17 分钟。
还是那句话,我们要学会使用man查看
根据指定的协议族、套接字类型和协议来分配一个的描述符及其所用的资源,返回分配的套接字描述符
#include/* See NOTES */ #include int socket(int domain, int type, int protocol);
此处需要重点理解socket每个参数的含义:
domain:指定协议族,我们可以通过man来查看不同类unix操作系统提供的常量值。在我的ubuntu14.04中
我们常用的AF_UNIX(Unix域协议),AF_INET(ipv4协议),AF_INET6(ipv6协议)
*****************socket在网络编程中的调用位置******************************************
#include/* See NOTES */#include int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
知识点总结:
情况3:若客户发出的SYN在中间的某个路由器上引发了一个“destination unreachable"(目的地不可达)ICMP错误,则认为是一个软错误。客户主机内核保存该信息,并按重传机制间隔继续发送SYN。若在某个规定的时间后仍未收到响应,则把保存的消息(即ICMP)作为EHOSTUNREACH或ENETUNREACH错误返回给进程。以下情况也是可能的:一是按照本地系统的路由表,根本没有到达远程系统的路径。二是connect调用根本不等待直接返回。
RST解析:
调试验证(纸上得来终觉浅,绝知此事要躬行)
运行书中时间服务程序 intro目录下
sudo ./daytimetcpsrv
另开一个终端,使用netstat命令查看服务是否成功开启,结果如下sunxiaowu@sunxiaowu:~$ sudo netstat -anp | grep 'daytime'tcp 0 0 0.0.0.0:13 0.0.0.0:* LISTEN 2714/daytimetcpsrvsunxiaowu@sunxiaowu:~$运行客户端时间获取程序
sunxiaowu@sunxiaowu:~/Downloads/unpv13e/intro$ ./daytimetcpcli 127.0.0.1Fri Jul 7 10:53:55 2017sunxiaowu@sunxiaowu:~/Downloads/unpv13e/intro$成功获得服务器时间
现在我们来模拟connect出错情况1
我们尝试指定主机不存在的一个IP地址,这样当客户主机发送请求(要求那个不存在的主机响应以其硬件地址)时,它将永远收不到ARP响应(这个ARP响应是什么,以后详解,可以参看tcp/ip详解),收不到SYN分节的响应。
sunxiaowu@sunxiaowu:~/Downloads/unpv13e/intro$ ./daytimetcpcli 192.122.2.2connect error: Connection timed outsunxiaowu@sunxiaowu:~/Downloads/unpv13e/intro$这个等待的具体时间可能有点长(中间会涉及tcp的重传机制),注意看报错的函数,正是connect
现在我们来模拟connect出错情况2
典型情况是可以通过ip找到主机,也就是主机会有回应,但客户端请求的服务端口并没有进程等待与之连接。我们可以把时间服务程序关掉,直接运行客户端时间获取程序来模拟此种错误。
sunxiaowu@sunxiaowu:~/Downloads/unpv13e/intro$ sudo netstat -anp |grep 'daytime' [sudo] password for sunxiaowu: tcp 0 0 0.0.0.0:13 0.0.0.0:* LISTEN 2714/daytimetcpsrvsunxiaowu@sunxiaowu:~/Downloads/unpv13e/intro$ sudo kill 2714sunxiaowu@sunxiaowu:~/Downloads/unpv13e/intro$ sudo netstat -anp |grep 'daytime'sunxiaowu@sunxiaowu:~/Downloads/unpv13e/intro$ ./daytimetcpcli 127.0.0.1connect error: Connection refusedsunxiaowu@sunxiaowu:~/Downloads/unpv13e/intro$注意看报错,主机会响应一个RST分节,导致connect报错,报错信息connection refused
现在我们来模拟connect出错情况3
我们指定一个不可到达的ip地址。
sunxiaowu@sunxiaowu:~/Downloads/unpv13e/intro$ ./daytimetcpcli 192.168.1.33connect error: No route to hostsunxiaowu@sunxiaowu:~/Downloads/unpv13e/intro$connect总结:
结合tcp状态图(可以查看我的另一篇博文:
SYN_SENT -> CONNECTED
把一个本地协议地址赋予一个套接字。
#include/* See NOTES */ #include int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
此函数直接查看man手册学习吧
注意一点,bind返回的而一个常见错误是EADDRINUSE(地址已使用)
#include/* See NOTES */ #include int listen(int sockfd, int backlog);
重点:内核会为任何一个给定的监听套接字维护两个队列:
这个在面试中也是被人家常问的(内核具体怎么维护这两个队列,以后在内核解析中慢慢讲述)
当来自客户的SYN到达时,TCP在未完成连接队列中创建一个新项,然后响应以三路握手的第二个分节。这一项一直保留在未完成队列中,直到三路握手的第三个分节到达或者该项超时为止。若三路握手正常完成,该项就从未完成连接队列转移到已完成连接队列的队尾。当进程调用accept,已完成连接队列中的对头项将返回给进程。如果队列为空,则进程将被投入睡眠,直到tcp在该队列中放入一项才唤醒它。
如何获取系统支持的最大LISTENQ?
NAME getenv, secure_getenv - get an environment variableSYNOPSIS #includechar *getenv(const char *name); char *secure_getenv(const char *name);
重点:
当一个客户SYN到达时,若这些队列是满的,TCP就忽略该TCP分节,也就是不发送RST。客户TCP将重发SYN,期望不久能在队列中找到可用空间。
在三路握手完成之后,但在服务器调用accept之前到达的数据应由服务器tcp排队,最大数据量为相应已连接套接字的接收缓冲区大小。
#include<sys/socket.h>
int accept(int sockfd,struct sockaddr *cliaddr,socklen_t *addrlen);
返回值:若成功则为非负描述符,若出错则为-1
#include返回:在子进程中为0,父进程中为子进程ID,若出错则为-1pid_t fork(void);
fork两种典型用法:
略
#include用来关闭套接字,并终止tcp连接int close(int fd);
重点:close一个tcp套接字的默认行为是把该套接字标记为已关闭(导致相应描述符的引用计数值减1),然后立即返回到调用进程。该套接字描述符不能再由调用进程使用(不能再作为read或write的第一个参数)
注意,此时tcp将尝试发送已排队等待的到对端的任何数据(发送队列),发送完毕后发生的是正常的tcp连接终止序列(计数为0才会引发四分组连接终止)。
#includeint shutdown(int sockfd,int howto);若成功则为0,出错返回-1
终止网络的通常方法:调用close函数。不过close有两个限制,却可以使用shutdown避免