OSI七层网络模型
建立七层网络模型的目的是为了让不同的制造商在网络上通信,比如我A主机是苹果公司生产,操作系统是苹果,B主机是IBM公司生产,操作系统是windows,在AB主机软硬件都不同的情况下通过七层网络模型就可以互相通信了。
下面阐述一下各层的作用:
- 1 应用层 就是应用软件,定义一些网络通信和数据传输的接口。
2表示层 掩盖系统中数据格式的不同性,制定独立结构的数据传输格式,差不多跟格式转换工程类似。
3会话层 管理用户会话和对话,报告上一层得错误。
4 传输层 管理网络中端到端的信息传送,通过错误纠正和流控制机制提高可靠有序的数据包传送,典型的有TCP/UDP协议。
5网络层 就是网络中如何传送数据,传到那里去? 比如IP协议,ARP等等。
6数据链路层 封装数据包
7物理层 就是用什么方式通信啊,光纤?封装数据包为数据帧。
这7层都是层层相扣的,假如A主机想发信息给B主机,A主机的信息首先发生到A主机的应用层,然后表示层…最后通过物理层(比如网线)到B主机的数据链路层,到应用层,最后到终端,B主机用户就能看见A主机所发的信息。A主机在发送信息的过程每层都封装了B主机的信息。数据在各层传送,每层都封装了上层的头和尾,协议头保护层与层直接的通信机制,比如传输层TCP就封装了IP协议。
- 关于传输层TCP协议
TCP(Transmission Control Protocol)功能:对建立网络上用户进程之间的对话负责,它确保进程之间的可靠通信,所提供的功能如下:
- 监听输入对话建立请求
- 请求另一网络站点对话
- 可靠的发送和接收数据
- 适度的关闭对话
UDP(User Datagram Protocol)提供不可靠的非连接型传输层服务,它允许在源和目的地之间传送数据,而不必在传送数据之前建立对话。它主要用于那些非连接型的应用程序,如:视频点播。
IP协议 IP的主要目的是为数据输入/输出网络提供基本算法,为高层协议提供无连接的传送服务。这意味着在IP将数据递交给接收站点以前不在传输站点和接收站点之间建立对话。它只是封装和传递数据,但不向发送者或接收者报告包的状态,不处理所遇到的故障。IP包由IP协议头与协议数据两部分构成。协议头主要存放一些源和目的的IP等信息。
TCP和UDP的区别:TCP提高的是可靠的交换,目的机接受到信息将发送确认信息给源机。UDP提高无连接,不可靠的传输服务,目的机接收到信息不会发生确认信息给源机。即使发送错信息也不会向发送方报错,另外UDP传输比TCP传输更快。
-
Linux中的网络编程通过Socket(套接字)接口实现,Socket是一种文件描述符。
地址结构:
struct sockaddr
{
u_short sa_family;
char sa_data[14];
}
sa_family 地址族,采用“AF_xxx”的形式,如:AF_INET。
Sa_data:14字节的特定协议地址。
编程中一般并不直接针对sockaddr数据结构操作,而是使用与sockaddr等价的sockaddr_in数据结构
struct sockaddr_in
{
short int sin_family; /* Internet地址族*/
unsigned short int sin_port; /* 端口号*/
struct in_addr sin_addr; /* IP地址*/
unsigned char sin_zero[8]; /* 填0 */
}
struct in_addr
{
unsigned long s_addr;
}
S_addr: 32位的地址。
在网络上标识一台机器可以用IP,也可以使用主机名。
struct hostent *gethostbyname(const char *hostname)
struct hostent
{
char *h_name; /* 主机的正式名称*/
char *h_aliases; /* 主机的别名*/
int h_addrtype; /* 主机的地址类型AF_INET*/
int h_length; /* 主机的地址长度*/
char **h_addr_list; /* 主机的IP地址列表*/
}
#define h_addr h_addr_list[0] /* 主机的第一个IP地址*/
基于TCP网络程序设计的算法如下:
server机
利用socket函数创建一个套接字
利用bind函数绑定server地址
利用listen函数设置最大可服务的服务器数目。
利用accept函数接受client的数据。
接受数据/发送数据
client机
利用socket函数创建一个套接字
利用bind函数绑定server地址
利用connect函数与server机连接
发送数据/接受数据。
下面是一个基于tcp协议的程序
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#define NUMBER_PORT 3333
int main(void)
{
int socket_fd,new_fd;
char buf[1024];
int sin_size,nbytes,i;
struct sockaddr_in server_addr,client_addr;
socket_fd=socket(AF_INET,SOCK_STREAM,0);
bzero(&server_addr,sizeof(struct sockaddr_in));
server_addr.sin_family=AF_INET;
server_addr.sin_port=htons(NUMBER_PORT);
server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
bind(socket_fd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr));
listen(socket_fd,5)
while(1)
{
sin_size=sizeof(struct sockaddr_in);
new_fd=accept(socket_fd,(struct sockaddr *)(&client_addr),&sin_size);
fprintf(stderr,"Server get connection from %s\n",inet_ntoa(client_addr.sin_addr));
nbytes=read(new_fd,buf,1024);
printf("receive %s \n",buf);
for(i=0;i<nbytes;i++)
buf[i]=toupper(buf[i]);
write(new_fd,buf,1024);
close(new_fd);
}
close(socket_fd);
return 0;
}
/*client*/
include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#define NUMBER_PORT 3333
int main(int argc,char *argv[])
{
struct sockaddr_in server_addr;
int socket_fd;
char buf[1024];
socket_fd=socket(AF_INET,SOCK_STREAM,0);
bzero(&server_addr,sizeof(struct sockaddr_in));
host=gethostname(argv[1]);
server_addr.sin_family=AF_INET;
server_addr.sin_port=htons(NUMBER_PORT);
server_addr.sin_addr=*(struct in_addr *)host->h_addr;
connect(socket_fd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr));
printf("please inpuy char : \n");
fgets(buf,1024,stdin);
write(socket_fd,buf,strlen(buf));
read(socket_fd,buf,1024);
printf("recevie %s from server \n",buf);
close(socket_fd);
exit(0);
}
程序运行结果;
基于UDP协议算法如下
server机
1 利用socket创建一个套接字
2利用bind函数绑定server地址
3循环接收数据,用函数recvfrom(),发送数据用sendto();
4关闭连接
client机
1 利用socket创建一个套接字
2利用bind函数绑定server地址
3循环接收数据,用函数recvfrom(),发送数据用sendto();
4关闭连接
因为UDP是无连接协议,所以算法简单。
下面是一个简单的UDP程序
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <arpa/inet.h>
#define SERVER_PORT 8888
int main(void)
{
int socket_fd,sin_size,i;
struct sockaddr_in server_addr,client_addr;
char buf[1024];
sockfd=socket(AF_INET,SOCK_DGRAM,0);
bzero(&addr,sizeof(struct sockaddr_in));
addr.sin_family=AF_INET;
addr.sin_addr.s_addr=htonl(INADDR_ANY);
addr.sin_port=htons(SERVER_PORT);
bind(socket_fd,(struct sockaddr *)&server_addr,sizeof(struct soockaddr));
sin_size=sizeof(struct sockaddr);
n=recvfrom(socket_fd,buf,1024,0,(struct sockaddr *)&client_addr,&sin_size);
printf("receive %s from client \n",buf);
for(i=0;i<n;i++)
buf[i]=toupper(buf[i]);
n=sendto(socket_fd,buf,1024,0,(struct sockaddr *)&client_addr,&sin_size);
close(socket_fd);
}
/*client*/
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <arpa/inet.h>
#define SERVER_PORT 8888
#define MAX_BUF_SIZE 1024
void udpc_requ(int sockfd,const struct sockaddr_in *addr,int len)
{
char buffer[MAX_BUF_SIZE];
int n;
while(1)
{
/* 从键盘读入,写到服务端 */
printf("Please input char:\n");
fgets(buffer,MAX_BUF_SIZE,stdin);
sendto(sockfd,buffer,strlen(buffer),0,addr,len);
bzero(buffer,MAX_BUF_SIZE);
n=recvfrom(sockfd,buffer,MAX_MSG_SIZE,0,(struct sockaddr*)addr,&len); }
}
int main(int argc,char **argv)
{
int sockfd;
struct sockaddr_in addr;
if(argc!=2)
{
fprintf(stderr,"Usage:%s server_ip\n",argv[0]);
exit(1);
}
sockfd=socket(AF_INET,SOCK_DGRAM,0);
if(sockfd<0)
{
fprintf(stderr,"Socket Error:%s\n",strerror(errno));
exit(1);
}
/* 填充服务端的资料 */
bzero(&addr,sizeof(struct sockaddr_in));
addr.sin_family=AF_INET;
addr.sin_port=htons(SERVER_PORT);
if(inet_aton(argv[1],&addr.sin_addr)<0)
{
fprintf(stderr,"Ip error:%s\n",strerror(errno));
exit(1);
}
udpc_requ(sockfd,&addr,sizeof(struct sockaddr_in)); close(sockfd);
}
分析:通过例子以及程序的调试,可以知道UDP和TCP协议传输数据的区别,TCP协议传输数据时,是点到点的传送,当server和client在通信时,同IP的client不能和server通信,如图:
无法返回数据,说明TCP协议下,如果server与client已经握手,那么同IP的client不能与server通信。
而在UDP协议下传输数据时,当server和client在通信时,假如同IPclient 试图与server通信时,如图:
其中cas是另外一个同IP的client发送的数据,说明UDP是无连接的,而且可以无法确定数据传输的可靠性。
下面是一个TCP协议的并发服务程序:
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#define MY_PORT 3333
int main(int argc ,char **argv)
{
int listen_fd,accept_fd;
struct sockaddr_in client_addr;
int n;
if((listen_fd=socket(AF_INET,SOCK_STREAM,0))<0)
{
printf("Socket Error:%s\n\a",strerror(errno));
exit(1);
}
bzero(&client_addr,sizeof(struct sockaddr_in));
client_addr.sin_family=AF_INET;
client_addr.sin_port=htons(MY_PORT);
client_addr.sin_addr.s_addr=htonl(INADDR_ANY);
n=1;
if(bind(listen_fd,(struct sockaddr *)&client_addr,sizeof(client_addr))<0)
{
printf("Bind Error:%s\n\a",strerror(errno));
exit(1);
}
listen(listen_fd,5);
while(1)
{
accept_fd=accept(listen_fd,NULL,NULL);
if((accept_fd<0)&&(errno==EINTR))
continue;
else if(accept_fd<0)
{
printf("Accept Error:%s\n\a",strerror(errno));
continue;
}
if((n=fork())==0)
{
/* 子进程处理客户端的连接 */
char buffer[1024];
close(listen_fd);
n=read(accept_fd,buffer,1024);
write(accept_fd,buffer,n);
close(accept_fd);
exit(0);
}
else if(n<0)
printf("Fork Error:%s\n\a",strerror(errno));
close(accept_fd);
}
}