1.基础知识
0.0.0.0 匹配所有IP地址
A类:
特点:一字节网络号,3字节主机号,网络号第一位为0.
范围:0.0.0.1~126.255.255.255
A类地址最大可用网络数:27 -2(网络号除了第一位全0,匹配所有的IP地址;127为环回地址)
A类地址最大可用主机数“224-2(主机号全为0是网关地址;全为1是广播地址)
IP地址数: 网络数*主机数=2113928964(21亿多)
B类:
特点:2字节网络号,2字节主机号,网络号前两位为10.
范围:128.0.0.0~191.255.255.255
B类地址最大可用网络数:214 -1(网络号除了前两位全0,匹配所有的IP地址)
B类地址最大可用主机数“216-2(主机号全为0是网关地址;全为1是广播地址)
IP地址数: 网络数*主机数=1073643522(10亿多)
C类:
特点:3字节网络号,1字节主机号,网络号前两位为110.
范围:192.0.0.0~223.255.255.255
C类地址最大可用网络数:221 -1(网络号除了前三位全0,匹配所有的IP地址)
C类地址最大可用主机数“28-2(主机号全为0是网关地址;全为1是广播地址)
IP地址数: 网络数*主机数=532676354(5亿多)
192.168.2.0/24 (24)指网络号的位数
子网掩码:求出IP地址的网络号或者主机号
子网掩码 & IP地址 = 网络号
~子网掩码 & IP地址 = 主机号
255.255.255.0(C类地址,3位网络号,1位主机号)
协议:双方的约定
常见的协议
(http超文本传输信息),FTP(文件传输协议)
TCP(传输控制协议),UDP(用户数据报协议)
ip(网间协议)
ARP(地址解析协议 IP地址--->mac地址)
RARP(反地址解析协议 mac地址--->IP地址)
协议可以不遵守,但是不安全
封包与拆包的过程:协议+层次结构
数据封包与拆包由操作系统完成
端口号1~1023(1~255之间为众所周知端口,256~1023端口通常由UNIX系统占用)
已登记端口:2014~49151
动态或私有端口:49152~65535
计算机一般是小端存储,网络字节序一般是大端存储
端口号转换
htonl: host to network long
htons: host to network short
ntohl: network to host long
ntohs: host to network short
IP地址转换
inet_addr:IP to network
inet_ntoa:network to IP
作业:
1.先关客户端,服务器会一直打印
2.关闭客户端之后,在连接,会显示地址还存在
3.客户端发送一个字符串,服务器返回字符串的长度给客户端
sprintf(buf,"%d",strlen(buf));
4.实现一个时间服务器,客户端发送time,服务器返回当前时间
5.客户端发送get 1.txt的请求,服务器获取文件内容后,发送给客户端。
服务器:
/*===============================================
* 文件名称:server.c
* 创 建 者:
* 创建日期:2022年08月16日
* 描 述:
================================================*/
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
//1.创建监听套接字
int sockfd = socket(AF_INET,SOCK_STREAM,0);
if(sockfd <0)
{
perror("sockfd");
return -1;
}
//端口复用函数
int on=1;
int k=setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
if(k<0)
{
perror("setsockopt");
return -1;
}
//2.绑定服务器的IP地址与端口号
struct sockaddr_in ser;
ser.sin_family=AF_INET;
ser.sin_port=htons(8888);
//ser.sin_addr.s_addr=inet_addr("0.0.0.0");//"0"表示本机的所有ip
ser.sin_addr.s_addr=htonl(INADDR_ANY);//"0"表示本机的所有ip
int ret= bind(sockfd,(struct sockaddr *)&ser,sizeof(ser));
if(ret<0)
{
perror("bind");
return -1;
}
//3.监听
if(listen(sockfd,10))//最大数量<1024-3
{
perror("listen");
return -1;
}
//4.等待客户端连接,返回通讯套接字(connfd)
while(1)
{
struct sockaddr_in cli;//创建结构体接收客户顿的内容
int len=sizeof(cli);
printf("wait a client\n");
int connfd = accept(sockfd,(struct sockaddr *)&cli,&len);//如果不关心客户端的地址,可以填NULL
if(connfd<0)
{
perror("accept");
return -1;
}
printf("client ip=%s client port=%d\n",inet_ntoa(cli.sin_addr),ntohs(cli.sin_port));
//5.收发数据(重点)
char buf[64]={0};
char str[64]={0};
while(1)
{
int n=read(connfd,buf,64);
if(n<0)
{
perror("read");
return -1;
}
else if(n==0)//客户端关闭了
{
break;
}
if(strcmp(buf,"time")==0)//显示时间
{
time_t my_t;
time(&my_t);
struct tm *t=localtime(&my_t) ;
sprintf(str,"%02d年%02d月%02d日 %02d:%02d:%02d\n",t->tm_year+1900,t->tm_mon+1,t->tm_mday,t->tm_hour,t->tm_min,t->tm_sec);
send(connfd,str,64,0);
memset(buf,0,64);//数组清空
memset(str,0,64);
}
else if(strncmp(buf,"get",3)==0)//显示文件内容
{
char name[64]={0};
strcpy(name,buf+4);
puts("name");
int fd=open(name,O_RDWR);
if(fd<0)
{
perror("open");
return -1;
}
int n;
while((n=read(fd,buf,64)))//读完退出
{
printf("n=%d\n",n);
send(connfd,buf,n,0);
memset(buf,0,64);//数组清空
}
//send(connfd,"over",5,0);//对面接受完成的标志
printf("send %s\n",name);
close(fd);
}
else//正常情况,求字符串长度
{
printf("message:%s\n",buf);
sprintf(str,"%ld",strlen(buf));
send(connfd,str,64,0);
memset(buf,0,64);//数组清空
memset(str,0,64);
}
}
close(connfd);
}
close(sockfd);
return 0;
}
客户端:
/*===============================================
* 文件名称:server.c
* 创 建 者:
* 创建日期:2022年08月16日
* 描 述:
================================================*/
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
//1.创建监听套接字
int sockfd = socket(AF_INET,SOCK_STREAM,0);
if(sockfd <0)
{
perror("sockfd");
return -1;
}
//2.接收连接
struct sockaddr_in ser;
ser.sin_family=AF_INET;
ser.sin_port=htons(8888);
//ser.sin_addr.s_addr=inet_addr("0.0.0.0");//"0"表示本机的所有ip
ser.sin_addr.s_addr=htonl(INADDR_ANY);//"0"表示本机的所有ip
int ret= connect(sockfd,(struct sockaddr *)&ser,sizeof(ser));//建立连接
if(ret<0)
{
perror("connect");
return -1;
}
char buf[64]={0};//储存数据
char len[64]={0};//储存字符串长度
while(1)
{
fgets(buf,64,stdin); //终端输入
buf[strlen(buf)-1]='\0';
int n=send(sockfd,buf,64,0);
if(n<0)
{
perror("read");
return -1;
}
else if((strcmp(buf,"quit"))==0)//客户端请求关闭
{
break;
}
else if(strcmp(buf,"time")==0)//接收时间
{
read(sockfd,len,64);
printf("time: %s\n",len);
memset(len,0,64);
}
else if(strncmp(buf,"get",3)==0)//接收文件内容
{
int fd=open("2.txt",O_RDWR | O_CREAT |O_TRUNC ,0664);
if(fd<0)
{
perror("open");
return -1;
}
int count=0;
while((count=recv(sockfd,buf,64,0)))
{
//if(strcmp(buf,"over")==0)//服务器发送完毕后会发送over提示已发送完毕
printf("%s \n",buf);
write(fd,buf,strlen(buf));
//write(1,buf,64);
memset(buf,0,64);//数组清空
if(count<64)
{
close(fd);
memset(buf,0,64);
break;
}
}
printf("recv -------------------------------\n");
close(fd);
}
else //接收字符串长度
{
read(sockfd,len,64);
printf("len=%s\n",len);
memset(len,0,64);
memset(buf,0,64);//数组清空
}
}
close(sockfd);
return 0;
}
运行结果: