一、网络编程
1.网络基础:
相关历史:
第一阶段:ARPANET(阿帕网),是网络基础协议的雏形
第二阶段:第一份IP协议说明书。
协议:两者之间需遵守的约定
第三阶段:TCP/IP
网络程序框架:C/S、B/S
C/S:表示客服端/服务器设计框架
优点:支持本地缓存(能实现较好的效果)、应用层协议可以自己灵活制定
缺点:开发工作量大、用户安全性较低
B/S:表示浏览器/服务器设计框架
优点:开发工作量小、用户安全性较高
缺点:不支持本地缓存、应用层协议必须遵守http协议
网络协议分层模型:
OSI分层模型:理想的网络分层模型
1.应用层
2.表示层
3.会话层
4.传输层
5.网络层
6.数据链路层
7.物理层
TCP/IP协议模型:一种标准的网络分层模型
1.应用层:http协议、tftp协议、NFS协议等
2.传输层:TCP协议、UDP协议等
3.网络层:IP协议、ICMP协议等
4.网络接口层:以太网、ARP协议、RARP协议等
总结:
1.应用层:可以由用户自定义的协议,方便输出传输以及数据加密
2.传输层:提供端对端的数据传输
3.网络层:提供数据的路由
4.网络接口层:实现网络底层驱动
端口号:表示进程(0~65535)unsigned short
内核使用0~1024,其他的可以自由分配
二、网络接口编程
nux系统内核提供套接字作为网络通讯的接口
TCP/IP四层协议,只有应用层是程序员能够自定义的,下面三层协议封装都是由内核完成的(但是有选择权力)
1.套接字分类
流式套接字(SOCK_STREAM):数据以二进制流的方式进行传递,无大小限制。保证数据可靠,无丢失,顺序发送。主要用于TCP协议,一般情况下,只要选择流式方式,那么内核就会默认选择TCP传输层协议
数据包套接字(SOCK_DGRAM):主要通过数据报的方式发送,固定大小。不能保证数据可靠,可能丢失、乱序发送,主要用于UDP协议。一般情况下,只要选择数据报式方式,那么内核就会默认选择UDP传输层协议
原始套接字( SOCK_RAW ):可以对较低层次协议如IP、ICMP直接访问。
2.相关接口函数
socket(); --创建套接字
bind(); --绑定套接字
listen(); --监听
accept(); --接受客户端连接请求
connect(); --主动发送连接请求
close(); --关闭套接字
read();/write(); --数据收发
(1)socket(); --创建套接字
#include <sys/types.h>
#include <sys/socket.h>int socket(int domain, int type, int protocol);
参数:
domain:通讯域的选择
AF_UNIX Local communication
AF_LOCAL Synonym for AF_UNIX
AF_INET IPv4 Internet protocols
type:套接字类型,决定了通讯语义
SOCK_STREAM
protocal:0
返回值:
成功返回监听套接字,错误返回-1
int sockfd = socket(AF_INET,SOCK_STREAM,0);
(2).bind(); --绑定套接字
#include <sys/types.h> #include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
参数:
返回值:成功返回0,失败返回-1
struct sockaddr {
sa_family_t sa_family;
char sa_data[14];
}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 */
};
struct addrssP_in
(3)listen(); --监听
#include <sys/types.h>
#include <sys/socket.h>int listen(int sockfd, int backlog);
参数:
sockfd:监听套接字
backlog:同时能够监听的客服端的个数
返回值:
成功返回0,失败返回-1
(4)accept(); --接受客户端连接请求
#include <sys/types.h>
#include <sys/socket.h>int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数:
sockfd:监听套接字
addr:客户端地址信息,如果不许客户端信息则传递 NULL
addrlen:客户端地址信息长度,如果不许客户端信息则传递 NULL
返回值:
成功返回用于通讯的套接字(连接套接字或通讯套接字),错误返回-1
(5).connect(); --服务器连接
#include <sys/types.h>
#include <sys/socket.h>int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
参数:
sockfd:监听套接字
addr:客户端地址信息,如果不许客户端信息则传递 NULL
addrlen:客户端地址信息长度,如果不许客户端信息则传递 NULL
返回值:
成功返回用于通讯的套接字(连接套接字或通讯套接字),错误返回-1
(6)close() --关闭套接字
#include <unistd.h>
int close(int fd);参数:
fd:需要关闭的套接字
返回值:
成功返回0,失败返回-1
3.
目录
TCP服务器搭建流程
创建套接字 ----socket();
eg:
int sockfd = socket(AF_INET,SOCK_STREAM,0);
绑定套接字 ----bind():ip地址与PORT端口
端口:标识进程 无符号短整型 0~65535,0~1024被内核使用,用户可指定1025~65535、
字节序:大端序 与 小端序
网络字节序,通常是大端序。大端就是指地地址存放高字节
个人PC的字节序通常是小端序。小段就是指地地址字节存放地地址
结论:需要在网络绑定端口时,进行字节序的转换。字节序转换函数:htons、htol等
htons:把主机字节序转换成网络字节序,以short类型
IP地址:标识主机,ipv4四个字节。
点分十进制:192.168.2.2
二进制形式:11000000(192) 10101000(168) 00000010(2) 00000010(2)
IP地址转换函数,点分转换成二进制
in_ADDR_T inet_addr(const char *cp)
eg:
struct sockaddr_in ser;
ser.sin_family=AF_INET;
ser.sin_port=htons(9090); (字节序转换函数)
ser.sin_addr.s_addr=inet_addr("192.168.2.2"); 自己的虚拟机的地址(IP地址转换函数)
bind(sockfd,(struct sockaddr*)&ser,sizeof(ser))
监听客户端连接请求 ---listen();
eg:
listen(sockfd,5);
接收客服端连接请求 ---accept()
eg:
int connfd = accept(sockfd,NULL,NULL);
关闭套接字:close()
eg:
close(connfd);
close(sockfd);
作业:建立一个简单的服务器
/*===============================================
* 文件名称:client.c
* 创 建 者:
* 创建日期:2022年08月15日
* 描 述:
================================================*/
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
char buf[64]={0};
int sockfd = socket(AF_INET,SOCK_STREAM,0);//创建监听套接字
printf("socket successful\n");
struct sockaddr_in ser;
ser.sin_family=AF_INET;
ser.sin_port=htons(9090); //自己设置,1025~65565
ser.sin_addr.s_addr=inet_addr("192.168.22.8");//ip设置为虚拟机的ip
int ret = bind(sockfd,(struct sockaddr*)&ser,sizeof(ser));//绑定套接字
if(ret<0)
{
perror("bind");
exit(-1);
}
printf("bind successful\n");
if(listen(sockfd,5)<0) //监听
{
perror("listen");
exit(-1);
}
printf("listen successful\n");
int accefd = accept(sockfd,NULL,NULL);//接收连接请求
if(accefd<0)
{
perror("accept");
exit(-1);
}
printf("accept successful\n");
while(1)
{
/* fgets(buf,64,stdin);
write(accefd,buf,64);//执行读写操作
*/
read(accefd,buf,64);
puts(buf);
}
close(sockfd); //关闭套接字
close(accefd);
return 0;
}
运行结果: