C++(17)文件上传和下载

307 阅读3分钟

网络传输中文件的上传和下载

使用TCP的方式,在服务器和客户端之间进行文件的传输,传输文件时需要传输文件名,文件内容,设置传输完成的标志,处理出错的信息。

TCP三次握手/四次挥手

image-20220721233310049

while(getchar()!='\n'); ----------- 清除输入缓冲区的垃圾数据

IO多路复用

IO多路复用可以同时监控多个描述符,找出其中"活动的"描述符,所谓活动的就是指可以读/写/错误异常。使用IO多路复用可以使用以下接口。

select poll epoll

select多路复用的实现

1.准备要监控的描述符
    ...
2.将要监控的描述符放入对应的描述符集合(fd_set)
    void FD_CLR(int fd, fd_set *set);//从描述符集合中删除指定的描述符
    int  FD_ISSET(int fd, fd_set *set);//判断描述符集合中是否有该描述符
    void FD_SET(int fd, fd_set *set);//从描述符集合中添加指定的描述符
    void FD_ZERO(fd_set *set);//情况描述符集合
    
3.调用select函数监控对应的描述符集合
#include <sys/select.h>
/* According to earlier standards */
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int select(int nfds, fd_set *readfds, fd_set *writefds,
          fd_set *exceptfds, struct timeval *timeout);
参数:
    nfds - 监控的最大的描述符的值+1
    readfds - 传入传出参数,传入要监控的读描述符集合,传出活动的读描述符集合(是否可读)
    writefds - 传入传出参数,传入要监控的写描述符集合,传出活动的写描述符集合(是否可写)
    exceptfds  - 传入传出参数,传入要监控的异常描述符集合,传出活动的异常描述符集合(是否出错)
    timeout - 超时时间,不使用使用传NULL
返回值:
    成功返回活动的描述符个数,超时返回0,出错返回-1

4.处理活动的描述符                

网络超时

在工程开发中一般不设置无限等待,需要考虑到对方无响应的情况,将无限等待改为有限时间等待,网络超时就是等待有限的时间,如果有限时间内对方无响应就不再等待。

(1)多路复用的接口中自带超时处理(select poll epoll)

struct timeval tv;
tv.tv_sec = 3;//3s超时
tv.tv_usec = 0;//us
//select
if(select(maxfd+1, &set, NULL, NULL, &tv)<=0){
  printf("timeout!\n");
}

(2)通过socket属性来设置超时(read,recv,recvfrom,accept)

struct timeval tv;
tv.tv_sec = xxx;//s
tv.tv_usec = yyy;//us
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));

(3)心跳检测

服务器和客户端在一定的时间间隔内(1s)发送一个代表保持连接的数据,表示客户端在线,如果能收到这个包,表示连接有效。

http通信

http属于应用层的协议,同时方式通过请求,请求分为两类 --------- GET/POST

(1)http请求帧格式

请求行(request line)
    请求方法(GET) URL http协版本(HTTP/1.0) 回车换行
消息头部(header)
    key:value 回车换行(\r\n)
    .....
    空行  回车换行(\r\n)
请求正文
    ......       

(2)使用C语言编程访问http的API接口

1.解析域名,得到IP
    gethostbyname
2.创建socket连接API的IP和端口
    socket
    以API服务器的IP和端口构造地址
    connect
3.构造http请求
    按照http协议的要求构造http请求
4.发送http请求
    send
5.读取SPI服务器返回的数据
    recv                

设置地址重用

//在bind前调用
int val = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));

获取Linux本机IP

(1)使用getiifaddrs函数

该函数可以获取当前Linux系统中所有网络硬件的信息,调用该函数会传出一个ifaddrs的链表,链表中每一个节点就代表系统的一个网络硬件,其中有IP信息。

image-20220721234050984

image-20220721234055334

参考代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <ifaddrs.h>
#include <sys/ioctl.h>
#include <net/if.h>

struct ifaddrs *ifAddrStruct = NULL;
struct ifaddrs *ifa = NULL;
void *tmpAddrPtr = NULL;   

//获取网络硬件信息
int res = getifaddrs(&ifAddrStruct);
if(res==-1){
    perror("getifaddrs");
    exit(-1);
}

//遍历网络硬件信息
for(ifa=ifAddrStruct; ifa!=NULL; ifa=ifa->ifa_next){
    //IPV4信息
    if(ifa->ifa_addr->sa_family==AF_INET){
        char buf[INET_ADDRSTRLEN] = {};
        tmpAddrPtr = &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
        inet_ntop(AF_INET,  tmpAddrPtr, buf, INET_ADDRSTRLEN);
        printf("%s ip address %s\n", ifa->ifa_name, buf);
    }
}

freeifaddrs(ifAddrStruct);
return 0;

(2)可以使用ioctl去获取套接字的ip信息

先创建一个套接字(socket函数),调用ioctl获取网络硬件信息。

参考代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <ifaddrs.h>
#include <sys/ioctl.h>
#include <net/if.h>    
    
   struct ifreq ifr[32];
	struct ifconf ifc;
	
	int sockfd = socket(AF_INET,SOCK_DGRAM,0);
	if(sockfd==-1){
		perror("socket");
		exit(-1);
	}
	
	ifc.ifc_len = sizeof(ifr);
	ifc.ifc_buf = (caddr_t)ifr;
	
    //获取系统网络硬件信息
	int res = ioctl(sockfd, SIOCGIFCONF, (char *)&ifc);
	if(res==-1){
		perror("ioctl");
		exit(-1);
	}
	
	int interface = ifc.ifc_len/sizeof(struct ifreq);
	while(interface--){
           //获取网络硬件信息中的地址参数
		res = ioctl(sockfd, SIOCGIFADDR, (char *)&ifr[interface]);
		if(res==-1){
			perror("ioctl");
			exit(-1);
		}
		
		printf("ip:%s\n",inet_ntoa(((struct sockaddr_in *)(&ifr[interface].ifr_addr))->sin_addr));
	}
	
	return 0;