【Linux网络】网络通信:Socket编程入门 目录[TOC](目录)1 ~ 网络通信的本质2~ 理解源IP地址和目的IP地址3~ 认识端口号Port3.1 端口号的范围划分3.2 理解socket3.3 端口号和进程PID4 ~ 网络字节序4.1 为什么要规定网络字节序4.2 网络字节序转化5 ~ Socket编程接口5.1 相关API介绍5.2 结构体家族5.2 传输层协议 UDP和TCP5.2.1 认识UDP协议5.2.2 认识TCP协议6 ~ 总结1 ~ 网络通信的本质网络通信的本质就是两个主机的进程之间在通信网络就是两个进程看到的同一份资源。即两个进程通过网络进行数据交互从远端获取数据和将数据发送到远端。那么要能够通信就需要主机A要能够找到主机B进程A要能够找到进程B。2~ 理解源IP地址和目的IP地址IP在网络中用来标识唯一的一台主机。源 IP 地址标识数据包的发送方主机。目的 IP 地址标识数据包的接收方主机。通过局部到整体数据包由主机A交到主机B但最终还需要将数据交给进程处理。但一个主机中有非常多的进程所以就需要对目标进程进行标记。3~ 认识端口号Port端口号是一个2字节的16位整数用来标识一台主机中唯一的进程。3.1 端口号的范围划分0 ~ 1023系统端口/知名端口HTTP,FTP,SSH等这些广为使用的应用层协议, 他们的端口号都是固定的。即约定这些端口号为特定的服务所占用方便客户端同一访问如报警打110。1024 - 65535操作系统动态分配的端口号。客户端程序的端口号 就是由操作系统从这个范围分配的。未来我们在进行套接字通信时指定的端口就在这个范围。注意一个进程可以绑定多个端口号但一个端口号只能被一个进程绑定3.2 理解socketIP标识唯一的主机端口号标识主机上唯一的进程。那么IP Port即对应互联网中唯一的进程即套接字。通过套接字主机A上的进程A就能够把数据准确交给主机B上的进程B了。3.3 端口号和进程PID当我们创建一个进程操作系统会自动为进程分配PIDPID标识唯一的进程。那为什么不直接用IP PID首先PID是系统概念这样不符合解耦设计的分层哲学其次端口号将来需要被客户端明确知道而端口号由客户端自己约定比较方便。更何况不是所有进程都需要网络通信4 ~ 网络字节序所有在网络中传输的多字节数据必须使用大端序编码这称为网络字节序。4.1 为什么要规定网络字节序计算机存储多字节数据有两种顺序例如存储0x12345678大端存储高字节位放在低地址处即12 34 56 78符合我们的阅读习惯。小端存储高字节位存储在该地址处即78 56 34 12。假如主机A大端想要将0x12345678发送给主机B小端最终主机B存的将是12 34 56 78刚好相反。所以就需要一个标准规定发送到网络中的必须是大端序列。大端机器正常读小端机器在读之前先将大端 - 小端即可。4.2 网络字节序转化主机将数据发送到网络必须先转化为大端序htonsHost to Network Short将端口号转化为网络字节序htonlHost to Network Long将IP转化为网络字节序从网络读取的数据必须转化为自身序列ntohs将端口号由网络字节序转为主机序列ntohl将IP由网络字节序转为主机序列5 ~ Socket编程接口Socket接口是应用层进入传输层的入口即用户层通过系统调用API触发用户态与内核态之间的数据拷贝从而间接完成网络数据的收发。5.1 相关API介绍1创建套接字(TCP/UDP服务端客户端intsocket(intdomain,inttype,intprotocol);参数含义常用取值domain协议族/地址族AF_INET(IPv4)、AF_INET6(IPv6)、AF_UNIX(本地套接字)type套接字类型SOCK_STREAM(TCP)、SOCK_DGRAM(UDP)、SOCK_RAW(原始套接字)protocol具体协议通常填 0让系统根据 domaintype 自动选择IPPROTO_TCP、IPPROTO_UDP、IPPROTO_ICMPdomain 协议族取值说明AF_INETIPv4 地址使用 sockaddr_in 结构体地址是 32 位AF_INET6IPv6 地址使用 sockaddr_in6 结构体地址是 128 位type套接字类型取值说明对应协议SOCK_STREAM面向连接、可靠、字节流、有序、无边界TCPSOCK_DGRAM无连接、不可靠、数据报、有边界、可能乱序/丢失UDP// ------------------代码示例-------------------------// 创建一个 TCP/IPv4 套接字intsockfdsocket(AF_INET,SOCK_STREAM,0);// 创建一个 UDP/IPv4 套接字intsockfdsocket(AF_INET,SOCK_DGRAM,0);2绑定TCP/UDP服务端它把一个具体的地址IP 端口绑定到套接字上告诉操作系统这个端口归我管有数据来就交给我。intbind(intsocket,conststructsockaddr*address,socklen_taddress_len);参数类型作用sockfdintsocket()创建的套接字文件描述符addrconst struct sockaddr *指向地址结构体的指针包含 IP 端口addrlensocklen_taddr指向结构体的实际字节长度3监听TCP服务端intlisten(intsocket,intbacklog);如果创建套接字和绑定是开饭馆和挂牌子那么listen就是让饭馆开始营业backlog代表饭馆能坐多少桌。所以只有监听后即套接字处于listen状态才能够接收数据并放入队列等待处理。4接收请求TCP服务端intaccept(intsocket,structsockaddr*address,socklen_t*address_len);服务端阻塞等待当客户端发起连接connect此时客户端accept接受请求并返回一个全新的套接字描述符专门用于与这个客户端通信。address为输出型参数存储了客户端的IP Port。5建立连接TCP 客户端intconnect(intsockfd,conststructsockaddr*addr,socklen_taddrlen);向服务器发起连接addr存储了要连接的服务端的IP Port。5.2 结构体家族sockaddr结构体——基类structsockaddr{sa_family_tsa_family;// 地址族如 AF_INETcharsa_data[14];// 存储具体地址数据但空间不够只是个占位符};sockaddr_in结构体——真正的 IPv4“身份证”包含了IP和Port信息就可以与对应进程进行数据交互。structsockaddr_in{sa_family_tsin_family;// 必须填 AF_INETin_port_tsin_port;// 端口号必须 htons 转换structin_addrsin_addr;// IP 地址必须 htonl 或 inet_pton 转换charsin_zero[8];// 填充位保证总长度与 sockaddr 一致};structin_addr{in_addr_ts_addr;// 真正的IP};sockaddr_un结构体——本地通信的“身份证”structsockaddr_un{sa_family_tsun_family;// 必须填 AF_UNIXcharsun_path[108];// 套接字文件的路径如 /tmp/my.sock};struct sockaddr *address是基类指针使用sockaddr_in和sockaddr_un时需要强转。5.2 传输层协议 UDP和TCP5.2.1 认识UDP协议无连接不可靠传输面向数据报UDP通信流程客户端不需要bind()系统自动分配临时端口数据报之间相互独立可能丢包、乱序5.2.2 认识TCP协议有连接可靠传输面向字节流服务端必须bind listen accept三步曲客户端必须connect主动建立连接。accept()返回的新套接字用于通信原监听套接字继续等新连接。TCP是字节流无消息边界需要应用层自行处理。6 ~ 总结网络编程的核心是跨主机的进程间通信IP Port标识两个唯一的进程通过 TCP/IP 协议栈完成数据交换。理论准备完成那么我们就可以接着进行网络通信实战了