网络

网络是由若干结点和链接这些节点的链路组成
网络通信基础

结点

可以是计算机、集线器、交换机或路由器

互联网

网络和网络之间通过路由器连起来,就构成了一个覆盖范围更大的网络,即互联网

网络编程

通过使用套接字来达到进程间通信目的编程是网络编程

网络设备

计算机(无论其为个人电脑或服务器)、集线器、交换机、网桥、路由器、网关、网络接口卡(NIC)、无线接入点(WAP)、打印机和调制解调器、光纤收发器、光缆等。

传输介质

光纤>同轴电缆>双绞线

套接字

TCP用主机的IP地址+主机上的端口号作为TCP连接的端点,这种端点就叫做套接字(socket)或插口。
网络通信基础

OSI模型

网络通信基础

  • 物理层: 将数据转换为可通过物理介质传送的电子信号 相当于邮局中的搬运工人
  • 数据链路层: 决定访问网络介质的方式,在此层将数据分帧,并处理流控制。本层指定拓扑结构并提供硬件寻 址。相当于邮局中的装拆箱工人
  • 网络层: 使用权数据路由经过大型网络 相当于邮局中的排序工人

- 传输层: 提供终端到终端的可靠连接 相当于公司中跑邮局的送信职员

  • 会话层: 允许用户使用简单易记的名称建立连接 相当于公司中收寄信、写信封与拆信封的秘书
  • 表示层: 协商数据交换格式 相当公司中简报老板、替老板写信的助理
  • 应用层: 用户的应用程序和网络之间的接口老板

MAC

MAC(Media Access Control或者Medium Access Control)地址,意译为媒体访问控制,或称为 物理地址、硬件地址,用来定义网络设备的位置。在OSI模型中,第三层网络层负责 IP地址,第二层数据链路层则负责 MAC地址。因此一个主机会有一个MAC地址,而每个网络位置会有一个专属于它的IP地址。

为什么要实现分层

网络通信基础

MAC和IP的区别

IP地址即指使用TCP/IP协议指定给主机的32位地址。

IP地址由用点分隔开的4个8八位组构成,如192.168.0.1就是一个IP地址,这种写法叫点分十进制格式。IP地址由网络地址主机地址两部分组成,分配给这两部分的位数随地址类(A类、B类、C类等)的不同而不同。网络地址用于路由选择,而主机地址用于在网络或子网内部寻找一个单独的主机。一个IP地址使得将来自源地址的数据通过路由而传送到目的地址变为可能。

MAC地址的长度为48位(6个字节),通常表示为12个16进制数,每2个16进制数之间用冒号隔开,如:08:00:20:0A:8C:6D就是一个MAC地址,其中前6位16进制数08:00:20代表网络硬件制造商的编号,它由IEEE(Istitute of Electrical and Electronics Engineers,电气与电子工程师协会)分配,而后3位16进制数0A:8C:6D代表该制造商所制造的某个网络产品(如网卡)的系列号。每个网络制造商必须确保它所制造的每个以太网设备都具有相同的前三个字节以及不同的后三个字节。这样就可保证世界上每个以太网设备都具有唯一的MAC地址。
###为什么同时需要MAC和IP??
(1)IP地址的分配是根据网络的拓朴结构,而不是根据谁制造了网络设置。若将高效的路由选择方案建立在设备制造商的基础上而不是网络所处的拓朴位置基础上,这种方案是不可行的。
(2)当存在一个附加层的地址寻址时,设备更易于移动和维修。例如,如果一个以太网卡坏了,可以被更换,而无须取得一个新的IP地址。如果一个IP主机从一个网络移到另一个网络,可以给它一个新的IP地址,而无须换一个新的网卡。
(3)无论是局域网,还是广域网中的计算机之间的通信,最终都表现为将数据包从某种形式的链路上的初始节点出发,从一个节点传递到另一个节点,最终传送到目的节点。数据包在这些节点之间的移动都是由ARP(Address Resolution Protocol:地址解析协议)负责将IP地址映射到MAC地址上来完成的。

网络字节序和主机字节序

  • 网络字节序:大端存储方式 (高地址存放低字节的数据)
  • 主机字节序:(intel)小端存储方式 (高地址存放高字节的数据)
  • 主机字节序: (IBM)大端存储方式
    举例:
#include<stdio.h>
int main()
{
  int  a = 0x12345678;
  char* p = (char*)&a;
  printf("%x\n",*p);//小端模式下  *p==78
  return 0;
}

主机字节序转网络字节序

#include<netinet/in.h>
//将长整形主机字节序转换为网络字节序
unsigned long int htonl(unsigned long int hostlong);
//将短整形主机字节序转换为网络字节序
unsigned short int htons(unsigned short int hostshort);

