本篇主干内容是TCP/IP网络编程1-9章学习笔记

1. linux文件描述符

描述符从3开始以由小到大的顺序编号,0,1,2,分配给标准I/O用作标准输入、标准输出和标准错误。

2. 协议族与套接字类型(socket函数第一、二个参数)

domain常用 : IPv4协议族 PF_INET;

type: SOCK_STREAM(面向连接,TCP), SOCK_DRGAM(面向消息, UDP)

选用TCP时,第三个参数可以为0。

3.地址族与数据序列

4字节IP地址分类:

A类地址: 首字节范围0~127(首位以0开始);

B类地址: 首字节范围128~191(前两位以10开始);

C类地址:首字节范围192~223(前3位以110开始).

端口号:操作系统参考端口号把数据传输给相应端口的套接字。端口号范围0~65535,其中0~1023位知名端口。

4.IPv4地址结构体

struct sockaddr_in {
sa_family_t sin_family; //地址族
uint16_t sin_port; //16位TCP/UDP端口号
struct in_addr sin_addr; //32位IP地址
char sin_zero[]; //补位,不使用
};

其中in_addr定义如下:

struct in_addr {
in_addr_t s_addr; //32位IPv4地址
}

注:1)sin_zero为了让sockaddr_in的大小与sockaddr结构体保持一致,便于传参的时候进行强制类型转换。

  2)为什么不直接填写sockaddr而要填写sockaddr_in再强转是因为sockaddr定义中一个sa_data[14]表示地址信息,不好填入。

  3)sockaddr_in是专为IPv4设计,但其仍然要传入地址族信息是由于sockaddr的要求,sockaddr中需要地址族信息(其不是为IPv4单独设计)。

5. 网络字节序

大端序:先保存高位字节,再保存低位字节;

例子存0x12345678,在大端序中,内存的分布为:

内存号 0x20号 0x21号 0x22号 0x23号
存储内容 0x12 0x34 0x56 0x78

小端序:先保存低位字节,再保存高位字节。

内存号 0x20号 0x21号 0x22号 0x23号
存储内容 0x78 0x56 0x34 0x12

CPU存储时多用小端序(Intel和AMD均是),网络字节序为大端序。

字节序转换函数:htons,htonl,ntohs,ntohl (h代表主机,n代表网络,l代表long,s代表short)。

注:除了向sockaddr_in结构体填充数据外,其余情况无需考虑字节序问题。

6. 网络地址初始化与分配

字符串形式的IP地址与32位整数型数据的转换:

#include <arpa/inet.h>

in_addr_t inet_addr(const char* s);          //成功返回32位大端序整数,失败返回INADDR_NONE 
int inet_aton(const char* s, struct in_addr* add) //成功返回1,失败返回0

二者功能相同,inet_addr需要将转换后的IP地址待遇sockaddr_in结构体声明的in_addr结构体变量中。

inet_aton则通过参数自动把结果填入该结构体遍历。

调用方法:

 if (!inet_aton(addr, &addr_inet.sin_addr)) { //addr存放字符串形式的IP地址(点分十进制)
error_handling("Conversion error");
}

INADDR_ANY可以自动获取IP地址,而不必手动输入。

7. TCP服务器端函数 (listen与等待连接请求;accept与受理连接请求)

#include <sys/socket.h>
int listen(int sock, int backlog); //backlog表示连接请求等待队列的长度

客户端请求连接时,受理之前一直使请求处于等待状态。

#include <sys/socket.h>
int accept(int sock, struct sockaddr* addr, socklen_t* addrlen);

addr保存发起连接请求的客户端地址信息的变量地址值。

accept函数受理连接请求等待队列中待处理的客户端连接请求。

函数调用成功后,accept函数内部产生用于数据I/O的套接字,并返回其文件描述符,这个套接字创建是自动的,并自动与发起连接请求的客户端建立连接。

8. TCP客户端函数 (connect与请求连接)

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

servaddr保持目标服务器端地址信息。

connect在服务器端接受连接请求后返回,但注意这里的接受连接是指把请求信息放在等待队列,所以connect函数返回后并不立即进行数据交换(等待accept).

9. TCP的服务器端/客户端函数调用关系

注:客户端只能等到服务器调用listen之后才能调用connect。但是在客户端调用connect之前,服务端可能率先调用accept,这时服务端进入阻塞状态,只到客户端调用connect函数为止。

10. TCP套接字中的I/O缓冲

write函数调用,数据移至输出缓冲;read函数调用,从输入缓冲读取数据。

1)I/O缓冲在每个TCP套接字中单独存在;

2)I/O缓冲在创建套接字时自动生成;

