tcp、udp的应用:
一、http协议存在两个报文,一个为请求报文(浏览器主动发给服务器的),一个为应答报文。
1、请求报文
2、应答报文
二、tcp服务器 – 让浏览器作为客户端,去链接使用基于c语言的服务器,观察收到的信息并回复相关数据
DNS域名解析
在Windows上ping一下百度:
通讯过程
其中 14.215.177.38 就为百度的服务器,在不同地域,其地址可能不一样。
HTTP协议使用 80号 协议。HTTPs 使用的是43号端口。
浏览器发来请求,解析以后发回浏览器,一般会发送html文件(网页结构),css布局,js,和一些资源(.jpg等)。
可以在服务器段创建一个文件,将该文件发送给浏览器。
三、代码实现示例
服务器启动后,浏览器链接,浏览器可向服务器请求数据,服务器发送数据
1、完成链接,回复网页ok
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
//创建套接字
int socket_init();
int recv_request(int c);//接收请求
void send_response(int c);//恢复应答
int main()
{
int sockfd = socket_init();
if(sockfd == -1)
{
printf("socket err/n");
exit(0);
}
while( 1 )
{
//接受客户端的链接
struct sockaddr_in caddr;
int len = sizeof(caddr);//大小
int c = accept(sockfd,(struct sockaddr*)&caddr,&len);//会阻塞 等待客户端链接,这里客户端为浏览器,及等待浏览器连接
if( c < 0 )
{
continue;
}
//浏览器的收和发
recv_request(c);//接收请求
send_response(c);//回复响应,回复应答
close(c);
}
}
int recv_request(int c)//接收请求
{
char buff[1024] = {0};
int n = recv(c,buff,1023,0);
if( n <= 0)
{
return -1;
}
printf("recv:\n%s\n",buff);
}
void send_response(int c)//回复应答
{
send(c,"ok",2,0);
}
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);//端口,http协议使用80号端口,小于1024的端口需要使用管理员权限
saddr.sin_addr.s_addr = inet_addr("0.0.0.0");//自身的真实ip,这里为模拟的ip
int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
if( res == -1)
{
return -1;
}
if( listen(sockfd,5) == -1 )
{
return -1;
}
return sockfd;
}
运行结果
2、回复正常网页,这里回复html文件
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
//创建套接字
int socket_init();
int recv_request(int c);//接收请求
void send_response(int c);//恢复应答
int main()
{
int sockfd = socket_init();
if(sockfd == -1)
{
printf("socket err/n");
exit(0);
}
while( 1 )
{
//接受客户端的链接
struct sockaddr_in caddr;
int len = sizeof(caddr);//大小
int c = accept(sockfd,(struct sockaddr*)&caddr,&len);//会阻塞 等待客户端链接,这里客户端为浏览器,及等待浏览器连接
if( c < 0 )
{
continue;
}
//浏览器的收和发
recv_request(c);//接收请求
send_response(c);//回复响应,回复应答
close(c);
}
}
int recv_request(int c)//接收请求
{
char buff[1024] = {0};
int n = recv(c,buff,1023,0);
if( n <= 0)
{
return -1;
}
printf("recv:\n%s\n",buff);
}
void send_response(int c)//回复应答
{
//打开文件
int fd = open("index.html",O_RDONLY);//这里写死为index.html
if( fd == -1 )
{
return;
}
//计算文件的大小
int filesize = lseek(fd,0,SEEK_END);
lseek(fd,0,SEEK_SET);
//组装一个报头
char http_buff[1024] = {"HTTP 1.1 200 OK\r\n"};
strcat(http_buff,"Server:http\r\n");
//将大小拼接到报文中
sprintf(http_buff+strlen(http_buff),"Content_Length: %d\r\n",filesize);
//再拼接一个\r\n,区分头部和数据
strcat(http_buff,"\r\n");
//发送信息
send(c,http_buff,strlen(http_buff),0);//报头部分
//发送数据部分
char data[1024] = {0};
int n = 0;
while( n = read(fd,data,1024) >0 )
{
send(c,data,n,0);
}
close(fd);
}
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);//端口,http协议使用80号端口,小于1024的端口需要使用管理员权限
saddr.sin_addr.s_addr = inet_addr("0.0.0.0");//自身的真实ip,这里为模拟的ip
int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
if( res == -1)
{
return -1;
}
if( listen(sockfd,5) == -1 )
{
return -1;
}
return sockfd;
}
index.html 文件
<html>
<head>
<meta charset=utf8>
<title>页面</title>
</head>
<body>
<h1>你好
</body>
</html>
运行结果:
这里出现乱码,是因为解码和编码的格式不一样。
缺少代码<meta charset=utf8>
添加<meta charset=utf8>
右键查看网页源码,即为所写的index.html文件:
3、多线程实现连接,并返回报文
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <pthread.h>
#define BUFF_SIZE 4096
#define PATH "/home/stu/桌面/daima/http"//存放资源文件的位置
#define ER404 "404 页面找不到"
#define HEAD_SIZE 1024
struct ArgNode
{
pthread_t id;
int c;
};
int socket_init();
int http_reponse(int c, char* filename)
{
if ( filename == NULL )
{
return -1;
}
char path[256] = {PATH};//将资源文件路径存放
strcat(path,filename);
printf("path:%s\n",path);//打印出全路径
int fd = open(path,O_RDONLY);
if ( fd == -1 )//打开失败或无资源文件
{
send(c,ER404,strlen(ER404),0);
return -1;
}
int filesize = lseek(fd,0,SEEK_END);//空文件或者错误
if ( filesize <= 0 )//=0文件没有内容,为空文件,<0出错
{
return -1;
close(fd);
}
//将偏移量重新移动到起始位置
lseek(fd,0,SEEK_SET);
//组装报头
char http_head[HEAD_SIZE] = {0};
strcat(http_head,"HTTP/1.1 200 OK\r\n");
strcat(http_head,"Server: myhttp\r\n");
sprintf(http_head+strlen(http_head),"Content-Length: %d\r\n",filesize);
strcat(http_head,"\r\n");//http报头组装完成
send(c,http_head,strlen(http_head),0);//发送http报头
//这里的写法较为简单粗暴,可能会出错
printf("send:\n%s\n",http_head);
//发送数据
int num = 0;
char data[BUFF_SIZE];
while( (num = read(fd,data,BUFF_SIZE)) > 0 )
{
send(c,data,num,0);
}
close(fd);
return 0;
}
char* get_filename(char buff[])
{
if ( buff == NULL )
{
return NULL;
}
char* ptr = NULL;
char* s = strtok_r(buff," ",&ptr);
if ( s == NULL )
{
return NULL;
}
printf("请求方法:%s\n",s);
s = strtok_r(NULL," ",&ptr);
if ( s == NULL )
{
return NULL;
}
if ( strcmp(s,"/") == 0 )
{
return "/index.html";
}
return s;
}
void* thread_fun(void* arg)
{
struct ArgNode * p = (struct ArgNode*)arg;
if (p == NULL )
{
pthread_exit(NULL);//退出线程,进程其他线程可以继续执行
}
int c = p->c;
while( 1 )
{
char buff[BUFF_SIZE] = {0};
int n = recv(c,buff,BUFF_SIZE-1,0);//接收浏览器的http请求报文
if ( n <= 0 )
{
printf("连接关闭,读取错误er:%d\n",n);
break;//浏览器关闭连接
}
printf("%s\n",buff);
char* filename = get_filename(buff);
if ( filename == NULL )
{
continue;
}
printf("请求资源名:%s\n",filename);//filename为文件名
if( http_reponse(c,filename) == -1 )//生成应答报文
{
break;
}
}
close(c);
free(p);//释放堆区空间
pthread_exit(NULL);
}
int main()
{
int sockfd = socket_init();
if ( sockfd == -1 )
{
printf("socket err\n");
exit(0);
}
while( 1 )
{
struct sockaddr_in caddr;
int len = sizeof(caddr);
int c = accept(sockfd,(struct sockaddr*)&caddr,&len);//阻塞 -- 浏览器连接 ,返回
if ( c < 0 )
{
continue;
}
struct ArgNode * p = (struct ArgNode*)malloc(sizeof(struct ArgNode));
if ( p == NULL )
{
close(c);
continue;
}
p->c = c;//
if ( pthread_create(&(p->id),NULL,thread_fun,p) != 0 )
{
free(p);
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);//http 80,root
saddr.sin_addr.s_addr = inet_addr("0.0.0.0");
int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
if ( res == -1 )
{
return -1;
}
if ( listen(sockfd,64) == -1 )
{
return -1;
}
return sockfd;
}
运行结果:
运行服务器:
手动输入 地址加 index.html
结果为:
在目录地下添加图片和图标ico
进行访问
就能得到图片
改进index.html 文件
<html>
<head>
<meta charset=utf8>
<title>百度一下,你就知道</title>
</head>
<body>
<h1>你好<h1>
</body>
</html>
<html>
<head>
<meta charset=utf8>
<title>百度一下,你就知道</title>
</head>
<body background="src1.webp">
<center>
<h1>你好秋天~<h1>
</center>
</body>
</html>
输入没有的文件,这里回复404,但由于未处理,这里回复为乱码。
三、如何在Linux内查看自己的IP地址:
在 linux 下可以通过两个命令来查看本机的 IP 地址(注意要进入管理员模式):
1.支持包括 Linux 在内的所有 Unix 系统。
#/sbin/ifconfig
2. 对于Linux 而言,也可以使用 ip 命令查看,提示:没有ifconfig命令时可以用此命令查看
#ipaddrshow