Linux应用软件编程---网络编程(TCP:[ 其他机制、头部标志位、应用示例 ]、 HTTP:[ 万维网、概念、格式、报文、应用示例 ]

一、TCP 网络协议补充内容

1、TCP 的其他机制

1)TCP 头部的标志位

TCP 头部可用抓包工具 (wireshark) 来查看。

|-------|-----------------------|
| 头部标志位 | 用途 |
| SYN | 请求建立连接标志位 |
| ACK | 响应报文标志位 |
| PSH | 携带数据标志位,通知接收方该从缓冲区读数据 |
| FIN | 请求断开连接标志位 |
| RST | 复位标志位 |
| URG | 紧急数据标志位 |

TCP 各部分所占字节数:

2、机制

1)安全可靠

(1)三次握手和四次挥手机制

(2)应答机制:TCP对于每一包数据都会给出相应的应答。发送数据时序列号表示这包数据的起始编号,响应报文中的确认号是接收方收到的最后一个字节编号+1。

(3)超时重传机制:当数据发送出去等待指定时间没有收到响应,此时认为这包数据丢失则进行冲传。

(4)滑动窗口机制:一段缓冲区,缓存TCP已发送未收到响应,准备发送等数据

2)提高效率

(1)延迟应答机制:发送数据的同时可以等待应答

(2)流量控制机制:结合TCP头部的窗口大小,动态调整发送速率。

(3)捎带应答机制:ACK报文可能和应用层的数据同时发送

3、应用示例

1)使用 TCP 协议实现全双工聊天

(1)客户端代码

cs 复制代码
#define SER_PORT 50000
#define SER_IP  "192.168.0.144"//服务端IP地址

int init_tcp_cli()
{
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (sockfd < 0)
	{
		perror("socket error");
		return -1;
	}
	
	struct sockaddr_in seraddr;
	seraddr.sin_family = AF_INET;
	seraddr.sin_port = htons(SER_PORT);
	seraddr.sin_addr.s_addr = inet_addr(SER_IP);

	int ret = connect(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));
	if (ret < 0)
	{
		perror("connect error");
		return -1;
	}
	return sockfd;
}

int main(int argc, const char *argv[])
{

	int sockfd = init_tcp_cli();
	if (sockfd < 0)
	{
		return -1;
	}

	pid_t pid = fork();
	if (pid > 0)
	{
		char buff[1024] = {0};

		while (1)
		{
			fgets(buff, sizeof(buff), stdin);
			send(sockfd, buff, strlen(buff), 0);
			if (0 == strcmp(buff, ".quit\n"))
			{
				break;
			}
		}
		wait(NULL);
	}
	else if (0 == pid)
	{
		char buff[1024] = {0};
		while (1)
		{
			recv(sockfd, buff, sizeof(buff), 0);
			if (0 == strcmp(buff, ".quit\n"))
			{
				break;
			}
			printf("B--->A : %s\n", buff);
		}
	}
	
	close(sockfd);
	return 0;
}

(2)服务端代码

cs 复制代码
#define SER_PORT  50000
#define SER_IP    "192.168.0.144"

int init_tcp_ser()
{
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (sockfd < 0)
	{
		perror("socket error");
		return -1;
	}
	
	struct sockaddr_in seraddr;
	seraddr.sin_family = AF_INET;
	seraddr.sin_port = htons(SER_PORT);
	seraddr.sin_addr.s_addr = inet_addr(SER_IP);
	
	int ret = bind(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));
	if (ret < 0)
	{
		perror("bind error");
		return -1;
	}

	ret = listen(sockfd, 10);
	if (ret < 0)
	{
		perror("listen error");
		return -1;
	}
	return sockfd;
}

int main(int argc, const char *argv[])
{
	int sockfd = init_tcp_ser();
	if (sockfd < 0)
	{
		return -1;
	}

	int connfd = accept(sockfd, NULL, NULL);
	if (connfd < 0)
	{
		perror("accept error");
		return -1;
	}
	
	pid_t pid = fork();
	if (pid > 0)
	{
		char buff[1024] = {0};
		while (1)
		{
			fgets(buff, sizeof(buff), stdin);
			send(connfd, buff, strlen(buff), 0);
			if (0 == strcmp(buff, ".quit\n"))
			{
				break;
			}
		}
		wait(NULL);
	}
	else if (0 == pid)
	{
		char buff[1024] = {0};
		while (1)
		{
			memset(buff, 0, sizeof(buff));
			recv(connfd, buff, sizeof(buff), 0);
			if (0 == strcmp(buff, ".quit\n"))
			{
				break;
			}
			printf("A---->B : %s\n", buff);
		}
	}
	close(connfd);
	close(sockfd);
	return 0;
}

2)使用 TCP 协议实现文件拷贝功能

(1)客户端代码

cs 复制代码
#define SER_PORT 50000
#define SER_IP  "192.168.0.179"

