6 网络

6 网络

  • 1、概念
  • [2 IP地址](#2 IP地址)
  • 3、套接字
  • 4、TCP协议
    • [4.1 TCP协议的基本特征](#4.1 TCP协议的基本特征)
    • [4.2 建立连接](#4.2 建立连接)
    • [4.4 终止连接](#4.4 终止连接)
    • [4.5 编程模型](#4.5 编程模型)
  • 5、UDP协议
    • [5.1 UDP协议的基本特性](#5.1 UDP协议的基本特性)
    • [5.2 常用函数](#5.2 常用函数)
    • [5.3 UDP通信模型](#5.3 UDP通信模型)
  • 6、域名解析

1、概念

计算机网络是实现资源共享和信息传递的计算机系统

ISO/OSI网络协议模型

TCP/IP协议

·应用程序负责组织的通常都是与业务相关的数据内容,而要想把这些数据内容通过网络发送出去,就要将其自上向下地压入协议栈,每经历一个协议层,就会对数据做一层封包,每一层输出的封包都是下一层输入的内容,消息包沿着协议栈的运动形成了消息流。

·当从网络上接收数据时,过程刚好相反,消息包自下向上地流经协议栈,每经历一个协议层,就会对输入的数据解一层封包,经过层层解包以后,应用程序最终得到的将只是与业务相关的数据内容

2 IP地址

  • 什么是P地址?
    IP地址,是IP协议提供的一种统一的地址格式,为互联网上的每个网络和每台主机分配一个逻辑地址,借以消除物理地址所带来的差异性
  • IP地址如何表示?
    在计算机内部,IP地址用一个32位无符号整数表示,如:0x01020304。
    人们更习惯使用点分十进制字符串表示,如:1.2.3.4。字符串形式的从左到右,对应整数形式的从高字节到低字节。注意这里所说的高低指的是数位高低而非地址高低
  • 什么是IP地址分级?
    A级地址:以0为首的8位网络地址+24位本地地址
    B级地址:以10为首的16位网络地址+16位本地地址
    C级地址:以110为首的24位网络地址+8位本地地址
    D级地址:以1110为首的32位多播地址
    例如:某台计算机的IP地址:192.168.182.48,写成整数形式:11000000 10101000 10110110 00110000
    代表的是C级地址,网络地址:192.168.182.0,本地地址:48
  • 子网掩码
    • 借助子网掩码可以快速帮助我们区定IP地址的网络地址和本地地址
      • 以IP地址:192.168.182.48,子网掩码:255.255.255.0为例
        网络地址=IP地址&子网掩码
        192.168.182.48 & 255.255.255.0 = 192.168.182.0
        本地地址=IP地址&~子网掩码
        192.168.182.48 & 0.0.0.255 = 0.0.0.48

3、套接字

套接字代表着主机的通信能力

套接字接口库规定在网络传输过程中采用网络字节序,也就是大端字节序,

而本机数据可能是小短字节序

  • 小端字节序:数据的低位存放在低地址

  • 大端字节序:数据的低位存放在高地址

4、TCP协议

TCP提供客户机与服务器的连接

4.1 TCP协议的基本特征

一个完整TCP通信过程需要依次经历三个阶段

  • 首先,客户机必须建立与服务器的连接
  • 然后,凭借已建立好的连接,通信双方相互交换数据
  • 最后,客户机与服务器双双终止连接,结束通信过程
    TCP保证数据传输的可靠性
  • TCP的协议栈底层在向另一端发送数据时,会要求对方在一个给定的时间窗口内返回确认。如果超过了这个时间窗口仍没有收到确认,则TCP会重传数据并等待更长的时间。只有在数次重传均告失败以后,TCP才会最终放弃。TCP含有用于动态估算数据往返时间(Round-Trip Time,RTT)的算法,因此它知道等待一个确认需要多长时间
    TCP保证数据传输的有序性
  • TCP的协议栈底层在向另一端发送数据时,会为所发送数据的每个字节指定一个序列号。即使这些数据字节没有能够按照发送时的顺序到达接收方,接收方的TCP也可以根据它们的序列号重新排序,再把最后的结果交给应用程序
    TCP是全双工的
  • 在给定的连接上,应用程序在任何时候都既可以发送数据也可以接收数据。因此,TCP必须跟踪每个方向上数据流的状态信息,如序列号和通告窗口的大小

4.2 建立连接

三路握手

  • 客户机的TCP协议栈向服务器发送一个SYN分节,告知对方自己将在连接中发送数据的初始序列号
  • 服务器的TCP协议栈向客户机发送一个单个分节,其中不仅包括对客户机SYN分节的ACK应答,还包含服务器自己的SYN分节,以告知对方自己在同一连接中发送数据的初始序列号
  • 客户机的TCP协议栈向服务返回ACK应答,以表示对服务器所发SYN的确认

tcp包头结构,有20个字节,其中2个字节的源端口、2个字节的目的端口、4字节的序号、4字节的确认号等等共计20个字节的数据

三次握手的解释:

① 客户机首先向服务器发送数据包,数据包中的SYN的比特位是1,并且在序号位置指定一个数字,假定为100

② 服务器收到客户机发来的数据包,其中SYN的比特位是1,那么服务器就会应答一个数据包,数据包里的ACK比特位置1,代表一种应答,并且在确认号这里会将收到的序号+1,这里是101,代表服务器真的收到了客户机的数据包,然后将序号指定一个数字假定是200,并且将SYN比特位置1,一起发送给客户机

③ 客户机收到服务器发来的数据包,首先检查数据包中的ACK和确认号是否正确,然后会向服务器应答一个数据包,其中ACK比特位置1,代表是对服务器的应答,确认号填写收到的序号+1,这里是201,发送给服务器,代表客户机真的收到了服务器的数据包

④ 服务器收到数据包,检查ACK和确认号,至此连接建立

接口## 4.3 交换数据

  • 一旦连接建立,客户机即可构造请求包并发往服务器,服务器接收并处理来自客户机的请求包,构造响应包
  • 服务器向客户机发送响应包,同时捎带对客户机请求包的ACK应答。
  • 客户机接收来自服务器的响应包,同时向对方发送ACK应答

4.4 终止连接

四次挥手

  • 客户机或者服务器主动关闭连接,TCP协议栈向对方发送FIN分节,表示数据通信结束。如果此时尚有数据滞留于发送缓冲区中,则FIN分节跟在所有未发送数据之后
  • 接收到FIN分节的另一端执行被动关闭,-方面通过TCP协议栈向对方发送ACK应答,另一方面向应用程序传递文件结束符
  • 一段时间以后,方才接收到FIN分节的进程关闭自己的连接,同时通过TCP协议栈向对方发送FIN分节
  • 对方在收到FIN分节后发送ACK应答
    四次挥手的解释
    ① 客户机关闭套接字之后,客户机首先向服务器发送一个数据包,其中FIN比特位置1
    ② 服务器收到客户机发来的数据包,其中FIN比特位为1,向客户机回传一个应答,其中ACK置1
    ③ 服务器关闭对应的套接字操作之后,服务器向客户机发送一个数据包,其中FIN比特位置1
    ④ 客户机收到服务器发来的数据包,其中的FIN比特位为1,向服务器回传一个应答,其中ACK置1
    ⑤ 至此连接断开,通信终止

4.5 编程模型

  • 相关函数
    1:socket 创建套接字
c 复制代码
// 头文件 sys/socket.h
int socket(int domain,int type,int protocol);
- 功能:创建套接字
- 参数:
	- domain:通信域,协议族,可取以下值:
		PF_LOCAL/PF_UNIX - 本地套接字,进程间通信
		PF_INET - 基于IPV4的网络通信
		PF_INET6 - 基于IPv6的网络通信
		PF_PACKET - 基于底层包的网络通信
	- type:套接字类型,可取以下值:
		SOCK_STREAM - 流式套接字,基于TCP协议
		SOCK_DGRAM - 数据报套接字,基于UDP协议
		SOCK_RAW - 原始套接字,工作在传输层以下
	- protocol:特殊协议,对于流式和数据报套接字而言,只能取0
- 返回值:成功返回表示套接字对象的文件描述符,失败返回-1。

2:相关结构体

c 复制代码
/*套接字接口库通过地址结构定位一个通信主体,可以是一个文件,可以是一台远程主机,也可以是执行者自己 */
// 基本地址结构,本身没有实际意义,仅用于泛型化参数
struct sockaddr{
	sa_family_t sa_family; // 地址族
	char sa_data\[14\]; // 地址值
}
// 本地地址结构,用于AF_LOCAL/AF_UNIX域的本地通信
struct sockaddr_un{
	sa_family_t sun_family;// 地址族(AF_LOCAL/AF_UNIX)
	char sun_path\[\];// 本地套接字文件的路径
}
// 网络地址结构,用于AF_INET域的IPV4写径通信
struct sockaddr_in{
	sa_family_t sin_family;∥地址族(AF_INET)
	in_port_t sin_port; //端口号(0~65535) - unsigned short
	struct in_addr sin_addr;//IP地址 - unsigned int
}
// 网络地址结构,用于AF_INET域的IPV4网络通信
struct in_addr{
	in_addr_t s_addr;
}
typedef uint16_t in_port_t; //无符号16位整数
typedef uint32_t in_addr_t; //无符号32位整数

3:字节序转换函数

c 复制代码
uint32_t htonl(uint32_t hostlong); //长整形主机字节序到网络字节序
uint32_t ntohl(uint32_t netllong); //长整形网络字节序到主机字节序
uint16_t htons(uint16_t hostshort); //短整形主机字节序到网络字节序
uint16_t ntohs(uint16_t netshort); //短整型网络字节序到主机字节序
in_addr_t inet_addr(char const* ip); // 点分十进制字符串地址 -> 网络字节序形式整数地址
int inet_aton(char const* ip,struct in_addr* nip); //点分十进制字符串地址 -> 网络字节序形式整数地址
char* inet_ntoa(struct in_addr nip); //网络字节序形式整数地址 -> 点分十进制字符串地址

4:bind 将套接字和本机的地址结构绑定在一起

c 复制代码
// 头文件 sys/socket.h
int bind(int sockfd,struct sockaddr const* addr,socklen_t addrlen);
- 功能:将套接字和本机的地址结构绑定在一起
- 参数:
	- sockfd:套接字描述符。
	- addr:自己的地址结构。
	- addrlen:地址结构的字节数
- 返回值:成功返回0,失败返回-1。

5:listen 启动侦听

c 复制代码
// 头文件 sys/socket.h
int listen(int sockfd,int backlog)
- 功能:启动侦听:在指定套接字上启动对连接请求的侦听功能
- 参数:
	- sockfd:套接字描述符,在调用此函数之前是一个主动套接字,是不能感知连接请求的,在调用此函数并成功返回之后,是一个被动套接字,具有感知连接请求的能力。
	- backlog:未决连接请求队列的最大长度,一般取不小于1024的值。
- 返回值:成功返回0,失败返回-1。

6:accept

c 复制代码
// 头文件 sys/socket.h
int accept(int sockfd,struct sockaddr* addr,socklen_t* addrlen);
- 功能:等待并接受连接请求,在指定套接字上阻塞,直到连接建立完成。
- 参数:
	- sockfd:侦听套接字描述符
	- addr:输出连接请求发起方的地址信息
	- addrlen:输出连接请求发起方的地址信息字节数
- 返回值:成功返回可用于后续通信的连接套接字描述符,失败返回-1。
  • 案例
c 复制代码
// tcp 服务器
#include <stdio.h>
#include <ctype.h> // toupper
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <sys/socket.h> // 网络相关
#include <sys/types.h>// 网络相关
#include <arpa/inet.h>// 网络相关
#include <sys/wait.h>

// 收尸
void sigfun(int signum){
	printf("服务器:收尸\n");
	for(;;){
		pid_t pid = waitpid(-1,NULL,WNOHANG);
		if(pid == -1){
			if(errno == ECHILD){
				printf("没有子进程了\n");
				break;
			}else{
				perror("waitpid");
				return ;
			}
		}else if(pid == 0){
			printf("%d进程在运行\n",getpid());
			break;
		}else{
			printf("%d进程:回收了%d进程的僵尸\n",getpid(),pid);
		}
	}
}

int main(){
	printf("服务器设置信号量\n");
	if(signal(SIGCHLD,sigfun)==SIG_ERR){ // 对17号信号做捕获处理
		perror("signal");
		return -1;
	}
	printf("服务器:创建套接字\n");
	int sockfd = socket(AF_INET,SOCK_STREAM,0);
	if(sockfd == -1){
		perror("socket");
		return -1;
	}
	printf("服务器:组织地址结构\n");
	struct sockaddr_in ser;// IPV4的网络地址结构
	ser.sin_family = AF_INET;
	ser.sin_port = htons(8980);// 端口号,这里需要做字节序转换,因为TCP协议栈需要这个端口
	ser.sin_addr.s_addr = inet_addr("192.168.174.152");
	printf("服务器:绑定套接字和地址结构\n");
	if(bind(sockfd,(struct sockaddr*)&ser,sizeof(ser)) == -1){
		perror("bind");
		return -1;
	}
	printf("服务器:启动侦听\n");
	if(listen(sockfd,1024)==-1){// 监听,用于接收客户端的连接请求(3次握手)
		perror("listen");
		return -1;
	}
	for(;;){
		printf("服务器:等待连接\n");
		struct sockaddr_in cli; //用于输出客户端的地址结构
		socklen_t len = sizeof(cli); // 用来输出地址结构的大小
		int conn = accept(sockfd,(struct sockaddr*)&cli,&len); // 用于完成与客户端的3次握手中的后两次
		if(conn == -1){ // conn 是用来和客户端通信用的,每接入一个客户端,就会生成一个新的conn
			perror("accept");
			return -1;
		}
		printf("服务器:接收到%s:%hu的客户端的连接\n",inet_ntoa(cli.sin_addr),ntohs(cli.sin_port));
		printf("服务器:业务处理\n");
		pid_t pid = fork();
		if(pid == -1){
			perror("fork");
			return -1;
		}
		if(pid == 0){
			close(sockfd);// 关掉从父进程复制过来的通信套接字
			for(;;){
				// 接收客户端发送的小写的串
				char buf[64]={};
				ssize_t size = read(conn,buf,sizeof(buf)-1);
				if(size == -1){
					perror("read");
					close(conn);
					return -1;
				}
				//客户端断开连接时
				if(size == 0){break;}
				// 转大写
				for(int i=0;i<strlen(buf);i++){
					buf[i]=toupper(buf[i]);
				}
				// 将大写的串回传给客户端
				if(write(conn,buf,strlen(buf))==-1){
					perror("write");
					close(conn);
					return -1;
				}
			}
			printf("服务器:关闭通信套接字\n");
			close(conn);
			return 0;
		}
		close(conn);// 子进程已经复制了这个套接字,父进程自己的直接关掉即可
	}
	printf("关闭服务器\n");
	close(sockfd);
	return 0;
}

7:connect 将套接字和对方的地址结构连接在一起

c 复制代码
// 头文件 sys/socket.h
int connect(int sockfd,struct sockaddr const* addr,socklen_t addrlen);
- 功能:将套接字和对方的地址结构连接在一起
- 参数:
	- sockfd:套接字描述符
	- addr:对方的地址结构
	- addrlen:地址结构的字节数
- 返回值:成功返回0,失败返回-1

8:send 发送数据

c 复制代码
// 头文件 sys/socket.h
ssize_t send(int sockfd,void const* buf,size_t count,int flags);
- 功能:发送数据
- 参数:
	- 若flags取0则与write函数完全等价,另外也可取以下值:
		MSG_DONTWAIT - 以非阻塞方式接收数据。
		MSG_OOB - 接收带外数据。
		MSG_DONTROUTE - 不查路由表,直接在本地网络中寻找目的主机
- 返回值:成功返回实际发送的字节数,失败返回-1。

9:recv 接收数据

c 复制代码
// 头文件 sys/socket.h
ssize_t recv(int sockfd,void* buf,size_t count,int flags);
- 功能:接收数据
- 参数:
	- 若flags取0则与read函数完全等价,另外也可取以下值:
		MSG_DONTWAIT - 以非阻塞方式接收数据。
		MSG_OOB - 接收带外数据。
		MSG_WAITALL - 等待所有数据,即不接收到count字节就不返回。
- 返回值:成功返回实际接收到的字节数,失败返回-1。
  • 案例
c 复制代码
// 基于tcp客户端的连接
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>

int main(){
	printf("客户端:创建套接字\n");
	int sockfd = socket(AF_INET,SOCK_STREAM,0);
	if(sockfd == -1){
		perror("socket");
		return -1;
	}
	printf("客户端:组织服务器的地址结构\n");
	struct sockaddr_in ser;
	ser.sin_family = AF_INET;
	ser.sin_port = htons(8980);
	ser.sin_addr.s_addr = inet_addr("192.168.174.152");
	printf("客户端:发起连接\n");
	if(connect(sockfd,(struct sockaddr*)&ser,sizeof(ser))==-1){
		perror("connect");
		return -1;
	}
	printf("客户端:业务处理\n");
	for(;;){
		// 发送小写的串
		char buf[64]={};
		fgets(buf,sizeof(buf),stdin);
		if(strcmp(buf,"!\n")==0){
			break;
		}
		if(send(sockfd,buf,strlen(buf),0)==-1){
			perror("send");
			return -1;
		}
		// 接收大写的串
		if(recv(sockfd,buf,sizeof(buf)-1,0)==-1){
			perror("recv");
			return -1;
		}
		// 显示
		printf("%s",buf);
	}
	printf("客户端:关闭套接字\n");
	close(sockfd);
	return 0;
}

5、UDP协议

5.1 UDP协议的基本特性

  • UDP不提供客户机与服务器的连接
    • UDP的客户机与服务器不必存在长期关系。一个UDP的客户机在通过一个套接字向一个UDP服务器发送了一个数据报之后,马上可以通过同一个套接字向另一个UDP服务器发送另一个数据报。同样,一个UDP服务器也可以通过同一个套接字接收来自不同客户机的数据报
  • UDP不保证数据传输的可靠性和有序性
    • UDP的协议栈底层不提供诸如确认、超时重传、RTT估算以及序列号等机制。因此UDP数据报在网络传输的过程中,可能丢失,也可能重复,甚至重新排序。应用程序必须自己处理这些情况
  • UDP不提供流量控制
    • UDP的协议栈底层只是一味地按照发送方的速率发送数据,全然不顾接收方的缓冲区是否装得下
  • UDP是全双工的
    • 在一个UDP套接字上,应用程序在任何时候都既可以发送数据也可以接收数据

5.2 常用函数

1:recvfrom 从哪里接收数据

c 复制代码
// 头文件 sys/socket.h
ssize_t recvfrom(int sockfd,void* buf,size_t count,int flags,struct sockaddr* src_addr,socklen_t* addrlen);
- 功能:从哪里接收数据
- 参数:
	- 前四个参数和函数recv相同
	- src_addr:输出源主机的地址信息
	- addrlen:输入输出源主机的地址信息的字节数。
- 返回值:成功返回实际接收的字节数,失败返回-1

2:sendto 发送数据到哪里

c 复制代码
// 头文件 sys/socket.h
ssize_t sendto(int sockfd,void const* buf,size_t count,int flags,struct sockaddr const* dest_addr,socklen_t addrlen);
- 功能:发送数据到哪里
- 参数:
	- 前四个参数和函数send相同
	- dest_addr:目的主机的地址信息。
	- addrlen:目的主机的地址信息的字节数。
- 返回值:成功返回实际发送的字节数,失败返回-1

5.3 UDP通信模型

  • 服务器
c 复制代码
// udp 服务器
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys//types.h>
#include <arpa/inet.h>

int main(){
	printf("服务器:创建套接字\n");
	int sockfd = socket(AF_INET,SOCK_DGRAM,0);
	if(sockfd == -1){
		perror("socket");
		return -1;
	}
	printf("服务器:组织地址结构\n");
	struct sockaddr_in ser;
	ser.sin_family = AF_INET;
	ser.sin_port = htons(8090);
	ser.sin_addr.s_addr=inet_addr("192.168.174.152");
	printf("服务器:绑定套接字和地址结构\n");
	if(bind(sockfd,(struct sockaddr*)&ser,sizeof(ser))==-1){
		perror("bind");
		return -1;
	}
	printf("服务器:业务处理\n");
	for(;;){
		char buf[64]={};
		struct sockaddr_in cli;
		socklen_t len = sizeof(cli);
		if(recvfrom(sockfd,buf,sizeof(buf)-1,0,(struct sockaddr*)&cli,&len)==-1){
			perror("recvfrom");
			return -1;
		}
		// 转大写
		for(int i=0;i<strlen(buf);i++){
			buf[i] = toupper(buf[i]);
		}
		// 将大写的串回传到客户端
		if(sendto(sockfd,buf,strlen(buf),0,(struct sockaddr*) &cli,len) == -1){
			perror("sendto");
			return -1;
		}
	}
	printf("服务器关闭套接字\n");
	close(sockfd);
	return 0;
}
  • 客户端
c 复制代码
// UDP客户端
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
int main(){
	printf("客户端:创建套接字\n");
	int sockfd = socket(AF_INET,SOCK_DGRAM,0);
	if(sockfd == -1){
		perror("socker");
		return -1;
	}        
	printf("客户端:组织服务器地址结构\n");
	struct sockaddr_in ser;
	ser.sin_family = AF_INET;
	ser.sin_port = htons(8090);
	ser.sin_addr.s_addr = inet_addr("192.168.174.152");
	printf("客户端:业务处理\n");
	for(;;){
		// 向客户端发送小写的串
		char buf[64] = {};
		fgets(buf,sizeof(buf),stdin);
		if(strcmp(buf,"!\n")==0){
				break;
		}
		if(sendto(sockfd,buf,strlen(buf),0,(struct sockaddr*)&ser,sizeof(ser))==-1){
				perror("sendto");
				return -1;
		}
		// 接收服务器回传
		if(recv(sockfd,buf,sizeof(buf)-1,0)==-1){
				perror("recv");
				return -1;
		}
		// 显示
		printf("客户端:收到%s",buf);
	}
	// 关闭套接字
	close(sockfd);
	return 0;
}

6、域名解析

  • IP地址是网络上标识站点的数字地址,为了方便记忆,采用域名来代替1P地址标识站点地址
  • 域名解析就是域名到IP地址的转换过程。域名的解析工作由DNS服务器完成
  • 当应用过程需要将一个主机域名映P地址时,就调用域名解析函数,解析函数将待转换的域名放在DNS请求中,以UDP报文方式发给本地域名服务器。本地的域名服务器查到域名后,将对应的IP地址放在应答报文中返回。
    相关函数
    gethostbyname 获取主机信息
c 复制代码
// 头文件 netdb.h
struct hostent* gethostbyname(char const* host_name);
- 功能:通过参数所传的主机域名,获取主机信息
- 参数:
	- host_name 主机域名
- 返回值:函数执行成功返回表示主机信息的结构体指针,失败返回NULL
- 注意,该函数需要再联网情况下使用

结构

c 复制代码
struct hostent{
	char *h_name; //主机官方名
	char **h_aliases; //主机别名表
	int h_addrtype; //地址类型
	int h_length; //地址长度
	char **h_addr_list; //IP地址表
};
  • 案例
c 复制代码
#include <stdio.h>
#include <unistd.h>
#include <netdb.h>
#include <arpa/inet.h>
int main(int argc,char* argv[]){
	struct hostent* h = gethostbyname(argv[1]);
	if(h==NULL){
		perror("gethostbyname");
		return -1;
	}
	printf("主机官方名\n");
	printf("\t%s\n",h->h_name);
	printf("主机别名\n");
	for(char **pp = h->h_aliases;*pp;pp++){
		printf("\t%s\n",*pp);
	}
	printf("IP地址表\n");
	for(struct in_addr **pp=(struct in_addr**)h->h_addr_list;*pp;pp++){
		printf("\t%s\n",inet_ntoa(**pp));
	}
	return 0;
}
  • 创建http请求发送
c 复制代码
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>

int main(){
	// 创建套接字
	int sockfd = socket(AF_INET,SOCK_STREAM,0);
	if(sockfd == -1){
		perror("socket");
		return -1;
	}
	// 组织百度服务器的地址结构
	struct sockaddr_in ser;
	ser.sin_family = AF_INET;
	ser.sin_port = htons(80);
	ser.sin_addr.s_addr = inet_addr("36.155.132.76");
	// 向百度服务器发起连接
	if(connect(sockfd,(struct sockaddr*)&ser,sizeof(ser))==-1){
		perror("connect");
		return -1;
	}
	// 组织http请求并发送给百度服务器
	char request[1024] ={};
	sprintf(request,"GET / HTTP/1.1\r\n"
					"Host: www.baidu.com\r\n"
					"Accept: */*\r\n"
					"Connection: close\r\n\r\n");
	if(send(sockfd,request,strlen(request),0)==-1){
		perror("send");
		return -1;
	}
	// 接收百度服务器回传的响应
	for(;;){
		char respond[1024] = {};
		ssize_t size = recv(sockfd,respond,sizeof(respond)-1,0);
		if(size == -1){
			perror("recv");
			return -1;
		}
		if(size == 0){
			break;
		}
		printf("%s",respond);
	}
	printf("\n");
	close(sockfd);
	return 0;
}
相关推荐
幻想编织者31 分钟前
Ubuntu实时核编译安装与NVIDIA驱动安装教程(ubuntu 22.04,20.04)
linux·服务器·ubuntu·nvidia
幽兰的天空8 小时前
介绍 HTTP 请求如何实现跨域
网络·网络协议·http
lisenustc9 小时前
HTTP post请求工具类
网络·网络协议·http
心平气和️9 小时前
HTTP 配置与应用(不同网段)
网络·网络协议·计算机网络·http
心平气和️9 小时前
HTTP 配置与应用(局域网)
网络·计算机网络·http·智能路由器
hunter2062069 小时前
ubuntu向一个pc主机通过web发送数据,pc端通过工具直接查看收到的数据
linux·前端·ubuntu
Mbblovey9 小时前
Picsart美易照片编辑器和视频编辑器
网络·windows·软件构建·需求分析·软件需求
张3蜂10 小时前
docker Ubuntu实战
数据库·ubuntu·docker
北顾南栀倾寒10 小时前
[Qt]系统相关-网络编程-TCP、UDP、HTTP协议
开发语言·网络·c++·qt·tcp/ip·http·udp
GZ_TOGOGO11 小时前
PIM原理与配置
网络·华为·智能路由器