//将网络字节序转换为长整型主机字节序
unsigned long int ntohl(unsigned long int netlong);
//将网络字节序转换为短整型主机字节
unsigned short int ntohs(unsigned short int netshort);

sockaddr_in

作用用做bind、connect、recvfrom、sendto等函数的参数,指明地址信息
sockaddr_in在头文件#include<netinet/in.h>或#include <arpa/inet.h>中定义
网络通信基础

s_addr按照网络字节顺序存储IP地址

相关接口函数

int socket(int domain,int type,int protocol);

函数说明:socket()用来建立一个新的socket,也就是向系统注册,通知系统建立一通信端口。参数domain 指定使用何种的地址类型

int accept(int s,struct sockaddr * addr,int * addrlen);

函数说明:accept()用来接受参数s的socket连线。参数s的socket必需先经bind()、listen()函数处理过,当有连线进来时accept()会返回一个新的socket处理代码,往后的数据传送与读取就是经由新的socket处理,而原来参数s的socket能继续使用accept()来接受新的连线要求。连线成功时,参数addr所指的结构会被系统填入远程主机的地址数据,参数addrlen为scokaddr的结构长度。

int bind(int sockfd,struct sockaddr * my_addr,int addrlen);

函数说明: bind()用来设置给参数sockfd的socket一个名称

int connect (int sockfd,struct sockaddr * serv_addr,int addrlen);

函数说明:connect()用来将参数sockfd 的socket 连至参数serv_addr 指定的网络地址。

int listen(int s,int backlog);

函数说明:listen()用来等待参数s 的socket连线。参数backlog指定同时能处理的最大连接要求,如果连接数目达此上限则client端将收到ECONNREFUSED的错误。Listen()并未开始接收连线,只是设置socket为listen模式,真正接收client端连线的是accept()。通常listen()会在socket(),bind()之后调用,接着才调用accept()。

int recv(int s,void *buf,int len,unsigned int flags);

函数说明:recv()用来接收远端主机(客户机)经指定的socket传来的数据,并把数据存到由参数buf 指向的内存空间,参数len为可接收数据的最大长度。

int send(int s,const void * msg,int len,unsigned int falgs);

函数说明:send()用来将数据由指定的socket 传给对方主机。参数s为已建立好连接的socket。参数msg指向欲连线的数据内容,参数len则为数据长度。参数flags一般设0

网络通信代码示例

客户程序:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<assert.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<string.h>
//tcp
int main()
{
	int sockfd=socket(AF_INET,SOCK_STREAM,0);
	assert(sockfd!=-1);
   
    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));
	assert(res!=-1);


  printf("input:\n");
  char buff[128]={0};
  fgets(buff,128,stdin);

  send(sockfd,buff,strlen(buff),0);
  memset(buff,0,128);
  recv(sockfd,buff,127,0);//接受服务器经自己sockfd传过来的数据
  printf("buff=%s\n",buff);

  close(sockfd);

  }

服务器程序

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<assert.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<string.h>
//tcp
int main()
{
	int sockfd=socket(AF_INET,SOCK_STREAM,0);
	assert(sockfd!=-1);

	
	struct sockaddr_in saddr,caddr;
	memset(&saddr,0,sizeof(saddr));//初始化结构体
	saddr.sin_family=AF_INET;// 设置地址家族
	saddr.sin_port=htons(6000);//指定端口 htons():主机转网络
    saddr.sin_addr.s_addr=inet_addr("127.0.0.1");//指定ip

    int res=bind(sockfd,(struct sockaddr *)&saddr,sizeof(saddr));//绑定ip+端口
	assert(res!=-1);
  
	listen(sockfd,5);//监听
    while(1)
    {
		int len=sizeof(caddr);
		int c=accept(sockfd,(struct sockaddr*)&caddr,&len);//接受客户请求
		if(c<0)
		{
			continue;
		}
		printf("accep c=%d,ip=%s\n",c,inet_ntoa(caddr.sin_addr));//打印客户ip
		char buff[128]={0};
		int n=recv(c,buff,127,0);//接受客服经客户sockfd传过来的数据
		printf("buff(%d)=%s\n",n,buff);

		send(c,"ok",2,0);//回复客户


		close(c);//关闭
    }



}

相关文章:

  • 2021-10-27
  • 2022-03-02
  • 2021-11-02
  • 2021-11-23
  • 2021-09-06
  • 2021-04-22
  • 2021-11-06
  • 2021-11-06
猜你喜欢
  • 2021-05-03
  • 2021-07-20
相关资源
相似解决方案