int init_tcp_cli()
{
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (sockfd < 0)
	{
		perror("socket error");
		return -1;
	}
	
	struct sockaddr_in seraddr;
	seraddr.sin_family = AF_INET;
	seraddr.sin_port = htons(SER_PORT);
	seraddr.sin_addr.s_addr = inet_addr(SER_IP);

	int ret = connect(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));
	if (ret < 0)
	{
		perror("connect error");
		return -1;
	}
	return sockfd;
}

int send_file(int sockfd, const char *filename)
{
	int fd = open(filename, O_RDONLY);
	if (fd < 0)
	{
		perror("open file error");
		return -1;
	}

	char buff[1024] = {0};
	while (1)
	{
		ssize_t cnt = read(fd, buff, sizeof(buff));
		if (cnt <= 0)
		{
			break;
		}
		send(sockfd, buff, cnt, 0);
	}
	close(fd);
}

int main(int argc, const char *argv[])
{
	if (argc < 2)
	{
		printf("Usage : ./a.out <sendfile>\n");
		return -1;
	}

	int sockfd = init_tcp_cli();
	if (sockfd < 0)
	{
		return -1;
	}
	send_file(sockfd, argv[1]);

	close(sockfd);
	return 0;
}

(2)服务端代码

cs 复制代码
#define SER_PORT  50000
#define SER_IP    "192.168.0.179"

int init_tcp_ser()
{
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (sockfd < 0)
	{
		perror("socket error");
		return -1;
	}
	
	struct sockaddr_in seraddr;
	seraddr.sin_family = AF_INET;
	seraddr.sin_port = htons(SER_PORT);
	seraddr.sin_addr.s_addr = inet_addr(SER_IP);
	
	int ret = bind(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));
	if (ret < 0)
	{
		perror("bind error");
		return -1;
	}

	ret = listen(sockfd, 10);
	if (ret < 0)
	{
		perror("listen error");
		return -1;
	}
	return sockfd;
}

int recv_file(int connfd, const char *filename)
{
	int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0664);
	if (fd < 0)
	{
		perror("open file error");
		return -1;
	}

	char buff[1024] = {0};
	while (1)
	{
		ssize_t cnt = recv(connfd, buff, sizeof(buff), 0);
		if (0 == cnt)
		{
			printf("client off\n");
			break;
		}
		else if (cnt < 0)
		{
			perror("recv error");
			break;
		}
		write(fd, buff, cnt);
	}
	close(fd);
}

int main(int argc, const char *argv[])
{
	if (argc < 2)
	{
		printf("Usage : ./a.out <recvfile>\n");
		return -1;
	}

	int sockfd = init_tcp_ser();
	if (sockfd < 0)
	{
		return -1;
	}

	int connfd = accept(sockfd, NULL, NULL);
	if (connfd < 0)
	{
		perror("accept error");
		return -1;
	}
	recv_file(connfd, argv[1]);

	close(connfd);
	close(sockfd);
	return 0;
}

二、HTTP 协议

1、万维网 WWW

1)概念

WWW 万维网(Web):称为世界范围的广域网,是一个大规模的、联机式的信息储藏方式。

2)解决的问题

(1)万维网服务器后台如何标记万维网数据 ?

url : 统一资源定位符

(2)万维网客户端与万维网服务器之前使用什么方式通信:

HTTP:超文本传输协议

(3)万维网客户端如何展示请求的数据:

HTML:超文本标记语言

2、url 统一资源定位符

格式: <协议>://<主机>:<端口>/<路径>

例如:https://www.baidu.com/

百度主页

3、HTTP 协议

1)概念

HTTP 协议:称为超文本传输协议,位于应用层。

端口:80

备用端口:8080

基于传输层的TCP协议

2)HTTP 通信过程

客户端通信流程:

(1)请求建立TCP连接

(2)发送HTTP请求报文

(3)接收HTTP响应报文

(4)断开连接

3)HTTP 的报文格式

(1)报文分类

HTTP 有两类报文:

请求报文------从客户到服务器请求报文,见图6-12(a)

响应报文------从服务端到客户的回答,见图6-12(b)

由于 HTTP 是面向文本的 ( text-oriented ) ,因此在报文中的每一个字段都是一些 ASCII 码串,因而每个字段的长度都是不确定的。

(2)HTTP 请求报文的方法

|---------|---------------------|
| 方法(操作) | 意义 |
| OPTION | 请求一些选项的信息 |
| GET | 请求读取由 URL 所标志的信息 |
| HEAD | 请求读取由 URL 所标志的信息的首部 |
| POST | 给服务器添加信息(例如,注释) |
| PUT | 在指明的 URL 下存储一个文档 |
| DELETE | 删除指明的 URL 所标志的资源 |
| TRACE | 用来进行环回测试的请求报文 |
| CONNECT | 用于代理服务器 |

(3)状态码

状态码(Status-Code)都是三位数字的,分为5大类共33种。例如:

|-----|-------------------------|
| 1xx | 表示通知信息的,如请求收到了或正在进行处理 |
| 2xx | 表示成功,如接受或知道了 |
| 3xx | 表示重定向,如要完成请求还必须采取进一步的行动 |
| 4xx | 表示客户的差错,如请求中有错误的语法或不能完成 |
| 5xx | 表示服务器的差错,如服务器失效无法完成请求 |

例如,常见的三种状态行:

(4)链接方式

Connection: keep-alive ---> 长连接:连接保持一定时间

Connection: close ---> 短连接:连接立马断开

4)HTTP 举例

访问 http://news.sohu.com,得到的请求报文与响应报文。

(1)HTTP 请求报文

|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 请求报文 |
| GET / HTTP/1.1\r\n Host: news.sohu.com\r\n User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/113.0\r\n Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8\r\n Accept-Language: en-US,en;q=0.5\r\n Connection: keep-alive\r\n \r\n |

(2)HTTP 响应报文

|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 响应报文 |
| HTTP/1.1 200 OK\r\n Date: Mon, 25 Aug 2025 06:14:56 GMT\r\n Content-Type: text/html;charset=utf-8\r\n Server: openresty\r\n Vary: Accept-Encoding\r\n Vary: Origin Vary: Access-Control-Request-Method Vary: Access-Control-Request-Headers Trace-Id: 15e6e7862abd49fdb1c327a6dbeb200d.10490.17561024969448219 Data-Source: X-Content-Type-Options: nosniff X-XSS-Protection: 0 S-REQ-ID: 17348448226369344247 S-REQ-TYPE: 0 X-Cache-Lookup: Cache Miss Content-Encoding: gzip Cache-Control: no-cache\r\n Transfer-Encoding: chunked\r\n X-NWS-LOG-UUID: 17348448226369344247\r\n Connection: keep-alive\r\n X-Cache-Lookup: Cache Miss\r\n \r\n <!DOCTYPE html><html><head><script>if(window&&window.performance&&typeof window.performance.now==='function'){!window.MptcfePerf?window.MptcfePerf={headst:+new Date()}:window.MptcfePerf.headst=+new Date()}</script><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta http-equiv=x-dns-prefetch-control content=on><meta name |

5)使用响应报文定位网页

cs 复制代码
#define SER_PORT 80
#define SER_IP "219.144.82.95"

int create_tcp_connect()
{
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd < 0)
    {
        perror("socket error");
        return -1;
    }

    struct sockaddr_in seraddr;
    seraddr.sin_family = AF_INET;
    seraddr.sin_port = htons(SER_PORT);
    seraddr.sin_addr.s_addr = inet_addr(SER_IP);

    int ret = connect(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));
    if(ret < 0)
    {
        perror("connect error");
        return -1;
    }
    return sockfd;
}

int send_http_request(int sockfd)
{
    char *preq = "GET / HTTP/1.1\r\n"
                "Host: news.sohu.com\r\n"
                "User-Agent: Mozilla/5.0(X11;Ubuntu;Linuxx86_64;rv:109.0)Gecko/20100101 Firefox/113.0\r\n"
                "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8\r\n"
                "Accept-Language: en-US,en;q=0.5\r\n"
                "Connection: close\r\n"
                "\r\n";

    ssize_t cnt = send(sockfd, preq, strlen(preq), 0);
    if(cnt < 0)
    {
        perror("send error");
        return -1;
    }
    return 0;
}

int recv_http_respose(int sockfd)
{
    char buff[1024] = {0};
    while(1)
    {
        ssize_t cnt = recv(sockfd, buff, sizeof(buff), 0);
        if(cnt < 0)
        {
            perror("recv error");
            return -1;
        }
        else if(0 == cnt)
        {
            printf("server off\n");
            break;
        }
        write(1, buff, cnt);
    }
    return 0;
}

int main(void)
{
    int sockfd = create_tcp_connect();
    if(sockfd < 0)
    {
        return -1;
    }

    send_http_request(sockfd);
    recv_http_respose(sockfd);

    close(sockfd);
    return 0;
}

【END】

相关推荐
轻松Ai享生活38 分钟前
Linux Swap 详解 (1)
linux
東雪蓮☆1 小时前
深入理解 iptables:Linux 防火墙从入门到精通
linux·运维·网络
努力学习的小廉2 小时前
深入了解linux系统—— 线程互斥
linux·运维·服务器
DDC楼宇自控与IBMS集成系统解读2 小时前
BA 楼宇自控系统 + AI:重构楼宇设备管理的 “智能决策” 体系
大数据·网络·数据库·人工智能·3d·重构
JioJio~z2 小时前
PLC通讯中遇到的实际场景
运维·服务器·网络
小米里的大麦3 小时前
034 进程间通信 —— System V 共享内存
linux
zgc12453673 小时前
Linux学习-网络编程2
linux·网络·学习
博睿谷IT99_3 小时前
OSPF 的工作过程、Router ID 机制、报文结构
开发语言·网络·华为·智能路由器·网络工程师·华为认证·数据通信
梅见十柒3 小时前
UNIX网络编程笔记:高级套接字编程20-25
网络·经验分享·笔记·unix
专注VB编程开发20年3 小时前
.NET组件读取压缩包中的内存数据电子表格XLSX文件和图片,不需要解压
linux·服务器·windows·c#·.net·excel·zip