文章目录
Linux网络编程篇
一、网络编程的概念:
网络:
-
地址:
- IP地址
- 端口号
-
数据 :
- 协议(http,tcp, udp)
Socket 套接字:
1.TCP : 面向连接 如:A 打电话 B (可靠)
2.UDP: 面向报文 如:A 发短信给 B 数据量大 (不可靠)
1.1 TCP/UDP对比
- TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前 不需 要建立连接
- TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付
- TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如 IP电话,实时视频会议等)
- 每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
- TCP首部开销20字节;UDP的首部开销小,只有8个字节
- TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道
1.2 端口号作用
一台拥有IP地址的主机可以提供许多服务,比如Web服务、FTP服务、 SMTP服务等
这些服务完全可以通过1个IP地址来实现。那么,主机是怎样区分不同的网络服务呢?显然不能只靠IP地址,因为IP地址与网络服务的关系是一对多的关系。
实际上是通过“IP地址+端口号”来区 分不同的服务的。端口提供了一种访问通
端口号来识别的。例如,对于每个TCP/IP实现来说,FTP服务器的TCP端口号都是21,每个Telnet服务器的TCP端口号都是23,每个TFTP(简单文件传送协议)服务器的UDP端口号都是69
二、字节序:
字节序
2.1 字节序概念:
字节序是指多字节数据在计算机内存中存储或者网络传输时各自字节的存储顺序。
2.2 常见序:
- Little endian(小端字节序) :将低序字节存储在起始地址
- Big endian (大端字节序):将高序字节存储在起始地址
文件描述符
我们知道在Linux系统中一切皆可以看成是文件,文件又可分为:普通文件、目录文件、链接文件和设备文件。在操作这些所谓的文件的时候,我们每操作一次就找一次名字,这会耗费大量的时间和效率。所以Linux中规定每一个文件对应一个索引,这样要操作文件的时候,我们直接找到索引就可以对其进行操作了。
文件描述符(file descriptor)就是内核为了高效管理这些已经被打开的文件所创建的索引,其是一个非负整数(通常是小整数),用于指代被打开的文件,所有执行I/O操作的系统调用都通过文件描述符来实现。同时还规定系统刚刚启动的时候,0是标准输入,1是标准输出,2是标准错误。这意味着如果此时去打开一个新的文件,它的文件描述符会是3,再打开一个文件文件描述符就是4…
Linux内核对所有打开的文件有一个文件描述符表格,里面存储了每个文件描述符作为索引与一个打开文件相对应的关系,简单理解就是下图这样一个数组,文件描述符(索引)就是文件描述符表这个数组的下标,数组的内容就是指向一个个打开的文件的指针。
三、Socket 编程步骤
网络编程场景模拟:
假如我是 客户端 ,我正要 去朋友家(服务器端),不知道他家具体位置,我的面前有5个楼,我要去他家怎么办呢?
正在这时候,我的朋友突然喊我,他用的 汉语(TCP/UDP) 叫我名字,他说到:我在 XX号楼(IP地址) ,XX号房间(端口号),我就很明确我要去访问的地方 。他就在房间里 等待我敲门(监听) ,然后我俩就 问他玩游戏吗(请求数据) ,他回到,上号!(回应数据),玩完后,我离开离他家(结束连接),我走后他也去上班了。
1.socket ():创建套接字
2.bind() :为套接字添加信息(IP地址和端口号)
3.listen() ; 监听网络连接
4.accept():监听到有客户端接入,接受一个连接
5. 数据交互
6.close() : 关闭套接字,断开连接。
四、Linux提供的API简析
服务器端
1. 连接协议API:
int socket(int domain ,int type ,int protocol);
参数说明:
参数1:domain
domain : 指明所使用的协议族
通常为AF_INET,表示互联网协议族(TCP/IP协议族);
AF_INET IPv4 | 因特网域 |
AF_INET IPV6 | 因特网域 |
AF_UNIX | Unix域 |
AF_ROUTE | 路由器套接字 |
AF_KEY | 密钥套接字 |
AF_UNSPEC | 未指定 |
参数2:type
type: 指定socket的类型:
- SOCK_STREAM : 流式套接字提供可靠的,面向连接的通信流;它使用TCP协议,从而保证了数据传输的正确性和顺序性。
- SOCK_DGRAM
:数据报套接字定义了一种无连接的服,数据通过相互独立的报文进行传输,是无序的,并且不保证是可靠,无差错的。它使用数据报协议UDP。 - SOCK_RAW : 允许程序使用底层协议,原始套接字允许对底承协议如IP 或 ICMP进行直接访问,功能强大但使用较为不便,主要用于一些协议的开发。
参数3:protocol
protocol :通常赋值“0 ”
0 选择 type 类型对应的默认协议
IPPROTO_TCP | TCP传输协议 |
IPPROTO_UDP | UDP传输协议 |
IPPROTO_SCTP | SCTP传输协议 |
2.IP地址和端口号 API:
地址准备好:
bind()函数:IP号端口号相应描述字赋值函数
#include<sys/types.h>
#include<sys/socket.h>
int bind(int sockfd , const struct sockaddr *addr , socklen_t addrlen);
功能: 用于绑定IP地址和端口号到socketfd
参数说明:
sockfd : 是一个socket描述符
addr :是一个指向包含有本机IP地址及端口号等信息的sockaddr类型的指针,指向要绑定给sockfd的协议地址结构,这个地址结构根据地址创建socket时的地址协议族的不同而不同
//ipv4 对应的是:
struct sckaddr{
unisgned short as_family; //协议族
char sa_data[14]; //IP+端口
};
同等替换上面代码:
struct scockaddr_in {
sa_family_t sin_family; //协议族
in_port_t sin_port; //端口号
struct in_addr sin_addr; //IP地址结构体
unisgned char sin_zero[8];
//填充,没有实际意义,只是为跟sockaddr结构在内存中对齐,这样两者才能相互转换
};
3.地址转变API:
int inet_aton(const char* straddr , straddr ,struct in_addr *adddrp);
把字符串形式的“192.168.1.123”转为网络能识别的格式
char* inet_ntoa(struct in_addr inaddr);
把网络格式的IP地址转化为字符串形式
4.监听 API :
listen()函数 : 监听设置函数
#include <sys/types.h>
#include <sys/socket.h>
int listen(int sockfd, int backlog);
功能:
- 设置能处理的最大连接数,listen()并未开始接受连线,只是设置sockect的listen模式,listen函数只用于服务器端,服务器进程不知道要与谁连接,因此,它不会主动地要求与某个进程连接,只是一直监听是否有其他客户进程与之连接,然后**响应该连接请求,**并对它做出处理,一个服务进程可以同时处理多个客户进程的连接。
- 主要就两个功能:将一个未连接的套接字转换为一个被动套接字(监听),规定内核为相应套接字排队的最大连接数。
- 内核为任何一个给定监听套接字维护两个队列:
- 未完成连接队列,每个这样的SYN报文段对应其中一项:已由某个客户端发出并到达服务器,而服务器正在等待完成相应的TCP三次握手过程。这些套接字处于 SYN_REVD状态
- 已完成连接队列,每个已完成 TCP三次握手过程的客户端对应其中一项。这些套接字处于ESTABLISHED状态;
参数说明:
sockfd: sockfd是socket系传调用返回的服务器端socket描述符
backlog: backlog指定在请求队列中允许的最大请求数
5.连接 API :
accept()函数
#include <sys/types.h>
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
功能
accept 函数由TCP服务器调用,用于从已完成连接队列队头返回下一个已完成连接。如果已完成连接队列为空,那么进程被投入睡眠
参数
sockfd: sockfd是socket系统调用返回的服务器端socket 描述符,是服务器监听套接字描述符。一个服务器通常仅仅创建一个监听套接字,它在该服务器的生命周期内一直存在
addr: 用来返回已连接的对端(客户端)的协议地址
addrled: 客户端地址长度
返回值
该函数的返回值是一个新的套接字描述符,返回值是表示已连接的套接字描述符,accept函数接受一个客户端请求后会返回一个新的SOCKFD值,当有不同的客户端同时有不同请求时,会返回不同的SOCKFD的值。这个不同的值和建立SOCKET 时生成的SOCKFD还是不同的。服务器与客户端之间的通信就是在这些不同的SOCKFD上进行的。
而第一个参数是服务器监听套接字描述符。一个服务器通常仅仅创建一个监听套接字,它在该服务器的生命周期内一直存在。内核为每个由服务器进程接受的客户连接创建一个已连接套接字(表示TCP 三次握手已完成),当服务器完成对某个给定客户的服务时,相应的已连接套接字就会被关闭。
6.数据收发 API:
字节流读取函数
在套接字通信中进行字节读取函数: read() , write()。与 I/O中的读取函数略有区别,因为它们输入或输出的字节数比可能比请求的少。
ssize_t write(int fd, const void*buf,sizet nbytes);
ssize_t read(int fd,void *buf,size_t nbyte);
返回值: 读或写的字节个数,出错则返回-1
参数说明:
write 参数 :将buf中的nbytes个字节写入到文件描述符fd中,成功时返回写的字节数。
read 参数:为从fd中读取 nbyte个字节到buf 中,