网络的基本概念
- 1.协议
- 1.1 协议的基本概念
- 1.2 常见的协议
- 2.分层模型
- 2.1网络七层OSI 7层模型:物数网传会表应(口诀)
- 2.2TCP/IP模型
- 2.3数据通信的过程
- 2.4网络的设计模式
- 2.5以太网帧的格式
- 3.SOCKET编程
- 3.1网络字节序
- 3.2 相关结构体和函数
- 3.3 代码实现
1.协议
1.1 协议的基本概念
协议:双方共同指定的一组规则,在网络通信中指通信双方传递数据和通信双方传递数据和解释数据的一组规则。
1.2 常见的协议
TCP/IP协议:传输控制协议/互联网协议是互联网通信的基础协议。它将数据分割成小的数据包进行传输,并通过IP地址定位目标设备。
HTTP协议:超文本传输协议是用于在网络中传输超文本的应用层协议。它用于在Web浏览器和Web服务器之间传输HTML页面。
FTP协议:文件传输协议是用于在网络中传输文件的协议。它允许用户在客户端和服务器之间进行文件的上传和下载。
SMTP协议:简单邮件传输协议是用于在网络中传输电子邮件的协议。它定义了如何发送和接收电子邮件。
DHCP协议:动态主机配置协议是用于在一个网络中自动分配IP地址的协议。它允许设备动态获取IP地址、子网掩码、网关等网络配置信息。
DNS协议:域名系统是将域名解析为IP地址的协议。它通过将用户输入的域名转换为IP地址,使得计算机能够找到目标服务器。
Telnet协议:用于在网络上远程登录到其他计算机进行操作和管理。
SSH协议:和Telnet相似,但是提供了更加安全的远程登录方式。
.SSL/TLS协议:用于在网络上进行安全的通信,提供数据加密和认证功能,常用于安全的网页浏览、电子商务等。
这些协议在网络通信、电子邮件、安全通信和文件传输等方面起到了重要的作用。
2.分层模型
2.1网络七层OSI 7层模型:物数网传会表应(口诀)
应用层:为客户提供各种应用服务,email服务,ftp服务,ssh服务。
表示层:编解码,翻译工作。
会话层:通过传输层建立数据传输的通道,建立,维护,终止会话。
传输层:传输数据,TCP,UDP协议,端对端传输。
网络层:定义了网络,两台机器之间的传输路径选择点对点传输。
数据链路层(网络接口层):数据校验,定义了网络传输的基本单位–帧。
物理层:双绞线,光纤(传输介质),将模拟信号信号转化为数字信号。
2.2TCP/IP模型
2.3数据通信的过程
在数据发送方是一个层层打包的过程,数据接收方是一个层层解包的过程。
2.4网络的设计模式
网络的设计模式,有B/S模式和C/S模式。
C/S模式:
客户端----服务器结构。C/S结构在技术上很成熟,它的主要特点是交互性强、具有安全的存取模式、网络通信量低、响应速度快、利于处理大量数据。因为客户端要负责绝大多数的业务逻辑和UI展示,又称为胖客户端。它充分利用两端硬件,将任务分配到Client和Server两端,降低了系统的通讯开销。C/S结构的软件需要针对不同的操作系统系统开发不同版本的软件,加之产品的更新换代十分快,已经很难适应百台电脑以上局域网用户同时使用
硬件环境要求:C/S 用户固定,一般只应用于局域网中,要求拥有相同的操作系统,如果对于不同操作系统还要相应开发不同的版本,并且对于计算机电脑配置要求也较高。
B/S模式:
浏览器----服务器结构,是目前应用系统的发展方向。BS是伴随着Internet技术的兴起,对C/S架构的改进,为了区别于传统的C/S模式,特意称为B/S模式。在这种结构下,通过W3浏览器来进入工作界面,极少部分事务逻辑在前端(Browser)实现,主要事务逻辑在服务器端(Server)实现,形成三层(3-tier)结构。这样使得客户端电脑负荷大大简化(因此被称为瘦客户端),减轻了系统维护、升级的支出成本,降低了用户的总体成本(TCO)。
硬件环境要求:要求有操作系统和浏览器就行,与操作系统平台无关(可以实现跨平台),对客户端的计算机电脑配置要求较低。
2.5以太网帧的格式
这里的目的地址和源地址都是指MAC地址,MAC地址为6个字节,IP地址为4个字节,不同类型对应了不同的值,CRC是进行校验的。
下面我们我们来以ARP请求包,假如我们有狠多的机器,我们想给其中一个机器建立对话,我们不知道它的MAC地址只知道IP地址,那么我们如何建立会话呢?
现在我们来看看以太网帧,以ARP为例。
这里是ARP请求包,不知道的mac地址我们填ff:ff:ff:ff:ff:ff。
如果这个IP地址和自己一样,给A机器发一个ARP应答包。
3.SOCKET编程
3.1网络字节序
socket编程之前,我们要知道大端字节序和小端字节序是什么意思
1.大端字节序(Big-Endian):在这种模式下,数据的高位字节保存在内存的低地址中,而数据的低位字节保存在内存的高地址中。这种排列方式与数据用字节表示时的书写顺序一致,符合人类的阅读习惯。大端字节序在网络传输和文件储存中较为常见。
2.小端字节序(Little-Endian):在这种模式下,数据的低位字节保存在内存的低地址中,而数据的高位字节保存在内存的高地址中。小端字节序与人类的阅读习惯相反,但更符合计算机读取内存的方式,因为CPU读取内存中的数据时,是从低地址向高地址方向进行读取的。
如何知道我们的程序是大端字节序还是小端字节序呢?可以通过下面这个代码
#include <stdio.h>
union{
char byte[4];
int num;
}test;
int main()
{
test.num=0x12345678;
if(test.byte[0]==0x78)
printf("小端\n");
else if(test.byte[0]==0x12)
printf("大端\n");
else
printf("error");
printf("[0]:%p:0x%X\n"
"[1]:%p:0x%X\n"
"[2]:%p:0x%X\n"
"[3]:%p:0x%X\n",
&test.byte[0], test.byte[0],
&test.byte[1], test.byte[1],
&test.byte[2], test.byte[2],
&test.byte[3], test.byte[3]);
return 0;
}
我们在网络传输的时候用的是网络字节序,也就是大端字节序,进行网络通信时要把地址转化为大端字节序,下面是相关函数。
把点分十进制转化为大端字节序IP
把大端字节序IP转化为点分十进制IP
3.2 相关结构体和函数
常用的结构体sockaddr,我们一般用第二个结构体,方便我们赋值
这是我们用的socket函数
下面是它的参数说明
成功以后会返回文件描述符
然后就是bind函数,用来给客户端和服务器端建立联系。
下面把客户端由注动变为监听状态(客户端主动连接服务器)
得到一个连接,进行客户端和服务端之间的通信
这里我们就用知道,accept是一个阻塞函数,它要从已连接队列中拿一个可用的连接过来。调用accept之前连接可能就已经建立了。
现在就可以连接服务器了,用connect函数
3.3 代码实现
服务端的开发
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <ctype.h>
int main()
{
//创建socket
//int socket(int domain, int type, int protocol);
int lfd = socket(AF_INET, SOCK_STREAM, 0);
if(lfd<0)
{
perror("socket error");
return -1;
}
//int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
//绑定
struct sockaddr_in serv;
bzero(&serv, sizeof(serv));
serv.sin_family = AF_INET;
serv.sin_port = htons(8888);
serv.sin_addr.s_addr = htonl(INADDR_ANY); //表示使用本地任意可用IP
int ret = bind(lfd, (struct sockaddr *)&serv, sizeof(serv));
if(ret<0)
{
perror("bind error");
return -1;
}
//监听
//int listen(int sockfd, int backlog);
listen(lfd, 128);
//int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
struct sockaddr_in client;
socklen_t len = sizeof(client);
int cfd = accept(lfd, (struct sockaddr *)&client, &len); //len是一个输入输出参数
//const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
//获取client端的IP和端口
char sIP[16];
memset(sIP, 0x00, sizeof(sIP));
printf("client-->IP:[%s],PORT:[%d]\n", inet_ntop(AF_INET, &client.sin_addr.s_addr, sIP, sizeof(sIP)), ntohs(client.sin_port));
printf("lfd==[%d], cfd==[%d]\n", lfd, cfd);
int i = 0;
int n = 0;
char buf[1024];
while(1)
{
//读数据
memset(buf, 0x00, sizeof(buf));
n = read(cfd, buf, sizeof(buf));
if(n<=0)
{
printf("read error or client close, n==[%d]\n", n);
break;
}
printf("n==[%d], buf==[%s]\n", n, buf);
for(i=0; i<n; i++)
{
buf[i] = toupper(buf[i]);
}
//发送数据
write(cfd, buf, n);
}
//关闭监听文件描述符和通信文件描述符
close(lfd);
close(cfd);
return 0;
}
客户端的开发
//客户端代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
int main()
{
//创建socket---用于和服务端进行通信
int cfd = socket(AF_INET, SOCK_STREAM, 0);
if(cfd<0)
{
perror("socket error");
return -1;
}
//连接服务端
//int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
struct sockaddr_in serv;
serv.sin_family = AF_INET;
serv.sin_port = htons(8888);
inet_pton(AF_INET, "127.0.0.1", &serv.sin_addr.s_addr);
printf("[%x]\n", serv.sin_addr.s_addr);
int ret = connect(cfd, (struct sockaddr *)&serv, sizeof(serv));
if(ret<0)
{
perror("connect error");
return -1;
}
int n = 0;
char buf[256];
while(1)
{
//读标准输入数据
memset(buf, 0x00, sizeof(buf));
n = read(STDIN_FILENO, buf, sizeof(buf));
//发送数据
write(cfd, buf, n);
//读服务端发来的数据
memset(buf, 0x00, sizeof(buf));
n = read(cfd, buf, sizeof(buf));
if(n<=0)
{
printf("read error or server closed, n==[%d]\n", n);
break;
}
printf("n==[%d], buf==[%s]\n", n, buf);
}
//关闭套接字cfd
close(cfd);
return 0;
}
最后留下一个过程图