OSI七层协议功能
物理层
面向物理传输媒体,屏蔽媒体的不同
主要定义物理设备标准,如网线的接口类型、光纤的接口类型、各种传输介质的传输速率等。它的主要作用是传输比特流(就是由1、0转化为电流强弱来进行传输,到达目的地后在转化为1、0,也就是我们常说的模数转换与数模转换)。这一层的数据叫做比特。
链路层
面向一条链路,成帧和无差错传输
主要将从物理层接收的数据进行MAC地址(网卡的地址)的封装与解封装。常把这一层的数据叫做帧。在这一层工作的设备是交换机,数据通过交换机来传输。
网络层
分配地址、面向多条链路、路由和流量控制
要将从下层接收到的数据进行IP地址(例192.168.0.1)的封装与解封装。在这一层工作的设备是路由器,常把这一层的数据叫做数据包。
传输层
面向两台主机通信,处理可靠传输细节,无差错、无失序传输
定义了一些传输数据的协议和端口号(WWW端口80等),如:TCP(传输控制协议),UDP(用户数据报协议)。 主要是将从下层接收的数据进行分段进行传输,到达目的地址后在进行重组。
会话层
面向一次会话,协调双方的交互、同步
主要在你的系统之间发起会话或或者接受会话请求(设备之间需要互相认识可以是IP也可以是MAC或者是主机名)
表示层
面向通信内容的表示,大家认同的描述方式
主要是进行对接收的数据进行解释、加密与解密、压缩与解压缩等(也就是把计算机能够识别的东西转换成人能够能识别的东西(如图片、声音等))
应用层
面向建立在通信基础上的应用,为应用提供通信服务,
主要是一些终端的应用,比如说FTP(各种文件下载),WEB(IE浏览),QQ之类的(你就把它理解成我们在电脑屏幕上可以看到的东西.就 是终端应用)
TCP协议特点
面向连接的可靠传输
端到端、建立/断开连接
正确、顺序传送数据
协议关注问题
IP数据报的丢失、重复、失序、延迟
发送和接收速度的匹配
系统重启动,一方连接信息丢失
网络拥塞
UDP协议特点
无连接
不需要在通信前建立连接
不使用控制报文
传输开销低
面向报文
不将报文分割,也不合并
UDP报文大小直接影响网络利用率
过小造成报头比率过大
过大造成MTU分片
尽力而为、任意交互
一对一、一对多、多对一和多对多
TCP/UDP端口号作为服务器程序标识
服务器启动时,首先在本地主机注册所使用的TCP/UDP端口号
客户通过与服务器指定的TCP端口建立连接(或直接向服务器指定的UDP端口发送信息)来访问特定服务
运行服务器程序的主机收到信息后,将其转交给注册该端口的服务器程序处理
Linux的网络分层结构
BSD Socket是通用接口,支持各种网络工作形式
INET Socket支持包括TCP/IP协议在内的Internet地址族
socket基本概念
Socket接口是应用程序与TCP/IP协议栈的接口
定义一组函数/例程,支持TCP/IP网络应用程序开发
与数据通信相关的系统调用是read()/write()
是一种文件描述符
一个套接字描述一个通信连接的一端
两个通信程序中各自有一个套接字来描述自己的
Socket不是TCP/IP标准的组成部分,但已成为事实上的工业标准
UNIX系列系统提供Socket
Windows系列、Macintosh系列、Solaris等亦提供
形式
(IP, PORT)
网络进程标识
<协议, 本地地址, 本地端口>
网间通信标识
<协议, 本地地址, 本地端口, 远程地址, 远程端口>
端口分类
公认端口:从0到1023
小于256的端口作为保留端口
注册端口:端口号从1024到49151 .
动态和/或私有端口:从49152到65535。理论上,不应为服务分配这些端口。实际上,机器通常从1024起分配动态端口。
基本socket API
socket()
创建一个新的Socket
close()
关闭一个Socket
bind()
将服务器(IP, Port)赋予Socket
listen()
等待到来的客户连接请求(TCP)
accept()
接受客户连接请求并建立连接(TCP)
connect()
向服务器发出连接请求
send()/recv()
发送/接收数据
字节顺序
大端模式(big endian):高字节放到高地址上
小端模式(little endian):高字节放到低地址上
主机字节顺序(HBO,Host Byte Order)
不同的机器HBO不相同,与CPU设计有关
Motorola 68k系列,HBO与NBO相同
Intel x86系列,HBO与NBO相反
网络字节顺序(NBO,Network Byte Order)
使用统一的字节顺序,避免兼容性问题
解决兼容性问题途径
往网络上发送前:转换成网络字节序
从网络接收数据:转换成主机字节序
头文件
#include <netinet/in.h>
函数原型
uint32_t htonl(uint32_t hostlong);
把32位值从主机字节序转换成网络字节序
uint16_t htons(uint16_t hostshort);
把16位值从主机字节序转换成网络字节序
uint32_t ntohl(uint32_t hostlong);
把32位值从网络字节序转换成主机字节序
uint16_t ntohs(uint16_t hostshort);
把16位值从网络字节序转换成主机字节序
说明
h代表host,n代表 network
s代表short,l代表long
socket()函数
功能
创建一个套接字
#include <sys/socket.h>
函数原型
int socket(int domain, int type, int protocol);
参数说明
domain:通信协议族,即地址族
type:套接字类型
protocol:通信协议
常设置为0 ,由内核根据指定的类型和协议族使用默认的协议
返回值
成功时,返回一个大于等于0的文件描述符
失败时,返回一个小于0的值
Linux支持的协议和地址族
地址 | 协议 | 协议描述 |
---|---|---|
AF_UNIX | PF_UNIX | Unix域 |
AF_INET | PF_INET | TCP/IP(V4) |
AF_INET6 | PF_INET6 | TCP/IP(V6) |
AF_AX25 | PF_AX25 | 业余无线电使用的AX.25 |
AF_IPX | PF_IPX | Novell的IPX |
AF_APPLETALK | PF_APPLETALK | AppleTalk DDS |
AF_NETROM | PF_NETROM | 业余无线电使用的 NetRom |
套接字类型
流套接字(SOCK_STREAM)
可靠的、面向连接的通信
使用TCP协议
数据报套接字(SOCK_DGRAM)
无连接服务
使用UDP协议
原始套接字(SOCK_RAW)
允许对底层协议(如IP、ICMP)直接访问
int main(){
……
int sockfd;
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror(“socket”);
exit(1);
}
……
}
函数原型
int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen)
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t *optlen)
功能
控制套接字行为,如修改缓冲区大小、传输方式等
参数说明
level:指定控制套接字的层次
SOL_SOCKET:通用套接字选项
IPPROTO_IP:IP选项
IPPROTO_TCP:TCP选项
optname:指定控制方式(选项名称)
optval:获得/设置套接字选项值
SOL_SOCKET参数选项
SO_BROADCAST 允许发送广播数据 int
SO_DEBUG 允许调试 int
SO_DONTROUTE 不查找路由 int
SO_ERROR 获得套接字错误 int
SO_KEEPALIVE 保持连接 int
SO_LINGER 延迟关闭连接 struct linger
SO_OOBINLINE 带外数据放入正常数据流 int
SO_RCVBUF 接收缓冲区大小 int
SO_SNDBUF 发送缓冲区大小 int
SO_RCVLOWAT 接收缓冲区下限 int
SO_SNDLOWAT 发送缓冲区下限 int
SO_RCVTIMEO 接收超时 struct timeval
SO_SNDTIMEO 发送超时 struct timeval
SO_REUSERADDR 允许重用本地地址和端口 int
SO_TYPE 获得套接字类型 int
SO_BSDCOMPAT 与BSD系统兼容 int
IPPROTO_IP
IP_HDRINCL
在数据包中包含IP首部
IP_OPTINOS
IP首部选项
IP_TOS
服务类型
IP_TTL
生存时间
IPPRO_TCP
TCP_MAXSEG
TCP最大数据段的大小
TCP_NODELAY
不使用Nagle算法
套接字选项示例
更改发送/接收缓冲区大小
接收缓冲区
int nRecvBuf=32*1024; //设置为32Ksetsockopt(s, SOL_SOCKET, SO_RCVBUF, (const void*)&nRecvBuf, sizeof(int));
发送缓冲区
int nSendBuf=32*1024;//设置为32Ksetsockopt(s, SOL_SOCKET, SO_SNDBUF, (const void*)&nSendBuf,sizeof(int));
说明
对于客户,SO_RCVBUF选项必须在connect之前设置
对于服务器,SO_RCVBUF选项必须在listen前设置
bind()函数
功能
将套接字地址与所创建的套接字号联系起来
客户端如果只想使用connect(),则无须使用该函数
#include <sys/socket.h>
函数原型
int bind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen);
参数说明
sockfd:调用socket返回的文件描述符
my_addr:保存地址信息(IP地址和端口)
addrlen:设置为 sizeof(struct sockaddr)
返回值
成功时,返回0
失败时,返回-1
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#define MYPORT 3490
int main(){
int sockfd;
struct sockaddr_in my_addr;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(MYPORT);
my_addr.sin_addr.s_addr = inet_addr("132.241.5.10");
bind(sockfd, (struct sockaddr*)&my_addr, sizeof(struct
sockaddr));
…….
}
sockaddr结构定义
功能
保存socket信息
结构
struct sockaddr {
unsigned short sa_family; /* 地址族,AF_xxx */ char sa_data[14]; /* 协议地址 */
};
说明
sa_family一般为AF_INET(表示TCP/IP)
sa_data包含socket的IP地址和端口号
/include/linux/socket.h
功能
sockaddr的另一种表示形式
结构
struct sockaddr_in { short int sin_family; /* 地址族 */ unsigned short int sin_port; /* 端口号 */ struct in_addr sin_addr; /* IP地址 */ unsigned char sin_zero[8]; /* 填充0,保持与struct sockaddr等长*/
};
说明
sin_zero用于将sockaddr_in结构填充到与struct sockaddr等长,可用bzero( )或memset( )函数将其置为0
当sin_port = 0时,系统随机选择一个未被使用的端口号
当sin_addr = INADDR_ANY时,表示填入本机IP地址
指向sockaddr_in的指针和指向sockaddr的指针可相互转换
connect()函数
功能
建立套接字连接
#include <sys/socket.h>
函数原型
int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen);
参数说明
sockfd:调用socket返回的文件描述符
serv_addr:远程主机IP 地址和端口
addrlen:设置为 sizeof(struct sockaddr)
返回值
成功时,返回0
失败时,返回-1
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#define DEST_IP "132.241.5.10"
#define DEST_PORT 23
int main() {
int sockfd;
struct sockaddr_in dest_addr;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
dest_addr.sin_family = AF_INET;
dest_addr.sin_port = htons(DEST_PORT);
dest_addr.sin_addr.s_addr = inet_addr(DEST_IP);
connect(sockfd, (struct sockaddr*)&dest_addr,
sizeof(struct sockaddr));