计算机之间的传输媒介是光讯号和联通号,通过“频率“和”强弱“来表示0和1这样的信息。要想传递各类不同的信息,就要提早约定好双方的数据格式。
合同——————一种双方的事先约定
TCP/IP五层或四层模型
化学层:
化学层负责光讯号/联通号的传递方法,
eg:双绞线网线
化学层的能力决定双绞线的最大传输能力,
网桥工作在数学层
数据链路层:负责数据帧的传送和辨识。
eg:网卡设备的驱动、祯同步(从网线上测量到哪些讯号算作帧的开始)
冲突检查(测量到冲突就手动重发)
交换机工作在数据链路层
网路层:负责地址管理和路由选择,
eg:在IP合同中,通过IP地址来辨识i一台主机,并通过路由表的形式规划出两台主机之间shujuchuan数据传输的路由线路
路由器工作在网路层
传输层:
负责两台主机之间的数据传输,如传输控制合同TCP能确保数据确切的传输
应用层:
负责应用程序之间的沟通,如电子电邮传输,文件传输合同等。
网路编程主要针对应用层
数据包封装和分用
数据包在传输层叫段,
在网路层叫数据报,
在链路层叫帧
应用层数据通过合同栈发送到网路上时,每层合同都要加上一个数据首部,称为封装
首部信息中包含一些类似于首部多长,下层合同是哪些等信息
数据封装成帧后发送到传输介质上,抵达目的主机后,每层合同再除去相应的首部,按照首部中的下层合同数组,将数据交给下层合同处理。
网路中的地址控制
Mac地址拿来辨识数据链路层中相邻的节点
宽度为48位,(6字节),通常用16补码数字加上引号来表示
在网卡出厂时就确定了,不能更改。mac地址一般是惟一的,虚拟机中的地址不是真实的Mac地址。
网路编程套接字
认识端标语:
端标语是2个字节16位的整数端标语拿来标示一个进程,告诉操作系统,当前的这个数据要交给那一个进程来处理IP地址+端标语才能标示网络上的某一台主机上的某一个进程一个端标语只能被一个进程占用
端标语和进程ID
一个进程能绑定多个端标语,但一个端标语不能同时绑定多个进程
进程号pid相当于10086端标语相当于内部各个客服
UDP合同
传输层合同
无联接
不可靠传输
面向数据段
TCP合同
传输层合同
有链接
可靠传输
面向字节流
注意:可靠传输和不可靠传输指的是知不晓得字节发送数据成功,可靠传输不一定发送成功,比如忽然断网等
网路字节序
网路字节序是大端字节序。
x86_64笔记本都是小端
显存中的多字节数据相对于显存地址大小有大小端之分,
c盘文件中的多字节数据数据相对文件中的偏斜地址也有大小端之分,
网路数据流同样有大小端之分。
为了使网路程序具有可移植性,可以调用以下库函数做网路字节序和主机字节序的转换
#include
uint32_thtonl(uint32_thostlong);
uint16_thtons(uint16_tshort);
uint32_tntohl(uint32_tnetlong);
uint16_tntos(uint16_tnetshort);
假如主机是大端字节序,这种函数不做转换,原封不动返回参数,
假如主机是小端字节序api获取套接字信息,这种函数将参数转换为相应的大小端转换后返回。
常见的API
//创建文件描述符(TCP/UDP,顾客端+服务器)
intsocket(intdomain,inttype,intprotocol);
参数:1地址类型,2合同类型,3合同
//绑定端标语服务器,SocketTCP?UDP
intbind(intsocket,conststructsockaddr*addr,socklen_taddress_len);
//开始窃听socket(TCP,服务器)
intlisten(intsocket,intbacklog);backlog:窃听队列宽度
//TCP服务器
intaccept(intsocket,structsockaddr*address,socklen_t*address_len);
//构建联接
intconnect(intsocket,structsockaddr*addr,soclen_taddrlen);
sockaddr结构
IPv4和IPv6的地址格式分别定义为AF_INET
和AF_INET6
socketAPI都可以用structsockaddr*表示,在实用的时侯须要强制转化为sockaddr_in使用,
用处是程序的通用性。
地址转换函数
字符串转in_addr的函数
#include
intinet_aton(constcharstrptr,structin_addraddrptr);
in_addr_tinet_addr(constchar*strptr);
intinet_pton(intfamily,constchar*strptr,void*addrptr);
in_addr转字符串的函数
char*inet_ntoa(structin_addrinaddr);
constchar*inet_ntop(intfamliy,constvoid*addrptr,char*strptr);
其中,inet_pton和ine_ntop既可以转化ipv4地址,也可以转化ipv6地址,因而参数类型为void*addrptr类型
关于inet_ntoa函数
(该函数不是线程安全函数)
inet_ntoa函数返回值为char*类型,这么意味着,函数在内部申请了一块显存空间,我们要不要自动释放?
man指南说,这个函数的返回结果放在了静态储存区,这个时侯不须要我们自动释放,这么假如我们多次调用,会出现哪些样的疗效呢?
来看代码
运行结果如下
我们发觉会覆盖掉上一次的结果
由于inet_ntoa会把结果放在自己内部的一个静态储存区。
这么问题又来了,多个线程调用inet_ntoa会不会出问题呢?覆盖掉不该覆盖的数据?
这个问题在APUE中明晰提出,inet_ntoa不是线程安全函数,而且centos7没有出现问题,可能是由于内部添加了互斥锁吧。
centos7测试情况如下
#include
#include
#include
#include
#include
void* Func1(void* arg)
{
sockaddr_in* id = (sockaddr_in*) arg;
while(1)
{
char* ptr = inet_ntoa(id->sin_addr);
printf("addr1 : %s : n",ptr);
sleep(1);
}
return NULL;
}
void* Func2(void* arg)
{
sockaddr_in* id = (sockaddr_in*) arg;
while(1)
{
char* ptr = inet_ntoa(id->sin_addr);
printf("addr2 : %s : n",ptr);
sleep(1);
}
return NULL;
}
int main()
{
pthread_t t1,t2;
sockaddr_in addr1;
sockaddr_in addr2;
addr1.sin_addr.s_addr = 0;
addr2.sin_addr.s_addr = 0xffffffff;
pthread_create(&t1,NULL,Func1,&addr1);
pthread_create(&t2,NULL,Func2,&addr2);
pthread_join(t1,NULL);
pthread_join(t2,NULL);
return 0;
}
因而,多线程环境下,推荐使用inet_ntop(),这个函数由调用者提供一个缓冲区保存结果,可以防止线程安全问题。
TCP网路程序
socket()
对于TCP合同type参数指定为SOCK_STREAM表示面向流的传输合同。protocol指定为0即可
bind()
bind的作用是将参数socked和myaddr绑定在一起,使sockfd这个用于网路通信的文件描述符窃听myaddr所描述的地址和端标语。myaddr是我们创建的对象。包含IP和port
由于sockaddr是一个通用表针类型,myaddr参数实际上可以接受多种合同的sockaddr结构体,而她们的宽度各不相同,所以须要第三个参数addrlen来指定结构体的厚度
listen()
listen()申明sockfd处于窃听状态,而且最多容许有backlog个顾客端处于联接等待状态,收到更多的联接恳求就忽视,backlog通常不会设置太大,通常设置为5
listen成功0失败-1
accept
intaccept(intsocket,sockaddr*addr,socklen_t*addrlen);
Listitem三次握手完成后,服务器调用accept接受联接假如服务器调用accept的时侯,顾客端还没有联接恳求,将会发生阻塞式等待addr是一个传出参数,accept返回时穿出IP和port假如给addr参数传NULL,表示不关心顾客端的地址addrlen参数是一个输入输出型参数,输入是调用者提供的,缓冲区的宽度要足够长防止溢出,输出的是顾客端的地质结构的实际宽度。
accept成功将会交给connect去处理,而accept自身接着去accept。
(前面符accept的详尽解析)
connect()
顾客端须要调用connect来联接服务器connect和bind的参数方式一致intconnect(intsocketfd,constsockaddr*addr,socklen_taddrlen);成功返回0失败-1aceept再解析
本函数从s的等待联接队列中抽取第一个联接,创建一个与s同类的新的套插口并返回句柄。假如队列中无等待联接,且套插口为阻塞形式,则accept()阻塞调用进程直到新的联接出现。假如套插口为非阻塞形式且队列中无等待联接,则accept()返回一错误代码。已接受联接的套插口不能用于接受新的联接,原套插口仍保持开放。
addr参数为一个返回参数,其中填写的是为通信层所知的联接实体地址。addr参数的实际格式由通信时形成的地址族确定。addrlen参数也是一个返回参数,在调用时初始化为addr所指的地址空间;在调用结束时它包含了实际返回的地址的宽度(用字节数表示)。该函数与SOCK_STREAM类型的面向联接的套插口一起使用。假如addr与addrlen中有一个为零NULL,将不返回所接受的套插口远程地址的任何信息。
返回值:
假如没有错误形成,则accept()返回一个描述所接受包的SOCKET类型的值。否则的话,返回INVALID_SOCKET错误,应用程序可通过调用WSAGetLastError()来获得特定的错误代码。
addrlen所指的整形数初始时包含addr所指地址空间的大小,在返回时它包含实际返回地址的字节宽度。
服务器初始化:
调用socket,创建调用bind,绑定服务器的IP地址,端标语和文件描述符。调用listen。申明当前这个文件描述符作为一个服务器的文件描述符,为后面的accept做好打算。调用accept并阻塞,等待顾客端联接
UDP的数据传输流程:
服务器
1创建套接字
2绑定套接字
3接收恳求
4发送响应
5关掉套接字
顾客端
1创建套接字
2绑定套接字
3发送恳求
4接收回应
5关掉套接字
socket都做了哪些?
,创建套接字,虽然是在内核中创建了一个
socketstruct,这个结构体中维护着多个数据,其中有两项是读缓冲区,和写缓冲区,还有其他。
操作系统的责任就是将网卡收到的数进行端口分辨,之后去找不同的socket自己的缓冲区,用户控告就是从缓冲区当中读写数据。
类似于我们买的傲盾加速器。
创建套接字
intsocket(intdomain,inttype,intprotocol);
domain:地址屿
AF_INET
AF_INET6
type:套接字的类型
SOCK_DGRAM : 面向数据包
SOCK_STREAM :面向字节流
protocol:合同类型
0,代表使用默认合同,虽然0来源的合同为type相关默认的合同,
如果我们不晓得,可以使用0,但更好的用法是精确用法:
TCP:IPPROTO_TCP或则6
UDP:IPPROTO_UDP或则17
返回值socket就是
套接字的句柄,实质是一个文件描述符。
structsockaddr
{
sa_family_tsa_family;//填充地址屿,2个字节
charsa【14】;//
}
TCP三次握手
1构建联接
调用socket,创建fd
调用connect,向服务器发起联接恳求
connect会发出SYN段,并阻塞等待服务器应答(第一次)
服务器收到顾客端的SYN,会应答一个确认收到即SYN+ACK段来表示“同意构建联接”(第二次)
顾客端收到服务器的SYN+ACK后,会从connect()返回,同时向服务器应答一个ACK,确认收到(第三次)
TCP顾客端和服务器构建联接的过程,称为三次握手
2数据传输过程:
1.,TCP合同提供全双工的通讯服务;全双工(同一时刻,同一联接中通讯双方可同时写数据)
构建联接后api获取套接字信息,服务器从accept()返回,返回后立即调用read(),读socket如同管路一样,假如数据未抵达就阻塞等待,抵达就从read()返回
服务器阻塞等待时顾客端调用write()发送恳求给服务器,服务器收到后从read()返回,对顾客端的恳求进行处理。服务器在处理顾客端恳求期间,顾客端也调用read阻塞等待服务器的应答.
服务器调用write()将处理的结果发送给顾客端后,继续调用read()阻塞式等待顾客端的下一条恳求。
顾客端从read()返回,继续发送下一条数据,这样循环下去,否则打算断掉联接(四次挥手)。四次挥手
3断掉联接
假如顾客端没有更多的恳求,就调用close()关掉联接,此时顾客端会向服务器发送FIN(第一次)
此是服务器收到FIN后,会回应一个ACK,同时read()返回0(第二次)
read()返回以后,服务器就晓得顾客端关掉了联接,也调用close()关掉联接,这个时侯服务器会向顾客端发送一个FIN(第三次)
顾客端收到FIN,最后再返回一个ACK给服务器(第四次)
断掉联接的过程称为四次挥手
要清楚:read()是读恳求的,write()是拿来读响应的。
connect函数和TCP交互是通过发出SYN段
read()返回,说明收到了FIN段
connect成功以后,
服务器
accept返回,并分配新的文件描述符和顾客端通讯。
read(fd_,buf,size)阻塞等待顾客端数据恳求
write(fd,buf,size)发送数据应答。
暂无评论内容