一、计算机网络基础
1. 网络
-
网络:由若干结点和连接这些结点的链路组成,网络中的结点可以是计算机,交换机、路由器等设备。 网络设备有:交换机、路由器、集线器 传输介质有:双绞线、同轴电缆、光纤 -
互联网:把多个网络连接起来就构成了互联网。目前最大的互联网就是我们常说的因特网。 -
IP地址 IP 地址就是给因特网上的每一个主机(或路由器)的每一个接口分配的一个在全世界范围内唯一的标识符。IP 地址因其特殊的结构使我们可以在因特网上很方便地进行寻址。 IP 地址有分 IPV4 和 IPV6 两种类别格式,IPV4 是类似”A.B.C.D”的格式,它是 32 位的,用“.”分成四个段,每个段是 8 个位(值为 0-255),用 10 进制表示。IPV6 地址是 128位。 -
MAC地址 在局域网中,硬件地址又称为物理地址或者 MAC 地址,长度 48 位,是固化在计算机适配器的 ROM 中的地址。 MAC和IP都可唯一标识一台主机,为什么有了MAC还需要IP呢? (1)IP便于寻址; (2)IP可表示在网络中地变化,而MAC不能。比如当我们把一个笔记本从一个城市带到另一个城市时,虽然地理位置改变了,但是电脑在局域网中的“地址”仍然不变。所以在网络上方便寻找某个主机,还得需要IP地址来完成。 -
端口号 在一台主机上用来唯一标识一个应用程序(进程)。
2. 网络分层模型
网络分层的好处:
二、Socket网络编程
1. TCP编程流程
listen的作用: 四次挥手也可以演化成三次(二三步和一起)
TCP服务器端代码ser.c
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
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);
saddr.sin_addr.s_addr=inet_addr("127.0.0.1");
int res=bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
assert(res!=-1);
res=listen(sockfd,5);
while(1)
{
int len=sizeof(caddr);
int c=accept(sockfd,(struct sockaddr*)&caddr,&len);
if(c<0)
{
perror("accept error");
continue;
}
printf("c=%d\n",c);
while(1)
{
char buff[128]={0};
int n=recv(c,buff,127,0);
if(n<=0)
{
break;
}
printf("buff(%d)=%s",n,buff);
send(c,"0k",2,0);
}
printf("one client over!\n");
close(c);
}
return 0;
}
TCP客户端代码cli.c
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
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);
while(1)
{
char buff[128]={0};
printf("input:\n");
fgets(buff,128,stdin);
if(strncmp(buff,"end",3)==0)
{
break;
}
send(sockfd,buff,strlen(buff),0);
memset(buff,0,sizeof(buff));
recv(sockfd,buff,127,0);
printf("buff=%s\n",buff);
}
close(sockfd);
}
2. TCP协议特点
-
TCP和UDP的区别: TCP:面向连接、可靠的、字节流服务。使用 TCP 协议通信的双发必须先建立连接,然后才能开始数据的读写。双方都必须为该连接分配必要的内核资源,以管理连接的状态和连接上数据的传输。TCP 连接是全双工的,双方的数据可以通过一个连接进行读写。完成数据交换之后,通信双方都必须断开连接以释放系统资源。 UDP:无连接、不可靠、数据报服务。发送端应用程序每执行一次写操作,UDP 模块就将其封装成一个 UDP 数据报发送。接收端必须及时针对每一个 UDP 数据报执行读操作,否则就会丢包。并且,如果用户没有指定足够的应用程序缓冲区来读取 UDP 数据,则 UDP 数据将被截断。 -
TCP字节流服务 -
什么是粘包?怎么解决? 法一:发一次收一次 法二:每次先发数据的长度,再开始发数据,保证一次把这次数据收完 -
TCP的状态转移图 为什么四次挥手都完成了还要停留到TIME_WAIT状态,持续大约两个报文的时间?(TIME_WAIT状态存在的意义?) TIME_WAIT存在于主动关闭的一方。 (1)可靠地终止与TCP连接 (2)保证让迟来的TCP报文段有足够的时间被识别丢弃。 -
拥塞控制方法 慢启动和拥塞避免 快速重传和快速恢复
2. 多进程、多线程处理并发
ser.c
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<signal.h>
int main()
{
signal(SIGCHLD,SIG_IGN);
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=bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
assert(res!=-1);
res=listen(sockfd,5);
while(1)
{
struct sockaddr_in caddr;
int len=sizeof(caddr);
int c=accept(sockfd,(struct sockaddr*)&caddr,&len);
if(c<0)
{
perror("accept error");
continue;
}
pid_t pid=fork();
if(pid==-1)
{
close(c);
continue;
}
if(pid==0)
{
while(1)
{
char buff[128]={0};
int n=recv(c,buff,127,0);
if(n<=0)
{
break;
}
printf("buff(%d)=%s",n,buff);
send(c,"0k",2,0);
}
close(c);
printf("client over!\n");
exit(0);
}
close(c);
}
}
3. UDP编程流程
UDP 提供的是无连接、不可靠的、数据报服务. udpser.c
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
int main()
{
int sockfd=socket(AF_INET,SOCK_DGRAM,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=bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
assert(res!=-1);
while(1)
{
struct sockaddr_in caddr;
int len=sizeof(caddr);
char buff[128]={0};
int n=recvfrom(sockfd,buff,127,0,(struct sockaddr*)&caddr,&len);
printf("recv(%d):%s\n",n,buff);
sendto(sockfd,"ok",2,0,(struct sockaddr*)&caddr,sizeof(caddr));
}
}
udpcli.c,客户端的地址系统随机分配
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<assert.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
int main()
{
int sockfd=socket(AF_INET,SOCK_DGRAM,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");
while(1)
{
char buff[128]={0};
printf("input:\n");
fgets(buff,128,stdin);
if(strncmp(buff,"end",3)==0)
{
break;
}
sendto(sockfd,buff,strlen(buff),0,(struct sockaddr*)&saddr,sizeof(saddr));
memset(buff,0,128);
int len=sizeof(saddr);
recvfrom(sockfd,buff,127,0,(struct sockaddr*)&saddr,&len);
printf("buff=%s\n",buff);
}
close(sockfd);
}
UDP数据报服务,接收时要一次性接收完
三、HTTP协议与Web服务器
1. 浏览器与服务器通信过程
2. HTTP请求报文头
3. HTTP应答报文头
4. HTTP的应答状态
5. Web服务器的C语言实现
myhttp.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<assert.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<fcntl.h>
int socket_init();
char* get_filename(char buff[])
{
if(buff==NULL)
{
return NULL;
}
char* s=strtok(buff," ");
if(s==NULL)
{
return NULL;
}
printf("请求的方法是:%s\n",s);
s=strtok(NULL," ");
if(s==NULL)
{
return NULL;
}
return s;
}
int main()
{
int sockfd=socket_init();
assert(sockfd!=-1);
while(1)
{
struct sockaddr_in caddr;
int len=sizeof(caddr);
int c=accept(sockfd,(struct sockaddr*)&caddr,&len);
if(c<0)
{
continue;
}
char recv_buff[512]={0};
recv(c,recv_buff,511,0);
printf("read:\n%s\n",recv_buff);
char* filename=get_filename(recv_buff);
if(filename==NULL)
{
send(c,"err",3,0);
close(c);
continue;
}
char path[128]={"/home/chenfan/code"};
if(strcmp(filename,"/")==0)
{
strcat(path,"index.html");
}
else
{
strcat(path,filename);
}
int fd=open(path,O_RDONLY);
if(fd==-1)
{
send(c,"404",3,0);
close(c);
continue;
}
int filesize=lseek(fd,0,SEEK_END);
lseek(fd,0,SEEK_SET);
char head_buff[512]={"HTTP/1.0 200 OK\r\n"};
strcat(head_buff,"Server: myhttp\r\n");
sprintf(head_buff+strlen(head_buff),"Content-Length:%d\r\n",filesize);
strcat(head_buff,"\r\n");
send(c,head_buff,strlen(head_buff),0);
printf("send:%s\n",head_buff);
char data_buff[1024]={0};
int num=0;
while((num=read(fd,data_buff,1024))>0)
{
send(c,data_buff,1024,0);
}
close(c);
}
}
int socket_init()
{
int sockfd=socket(AF_INET,SOCK_STREAM,0);
if(sockfd==-1)
{
return -1;
}
struct sockaddr_in saddr;
memset(&saddr,0,sizeof(saddr));
saddr.sin_family=AF_INET;
saddr.sin_port=htons(80);
saddr.sin_addr.s_addr=inet_addr("127.0.0.1");
int res=bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
if(res==-1)
{
return -1;
}
res=listen(sockfd,5);
if(res==-1)
{
return -1;
}
return sockfd;
}
index.html
<html>
<head>
<meta charset=utf-8>
<title>主页</title>
</head>
<body background=1.png>
<a href="/next.html">下一页</a><br>
<a href="/www.baidu.com">链接百度</a>
<center>
<h1>送孟浩然之广陵</h1><br>
故人西辞黄鹤楼,<br>
烟花三月下扬州。<br>
孤帆远影碧空尽,<br>
唯见长江天际流。<br>
</center>
</body>
</html>
next.html
<html>
<head>
<meta charset=utf-8>
<title>新页面</title>
</head>
<body background=2.png>
<a href="/index.html">上一页</a>
</body>
</html>
|