数据链路层: 通过各种控制协议,将有差错的物理信道变为无差错的、能
可靠传输数据帧的数据链路。
MAC(物理地址)48位
网络层: 实现数据包的选路和转发。
IP 地址有分 IPV4 (32位)和 IPV6 (128位)两种类别格式,(主机号和网络号构成)
传输层:传输层为应用层提供过了一个进程间通讯的途径 (TCP/UDP/SCTP)
OSI七层协议,TCP/IP五层协议
寄快递,ip /协议 发送的数据大小有限制。
tcp/ip 面向连接的可靠的 流式服务
#include
uint32_t htonl(uint32_t hostlong);// 长整型的主机字节序转网络字节序
uint32_t ntohl(uint32_t netlong); // 长整型的网络字节序转主机字节序
uint16_t htons(uint16_t hostshort); // 短整形的主机字节序转网络字节序
uint16_t ntohs(uint16_t netshort);// 短整型的网络字节序转主机字节
套接字:
通过套接字进行收发数据。(相当于手机)
ip和端口填充到套接字的地址(结构体sockaddr中)
#include
int socket(int domain, int type, int protocol)
//socket()创建套接字,成功返回套接字的文件描述符,失败返回-1
// domain: 设置套接字的协议簇, AF_UNIX AF_INET AF_INET6
//type: 设置套接字的服务类型 SOCK_STREAM SOCK_DGRAM
//protocol: 一般设置为 0,表示使用默认协议
IPV4
地址,但编程中我们需要先把它们转化IPV4
地址和网络字节序整数表示的 IPV4
地址之间的转换:IPV6
地址,要用网络字节序表示int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
//bind()将 sockfd 与一个 socket 地址绑定,成功返回 0,失败返回-1
//sockfd 是网络套接字描述符
//addr 是地址结构 设置ip地址和端口
//addrlen 是 socket 地址的长度
通用套接字地址结构:
sockaddr
专用套接字地址结构:
IPV4:
TCP/IP协议族有sockaddr_in
:设置ip端口
IPV6:
sockaddr_in6
1,存放未完三次握手的链接。
2,存放已经完成三次握手的链接
int listen(int sockfd, int backlog);
//listen()创建一个监听队列以存储待处理的客户连接,成功返回 0,失败返回-1
//sockfd 是被监听的 socket 套接字
//backlog 表示处于完全连接状态的 socket 的上限(完成三次握手的链接的个数)
//linux 上是 5
accept()处理存放在 listen 创建的已完成三次握手的队列中的连接。每处理一个连接,则
accept()返回该连接对应的套接字描述符。
如果队列为空,则阻塞。
一般由客户端程序执行。指定服务器端的ip地址和端口。
进行三次握手,建立链接
:收发数据。向缓冲区写入和读取数据
在写程序之前ping 127.0.0.0
一下,看客户端与服务器端是否联通。
struct in_addr
{
u_int32_t s_addr;
//sin_addr: IPV4 地址结构:s_addr 以网络字节序表示 IPV4 地
};
struct sockaddr_in
{
sa_family_t sin_family;
//sin_family: 地址族 AF_INET
struct in_addr sin_addr;
//sin_port: 端口号,需要用网络字节序表示
};
服务器端:
struct sockaddr_in saddr,caddr;
//专用套接子地址结构,设置主机ip地址和端口
memset(&saddr,0,sizeof(saddr));
//将初始值置为0
saddr.sin_family = AF_INET;
//设置地址组协议
saddr.sin_port = htons(6000);
//端口/ 小端存放转大端存放-----主机字节序列转网络字节序列
// 发送数据的时候,其字节会从(长整型,短整型主机字节转为网络字节),收数据相反。
saddr.sin_addr.s_addr = inet_addr("0.0.0.0");
//设置ip地址
#include
#include
#include
#include
#include
#include
#include
int main()
{
int sockfd = socket(AF_INET,SOCK_STREAM,0);
//套接子协议(AF_INET 表示IPV4,AF_INET6 :IPV6)
//TCP 数据传输为数据流 ,SOCK_STREAM
//默认协议,一般为0
if(sockfd == -1)
{
exit(1);
}
struct sockaddr_in saddr,caddr; //专用套接子地址结构,设置主机ip地址和端口
memset(&saddr,0,sizeof(saddr)); //将初始值置为0
saddr.sin_family = AF_INET; //设置地址组协议
saddr.sin_port = htons(6000); //端口/
saddr.sin_addr.s_addr = inet_addr("0.0.0.0");//设置ip地址
int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));//强转为通用套接子地址结构。
if(res== -1)
{
printf("bind falied");
exit(1);
}
res = listen(sockfd,5); //监听套接子(已经完成三次握手和未完成三次握手)
if(res == -1)
{
close(sockfd);
exit(1);
}
while(1)
{
int len = sizeof(caddr); // caddr:那个客户端成功连接。那么caddr存放的就该ip地址和端口号。
int c = accept(sockfd,(struct sockaddr*)&caddr,&len);//阻塞
// 返回的该连接的连接套接子,处理已经完成三次握手的连接
if(c == -1)
{
continue;
}
printf("accept client ip :%s,port = %d\n",inet_ntoa(caddr.sin_addr),nthons(caddr.sin_port))//将ip地址幻化为点分十进制的整型。nthons网络转主机
char buff[128] ={0};
int n = recv(c,buff,127,0);
printf("recv(%d):%s\n",n,buff);
send(c,"ok",2,0);
close(c);
}
}
saddr
:存放的是服务器端的ip地址和端口
int len = sizeof(caddr);
// caddr
:那个客户端成功连接。那么caddr
存放的就该客户端主机的ip地址和端口号。
客户端
#include
#include
#include
int main()
{
int sockfd = socket(AF_INET,SOCK_STREAM,0);
if(sockfd == -1)
{
exit(1);
}
//客户端可以不用设置端口和ip地址,客户端去连接服务器,不需要知道自己的ip和端口,只需要知道服务器的就行。
struct sockaddr_in saddr;
memset(&saddr,0,sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(6000);
saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
//连接服务器
int res = connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
if(res == -1)
{
printf("connect failed\n");
close(sockfd);
exit(1);
}
printf("input:\n");
char buff[128] = {0};
fgets(buff,128,stdin);
send(sockfd ,buff,strlen(buff)-1,0);
memset(buff,0,128);
recv(sockfd,buff,128,0);
printf("recv:%s\n",buff);
close(sockfd);
exit(0);
}