3) 即使关闭套接字也会继续传递输出缓冲中遗留的数据;

4)关闭套接字将丢失输入缓冲中的数据。

11. TCP的半关闭

1) 单方面断开可能带来的问题:

主机A调用close后,主机B传输给主机A的数据也无法接受。

2)

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

howto取 SHUT_RD 断开输入流;SHUT_WR断开输出流; SHUT_RDWR同时断开I/O流

3)断开输出流时向对方主机传递EOF。

12 IP地址与域名的转换

IP地址比域名发生变更的频率高,利用IP域名编写程序是更好的选择,所以说,程序需要IP地址和域名之间的转换函数。

#include <netdb.h>
struct hostent* gethostbyname(const char* hostname);// 成果返回hostent结构体地址,失败返回NULL指针

关于hostent结构体:

struct hostent {
char* h_name; // official name
char** h_aliases; //alias list
int h_addrtype; //host address type
int h_length; //address length
char** h_addr_list; //address list
};

最重要的成员是 h_addr_list,可以通过此变量与整数形式保存域名对应的IP地址。用户较多的网站有可能分配多个IP给同一个域名。

h_addr_list指向字符串指针数组,字符串指针数组中的元素实际是in_addr结构体变量的地址值(声明为char*而不是in_addr*因为hostent不仅仅为IPv4准备)。

如下图:

13. 几个基础的套接字选项

1) 如何获取和更改套接字选项:

#include <sys/socket.h>
int getsockopt(int sock, int level, int optname, const void* optval, socklen_t* optlen); int setsockopt(int sock, int level, int optname, const void* optval, socklen_t* optlen);

sock:用于查看选项套接字的文件描述符;

level:要查看的可选项的协议层。有SOL_SOCKET(套接字相关), IPPROTO_IP(IP相关), IPPROTO_TCP(TCP协议相关)

optname:要查看的可选项名;

optval: 保持查看结果的缓冲地址值;

optlen:optval的缓冲大小。getsockopt调用函数后,保存可选项信息的字节数。

举例:(通过SO_TYPE查看套接字类型)

 int sock_type;
int tcp_sock = socket(PF_INET, SOCK_STREAM, );
int state = getsockopt(tcp_sock, SOL_SOCKET, SO_TYPE, (void*)&sock_type, &optlen);
if (state) {
error_handling("getsocket() error");
}
else {
printf("SOCKET_STREAM: %d \n", sock_type);
}

2) SO_SNDBUF & SO_REVBUF

SO_SNDBUF 是输入缓冲大小相关可选项;SO_REVBUF是输出缓冲大小相关可选项。

用这两个可选项既可以读取当前I/O缓冲大小, 也可以进行修改。(注:修改时不会完全按照请求更改大小)

3) SO_REUSEADDR 和 Time_wait

Time_wait存在的原因:

四次挥手中最后一个ACK(A回复的)可能在途中丢失,这样主机B会重传FIN消息。

但如果没有Time_wait状态,则主机A已经处在完全终止的状态,这个重传的信息便永远无法到达A。

基于这些考虑,先传FIN消息的主机应经过Time_wait状态。

Time_wait可能带来的问题:

网络状态不理想时,Time_wait会一直持续;想要快速重启服务器时,Time_wait导致必须等待几分钟。

SO_REUSEADDR可以将Time_wait状态下的套接字端口号重新分配给新的套接字:

 int optlen = sizeof(option);
int option = ;
setcsockopt(serv_sock, SOL_SOCKET, SO_REUSEADDR, (void*)&option, optlen);

注:客户端套接字端口号任意指定,所以每次运行程序时会动态分配端口号,故无需过多关注客户端Time_wait

参考文献:

尹圣雨 《TCP/IP网络编程》 https://book.douban.com/subject/25911735/

