internet历史:
1、冷战时期: 1957年前苏联,1958。
1968年“资源共享的计算机网络”,是早期的网络雏形,简称"阿帕网","ARPAnet"。
2、阿帕网早起的传输控制协议: NCP,不能互联不同类型的计算机和不同类型的操作系统,没有纠错能力。
3、TCP/IP 协议: 是Internet工业上的标准,也被称为"世界语"。
TCP协议:检测网络传输中的差错控制协议。
IP 协议:互联不同类型的计算机和操作系统。
4、网络体系结构:
采用分而治之的设计思想。主要指网络层次结构和每层使用的协议的集合。
-
OSI七层模型: 应用层:提供应用层协议,FTP、E_mail、HTTP等,用来获取数据与用户进行交互。 表示层:将数据格式定义,转换/加密。 会话层:建立通信中逻辑名字与物理名字之间的联系。 传输层:差错处理/恢复,流量控制。提供可靠的数据传输服务。 网络层:数据分组、路由选择。 数据链路层:数据组成可发送、接口的帧。 物理层:信号、速率、接口等。 Tcp/IP协议体系结构: 应用层:提供应用程序的协议(TelNet、FTP、HTTP、DNS、SMTP等) HTTP:超文传输协议 HTTPS:超文传输协议,被加密的,安全性更高 DNS:域名解析协议 传输层:提供数据传输控制协议(TCP、UDP) TCP:传输控制协议 UDP:用户数据报协议 网络层:提供IP协议、ICMP(ping)、IGMP(组播) IP:网间协议 ICMP:互联网控制协议: ping IGMP: 网络接口和物理层:ARP(地址解析协议)、RARP(逆地址解析协议) ARP:地址解析协议, 将MAC地址 --》 IP RARP:逆地址解析协议, 将 IP --》 MAC地址
*** 5、TCP、UDP协议:
TCP:提供一种面向连接的控制协议,数据安全可靠(数据无失序、无重复、无丢失)。
应用场景:
1、安全性要求较高的场合,QQ、微信的密码传输。
2、大量数据传输,FTP
3、需要点对点建立的传输
优缺点:
1、数据安全可靠
2、传输效率低、耗时高
UDP:无连接的协议,不保证数据的可靠性。
应用场景:
1、少量数据传输,QQ、微信 聊天数据
2、网络流媒体数据传输,视频缓存
3、广播、组播
优缺点:
1、传输速率高
2、安全性差,容易丢包
** 6、预备知识
1、socket:
是一种网络编程的接口;
也是一个特殊的文件描述符。
2、套接字类型:
流式套接字(SOCK_STREAM):提供一个面向连接的服务,保证数据的安全性、可靠性,(TCP协议)。
数据报套接字(SOCK_DGRAM):无连接的服务器,不保证数据的安全可靠性(UDP协议)。
原始套接字(SOCK_RAW):原始底层通信服务(IP、ICMP、IGMP)。
3、ip地址:
用来在网络中标识主机的。常以点分形式"192.168.12.177"。
由:
-
网络号 + 子网号 + 主机号 : "192.168" + "12" + "177"。 IPv4地址:32位存储,主机号占 8位 IPv6地址:128位存储,主机号占 32位 根据ip地址的高8位进行分类: A类:"0.0.0.0" ~ "127.255.255.255" B类:"128.0.0.0" ~ "191.255.255.255" C类:"192.0.0.0" ~ "223.255.255.255" D类:"224.0.0.0" ~ "239.255.255.255" 组播地址 E类:"240.0.0.0" ~ "255.255.255.255" 保留地址
-
主机号: = ip地址 & (~子网掩码) = "192.168.12.177" * (~(255.255.255.0)) = 177 网段号: = ip地址 & (255 << 8) = "192.168.12.177" & (255 << 8) = 12 注意:网段(子网号)就是路由器编号,路由器ip:网络号+子网号+1 4、相关ip的转换函数: #include <arpa/inet.h> 1、将主机字符串的ip地址转换成 网络字节序 二进制地址,返回转换后的地址。
** in_addr_t inet_addr(const char *ip);
2、将网络字节序的二进制ip转换成 主机字符串ip
** char *inet_ntoa(struct in_addr inaddr);
5、端口号
用来标识处理网络数据的进程。也就是该进程的进程号。
6、相关端口号的转换函数:
1、主机字节序转网络字节序(小端序 -> 大端序)
** u_long htonl (u_long hostlong); // host to network long
** u_short htons (u_short short); // host to network short
2、网络字节序 转主机字节序(大端序 -> 小端序)
** u_long ntohl (u_long hostlong); // network to host long
** u_short ntohs (u_short short); // network to host short
编程流程:
1、基于tcp的服务器流程:
1、创建套接字 socket: 打开网卡设备、设置使用的网络协议和传输控制协议。
2、绑定 bind: 将本机ip、端口号与套接字绑定(将ip和端口号共享到网络中)。
3、监听 listen 设置监听套接字,在内核中开辟 请求的队列,并设置该缓存长度。
4、接受连接 accept 扫描请求队列是否有请求,如果没有就阻塞等待;
如果有请求就 取出请求建立连接(通信的管道)。
5、数据交互 read/write、recv/send
6、关闭 close
2、系统调用接口:
1、创建套接字
函数原型:
int socket(int domain, int type, int protocol);
头文件:
#include <sys/types.h>
#include <sys/socket.h>
参数:
domain :地址族协议,(网络层协议:IPV4-->AF_INET、IPV6-->AF_INET6)
type :套接字类型,(传输控制协议:流式套接字 --> SOCK_STREAM)
protocol :通常设置 0
返回值:
成功:返回 套接字(文件描述符)
失败:返回 -1,并设置错误信息
2、函数原型:
int bind(int sockfd, struct sockaddr *addr, size_t size);
功能:
将ip地址、端口号 与 套接字进行绑定,将ip和端口共享到网络中。
头文件:
同上
参数:
sockfd: 套接字,socket函数成功的返回值
addr : 通用协议地址结构体的首地址
size : 结构体的大小
返回值:
成功:返回 0
失败:返回-1,并设置错误信息
通用协议地址结构体:
struct sockaddr {
sa_family_t sa_family; //地址协议族 AF_INET
char sa_data[14];
}
Internet协议地址结构体:
struct sockaddr_in {
sa_family_t sin_family; /* address family: AF_INET */
in_port_t sin_port; /* port in network byte order */
struct in_addr sin_addr; /* internet address */
};
/* Internet address. */
struct in_addr {
uint32_t s_addr; /* address in network byte order */
};
结构体头文件: #include <arpa/inet.h>
填写ip地址和端口号
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(9527);
// INADDR_ANY (0.0.0.0) means any address for binding;
addr.sin_addr.s_addr = inet_addr("0.0.0.0");
3、函数原型:
int listen(int sockfd, int backlog);
功能:
设置监听套接字,设置 客户端连接请求队列的长度,
也就是设置同一时刻能接受的最大请求数。
监听完成就是启动服务器,套接字变成监听套接字
参数:
sockfd:套接字
backlog:请求队列的长度,在内核中开辟的。
返回值:
成功:返回 0
失败:返回-1,并设置错误信息
4、函数原型:
int accept(int sockfd, struct sockaddr *addr, socklen_t *len);
功能:
阻塞等待,接受客户端连接,建立通信管道。
如果 请求队列中没有客户端的请求,该函数就阻塞。有就立即接受连接,建立通道。
参数:
sockfd:监听套接字
addr :结构体首地址(存储连接成功的客户端ip、端口号的结构体首地址)
len :存储结构体的大小(存储客户端信息的结构体首地址)
返回值:
成功:返回连接套接字(通信管道的id号),标识成功的客户端。
失败:返回-1,并设置错误信息
注意:
1、如果不需要保存客户端ip地址和端口号,则第2、3参数 传递 NULL。
2、该函数调用一次 就只能接受一个 客户端连接
5、数据交互
函数原型:
ssize_t read(int connfd, void *buf, size_t size);
参数:
connfd:连接套接字(通信管道id)
ssize_t recv(int connfd, void *buf, size_t len, int flags);
参数:
flags:阻塞与非阻塞模式,通常0表示阻塞。
注意:
1、read读数据,默认为阻塞模式,如果读缓冲区有数据立即返回,无数据就等待
2、recv读数据可以设置 模式
3、当 连接断开时,read或recv立即返回 0值
ssize_t write(int connfd, void *buf, size_t size);
参数:
connfd:连接套接字(通信管道id)
ssize_t send(int connfd, void *buf, size_t len, int flags);
参数:
flags:阻塞与非阻塞模式,通常0表示阻塞。
6、关闭套接字
int close(int sockfd);
server、client基础版本
server.c
#include<stdio.h>
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#define BUFF_SIZE 128
int main()
{
//1.socket
int sockfd=socket(PF_INET,SOCK_STREAM,0);
if(sockfd==-1)
{
perror("SOCKET error!");
return -1;
}
printf("sockfd success!\n");
//2.bind
struct sockaddr_in saddr;
memset(&saddr,0,sizeof(saddr));
saddr.sin_family=PF_INET;
saddr.sin_port=htons(8888);
saddr.sin_addr.s_addr=INADDR_ANY;
int s_len=sizeof(saddr);
if (bind(sockfd,(struct sockaddr *)&saddr,s_len)<0)
{
perror("bind error!");
return -1;
}
printf("bind success!\n");
//3.listen
int ret =listen(sockfd,5);
if(ret==-1)
{
perror("listen fail!");
return -1;
}
printf("listening...\n");
//4.accept
while(1)
{//first while
struct sockaddr_in caddr;
memset(&caddr,0,sizeof(caddr));
int c_len=sizeof(caddr);
printf("wait a client ...!\n");
int connfd=accept(sockfd,(struct sockaddr*)&caddr,&c_len);
if(connfd==-1)
{
perror("conncet fail!");
return -1;
}
printf("connect success!\n");
char buf[BUFF_SIZE];
while(1)
{//second while
ret=read(connfd,buf,sizeof(buf));
if(ret<0)
{
perror("read error!");
return -1;
}
if(ret==0)
{
printf("disconnect!\n");
break;
}
printf("client:%s\n",buf);
//hui fu
write(connfd,buf,sizeof(buf));
}//second while
close(connfd);
}//first while
close(sockfd);
//5.read/write or revc/send
//6.close(connfd)and(sockfd)
return 0;
}
client.c
#include<stdio.h>
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#define BUFF_SIZE 128
int main()
{
//1.socket
int sockfd=socket(PF_INET,SOCK_STREAM,0);
if(sockfd==-1)
{
perror("SOCKET error!");
return -1;
}
printf("sockfd success!\n");
struct sockaddr_in saddr;
memset(&saddr,0,sizeof(saddr));
saddr.sin_family=PF_INET;
saddr.sin_port=htons(8888);
saddr.sin_addr.s_addr=INADDR_ANY;
if(connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr))==-1)
{
perror("connect fail!");
return -1;
}
char buf[BUFF_SIZE];
while(1)
{//first while
printf("input>");
fgets(buf,sizeof(buf),stdin);
if(strcmp(buf,"quit\n") == 0)break;
write(sockfd,buf,sizeof(buf));
bzero(buf,0);
read(sockfd,buf,sizeof(buf));
printf("server:%s\n",buf);
}//first while
close(sockfd);
return 0;
}
封装版本
serve.h
/*服务器搭建流程
1、肯定是要操作文件(网卡设备),那么socket去得到套接子。
2、绑定地址和端口号,就相当于挂起招牌,放到网络中去
3、设置监听,同一时刻允许的最大连接数,多了就会丢弃。
4、循环等待客户端连接
5、在循环里面等待到客户端连接,就开始按照客户端的合理需求,给客户端反馈。
6、关闭连接套接字,关闭socket套接子。
*/
#ifndef _SERVER_H
#define _SERVER_H
#include <stdio.h>
#include <string.h>
//socket
#include <sys/types.h>
#include <sys/socket.h>
#include <time.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<netinet/in.h>
#include<arpa/inet.h>
//read
#include <unistd.h>
#define MAXMEM 5 //listen maxmember queue
#define TIMEOUT 5//超时检测时间设置
static struct sockaddr_in s_addr;//save host IP and PORT,put it online
static int s_len = sizeof(s_addr);
static struct sockaddr_in c_addr;//save connect client IP and PORT
static int c_len = sizeof(c_addr);
static char buf[64] = {0};//save massage come from client
static int ret = 0;
static int sockfd;
static int connfd;
static int reuseaddr=1;
int server_init(int port);
int WaitForClient(int sockfd);
#endif
server.c
#include "server.h"
// 绑定IP和端口号放到网络上,一定要确定绑定操作的正确。主机字节序转换成网络字节序;
int server_init(int port)
{
//得到套截字
if((sockfd = socket(AF_INET,SOCK_STREAM,0)) < 0)
{
perror("sockfd");
return -1;
}
//设置端口号重用
if(setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&reuseaddr,sizeof(reuseaddr) ) < 0)
{
perror("socket");
return -1;
}
//准备一个saddr给bind函数
bzero(&s_addr,0);
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(port);
s_addr.sin_addr.s_addr=htonl(INADDR_ANY);
if( bind(sockfd,(struct sockaddr *)&s_addr,s_len ) <0)
{
perror("bind");
return -1;
}
//设置同一时刻最大的连接数
if( listen(sockfd,MAXMEM) < 0)
{
perror("listen");
return -1;
}
return sockfd;
}
//等待客户端连接函数
int WaitForClient(int sockfd)
{
//超时检测
struct timeval tm = {TIMEOUT,0};
if( setsockopt(sockfd,SOL_SOCKET,SO_RCVTIMEO,&tm,sizeof(tm)) < 0)
{
perror("setsockopt");
return -1;
}
bzero(&c_addr,0);
//accept等待客户端连接
if((connfd = accept(sockfd,(struct sockaddr *)&c_addr,&c_len)) < 0)
{
perror("accept");
return -1;
}
printf("connect from [%s:%d]\n",inet_ntoa(c_addr.sin_addr),ntohs(c_addr.sin_port));
//数据收发
while(1)
{
ret = read(connfd,buf,sizeof(buf));
if(ret == 0)
{
break;
}
else if(ret < 0)
{
perror("read");
break;
}
if(strcmp(buf,"quit") == 0)
{
printf("wait for a new client\n");
close(connfd);
break;
}
else if(strcmp(buf,"exit") == 0)
{
printf("server quit\n");
exit(0);
}
printf("client:%s\n",buf);
write(connfd,buf,ret);
memset(buf,0,sizeof(buf));
}
close(connfd);
}
text.c 测试代码
#include"server.h"
int main(int argc,char *argv[])
{
int sockfd = server_init(8888);
if(sockfd < 0)
{
return -1;
}
printf("server init success!\n");
//wait for client connect
while(1)
{
WaitForClient(sockfd);
}//1.while()
close(sockfd);
return 0;
}
网络传输时候组装数据和解析数据的
解析数据
int sscanf(要解析的字符串, “格式”,解析存放缓冲区);
注意%s解析的时候:“ ”遇到空格会断开,解决办法:还没想到;
组装数据
int sprintf(组装存放区域,“格式”,要组装的成员);注意等数据传输到网络中的时候都是以字符串的形式传输的。