TCP/IP网络编程 读书笔记1的更多相关文章

  1. 《TCP/IP网络编程》学习笔记整理

    简介 本笔记目前已包含 <TCP/IP网络编程>中的前 5 章,后续章节会在近期内补充完整. 我在整理笔记时所考虑的是:在笔记记完后,当我需要查找某个知识点时,不需要到书中去找,只需查看笔 ...

  2. 浅谈TCP/IP网络编程中socket的行为

    我认为,想要熟练掌握Linux下的TCP/IP网络编程,至少有三个层面的知识需要熟悉: 1. TCP/IP协议(如连接的建立和终止.重传和确认.滑动窗口和拥塞控制等等) 2. Socket I/O系统 ...

  3. 《TCP/IP网络编程》

    <TCP/IP网络编程> 基本信息 作者: (韩)尹圣雨 译者: 金国哲 丛书名: 图灵程序设计丛书 出版社:人民邮电出版社 ISBN:9787115358851 上架时间:2014-6- ...

  4. TCP/IP网络编程系列之四(初级)

    TCP/IP网络编程系列之四-基于TCP的服务端/客户端 理解TCP和UDP 根据数据传输方式的不同,基于网络协议的套接字一般分为TCP和UDP套接字.因为TCP套接字是面向连接的,因此又称为基于流的 ...

  5. TCP/IP网络编程系列之三(初级)

    TCP/IP网络编程系列之三-地址族与数据序列 分配给套接字的IP地址和端口 IP是Internet Protocol (网络协议)的简写,是为首发网络数据而分配给计算机的值.端口号并非赋予计算机值, ...

  6. TCP/IP网络编程系列之二(初级)

    套接字类型与协议设置 我们先了解一下创建套接字的那个函数 int socket(int domain,int type,int protocol);成功时返回文件描述符,失败时返回-1.其中,doma ...

  7. TCP/IP网络编程之多线程服务端的实现(二)

    线程存在的问题和临界区 上一章TCP/IP网络编程之多线程服务端的实现(一)的thread4.c中,我们发现多线程对同一变量进行加减,最后的结果居然不是我们预料之内的.其实,如果多执行几次程序,会发现 ...

  8. TCP/IP网络编程之优于select的epoll(二)

    基于epoll的回声服务端 在TCP/IP网络编程之优于select的epoll(一)这一章中,我们介绍了epoll的相关函数,接下来给出基于epoll的回声服务端示例. echo_epollserv ...

  9. TCP/IP网络编程之进程间通信

    进程间通信基本概念 进程间通信意味着两个不同进程间可以交换数据,为了完成这一点,操作系统中应提供两个进程可以同时访问的内存空间.但我们知道,进程具有完全独立的内存结构,就连通过fork函数创建的子进程 ...

随机推荐

  1. JPinyin繁体相互转换

    // 用正则表达式"[\u4e00-\u9fa5]"匹配 字符串 Scanner sc =new Scanner(System.in);System.out.println(&qu ...

  2. spring JdbcTemplate在spring的ioc中使用

    package com.com.jdbctemplate; import org.springframework.context.ApplicationContext; import org.spri ...

  3. centos下彻底删除mysql

    打算重新试试安装两个mysql,就把老的删除了. yum remove mysql mysql-server mysql-libs compat-mysql51 rm -rf /var/lib/mys ...

  4. js顺序加载与并行加载

    前端优化过程中常提到js的加载方式,下面说下几种常用的加载方式: 1:head标签内插入<script>标签 <script type="text/javaScript&q ...

  5. 洛谷P2381 圆圆舞蹈

    P2381 圆圆舞蹈 题目描述 熊大妈的乃修在时针的带领下,围成了一个圆圈舞蹈,由于没有严格的教育,奶牛们之间的间隔不一致. 奶牛想知道两只最远的奶牛到底隔了多远.奶牛A到B的距离为A顺时针走和逆时针 ...

  6. 洛谷P1080 [NOIP2012提高组D1T2]国王游戏 [2017年5月计划 清北学堂51精英班Day1]

    P1080 国王游戏 题目描述 恰逢 H 国国庆,国王邀请 n 位大臣来玩一个有奖游戏.首先,他让每个大臣在左.右 手上面分别写下一个整数,国王自己也在左.右手上各写一个整数.然后,让这 n 位大臣排 ...

  7. Leetcode50. Pow(x, n)(快速幂)

    实现 pow(x, n) ,即计算 x 的 n 次幂函数. 示例 1: 输入: 2.00000, 10 输出: 1024.00000 示例 2: 输入: 2.10000, 3 输出: 9.26100 ...

  8. gcd(辗转相除法)

    证明过程: 假设用f(x, y)表示x,y的最大公约数,取k = x/y,b = x%y,则x = ky + b,如果一个数能够同时整除x和y,则必能同时整除b和y:而能够同时整除b和y的数也必能同时 ...

  9. spark应用程序引用别的jar包

    第一种方式 操作:将第三方jar文件打包到最终形成的spark应用程序jar文件中 应用场景:第三方jar文件比较小,应用的地方比较少 第二种方式 操作:使用spark-submit提交命令的参数: ...

  10. Leetcode867.Transpose Matrix转置矩阵

    给定一个矩阵 A, 返回 A 的转置矩阵. 矩阵的转置是指将矩阵的主对角线翻转,交换矩阵的行索引与列索引. 示例 1: 输入:[[1,2,3],[4,5,6],[7,8,9]] 输出:[[1,4,7] ...