出于某些原因和教育目的,我从头开始开发了一个 C++ HTTP 服务器,它应该使用处理动态请求的 AJAX 从 HTML 页面捕获文件上传,并将其保存在服务器程序所在的同一目录中。
服务器对于 .txt 文件运行良好。文件上传并保存在服务器目录中,没有任何缺陷。一切仍然完美,但当我尝试上传可执行文件、视频文件、音乐文件等二进制文件时,问题就开始了。基本上,服务器可以正常处理 .txt 文件,而不是其他文件。每当上传二进制文件时,服务器端的输出文件都会以某种方式损坏。
我知道有用于这些操作的 PHP 和 NodeJS 框架,但实际上我将在裸机控制器服务器上部署这个项目,该服务器的资源非常有限,无法承受复杂的框架。所以使用scratch的C++服务器似乎是实现我的目标的唯一方法。尽管我无法直接将此代码移植到控制器中,但过程保持不变。要第一手了解该过程,我必须使用 Windows Sockets 在 Windows 上进行一些实验。
这是我迄今为止尝试过的,
C++ HTTP 服务器代码。
#include <stdio.h>
#include <math.h>
#include <cmath>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <winsock2.h>
#include <windows.h>
#define PORT 12345
WSADATA ws;
char Command[8888];
char FILEDATA[(2 * 1024)]; // FILE HEADER and BUFFER
SOCKET soc, acpt;
FILE *f ;
unsigned long long FileSize(char * direc){
unsigned long long size = 0;
FILE *fsize = fopen(direc,"rb");
fseek(fsize,0,SEEK_END);
size = _ftelli64(fsize);
fseek(fsize,0,SEEK_SET);
fclose(fsize);
return size;
}
void Serve(){
int optval = 1;
int RecvBytes=0;
sockaddr_in addr, peerAddr;
struct timeval RequestTimeout = {0,5000000} , DownloadTimeout= {0,500000};
char URL[4000]={0};
char FileName[260]={0};
addr.sin_addr.s_addr = inet_addr("0.0.0.0");
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT);
fd_set rfd;
FD_ZERO(&rfd);
soc = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
setsockopt(soc,SOL_SOCKET,SO_REUSEADDR,(const char*)&optval,sizeof(optval));
bind(soc,(sockaddr*)&addr,sizeof(addr));
listen(soc,100);
acpt = accept(soc,NULL,NULL);
FD_SET(acpt,&rfd);
int connstat = select(acpt+1,&rfd,NULL,NULL,&RequestTimeout); // act only if a stable reading can be performed
if(connstat){
int x = 0;
RecvBytes = recv(acpt,Command,sizeof(Command),0); // Receives HTTP header
RecvBytes = recv(acpt,FILEDATA,sizeof(FILEDATA),0); // Receives File headers and content
char * namePtr = strstr(FILEDATA,"\r\n\r\n");
namePtr = strstr(FILEDATA,"filename=\"") + strlen("filename "); // Extraction of File Name begins here
while(namePtr[x]!='"'){
x++;
}
strncpy(FileName,namePtr,x); // Extraction of file name ends here.
printf("NAME: %s\n\n",FileName);
int NumberOfBytes = (&strstr(FILEDATA,"\r\n\r\n")[0]+2 - &FILEDATA[0])+2;
f = fopen(FileName,"wb"); // Open file for writing
fwrite(strstr(FILEDATA,"\r\n\r\n")+3,1,RecvBytes - NumberOfBytes ,f); // Received Number of Bytes - Number of Bytes Till Header
while(select(acpt+1,&rfd,NULL,NULL,&DownloadTimeout)){ // Keep reading if more data remains available
RecvBytes = recv(acpt,FILEDATA,sizeof(FILEDATA),0); // receive
if(RecvBytes==0){
break;
}
fwrite(FILEDATA,1,RecvBytes,f);
}
}
fclose(f);
unsigned long long truncFile = FileSize(FileName) - 45;
f = fopen(FileName,"r+"); // truncating file to remove the webkit boundary substring at the end of the file.
ftruncate(fileno(f),truncFile);
fclose(f);
printf("OUTPUT FILE SIZE: %llu\n\n",FileSize(FileName));
MessageBeep(MB_ICONERROR); // A beep means operation is complete
shutdown(acpt,2);
closesocket(acpt);
closesocket(soc);
shutdown(soc,2);
acpt = NULL;
soc = NULL;
memset(FILEDATA,'\0',sizeof(FILEDATA));
}
int main(){
//printf("%d\n",strlen("------WebKitFormBoundary5BlPHcjSKYuZFzV3--")); 42
WSAStartup(MAKEWORD(2,2),&ws);
while(1){
Serve();
}
return 0;
}
现在用于上传的AJAX HTML页面,
<!DOCTYPE html>
<html>
<body>
The content of the body element is displayed in your browser.
<input type="file" id="image-file">
<button onclick="Send()">Send</button>
<script>
function Send(){
let photo = document.getElementById("image-file").files[0]; // file from input
let req = new XMLHttpRequest();
let formData = new FormData();
formData.append("photo", photo);
req.open("POST", 'http://localhost:12345/');
req.send(formData);
}
</script>
</body>
</html>
我想有一个最快的解决方案来解决这个问题。感谢您理解我的担忧,问候...
经过一番努力,这些代码终于完美运行了!
C HTTP 服务器下载器
#include <stdio.h>
#include <math.h>
#include <cmath>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <winsock2.h>
#include <windows.h>
#define PORT 12345
WSADATA ws;
char Command[8888];
char FILEDATA[(16 * 1024 * 1024)]; // FILE HEADER and BUFFER
SOCKET soc, acpt;
FILE *f ;
unsigned long long FileSize(char * direc){
unsigned long long size = 0;
FILE *fsize = fopen(direc,"rb");
fseek(fsize,0,SEEK_END);
size = _ftelli64(fsize);
fseek(fsize,0,SEEK_SET);
fclose(fsize);
return size;
}
void Serve(){
int optval = 1;
int RecvBytes=0;
sockaddr_in addr, peerAddr;
struct timeval RequestTimeout = {0,5000000} , DownloadTimeout= {0,500000};
char URL[4000]={0};
char FileName[260]={0};
addr.sin_addr.s_addr = inet_addr("0.0.0.0");
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT);
fd_set rfd;
FD_ZERO(&rfd);
soc = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
setsockopt(soc,SOL_SOCKET,SO_REUSEADDR,(const char*)&optval,sizeof(optval));
bind(soc,(sockaddr*)&addr,sizeof(addr));
listen(soc,100);
acpt = accept(soc,NULL,NULL);
FD_SET(acpt,&rfd);
int connstat = select(acpt+1,&rfd,NULL,NULL,&RequestTimeout);
if(connstat){
int x = 0;
RecvBytes = recv(acpt,Command,sizeof(Command),0); // Receives HTTP header
RecvBytes = recv(acpt,FILEDATA,sizeof(FILEDATA),0); // Receives File headers and content
char * namePtr = strstr(FILEDATA,"\r\n\r\n");
namePtr = strstr(FILEDATA,"filename=\"") + strlen("filename "); // Extraction of File Name begins here
while(namePtr[x]!='"'){
x++;
}
strncpy(FileName,namePtr,x); // Extraction of file name ends here.
printf("NAME: %s\n\n",FileName);
int NumberOfBytes = (&strstr(FILEDATA,"\r\n\r\n")[0]+2 - &FILEDATA[0])+2;
f = fopen(FileName,"wb"); // Open file for writing
printf("CONTENTS: %d ", &strstr(FILEDATA,"\r\n\r\n")[0]+2 - &FILEDATA[0]);
fwrite(strstr(FILEDATA,"\r\n\r\n")+4,1,RecvBytes - NumberOfBytes ,f); // Received Number of Bytes - Number of Bytes Till Header
while(select(acpt+1,&rfd,NULL,NULL,&DownloadTimeout)){ // Keep reading if more data remains available
RecvBytes = recv(acpt,FILEDATA,sizeof(FILEDATA),0); // receive
if(RecvBytes==0){
break;
}
fwrite(FILEDATA,1,RecvBytes,f);
}
}
fclose(f);
unsigned long long truncFile = FileSize(FileName) - 45;
f = fopen(FileName,"r+b"); // truncating file to remove the webkit boundary
ftruncate(fileno(f),truncFile);
fclose(f);
printf("OUTPUT FILE SIZE: %llu\n\n",FileSize(FileName));
MessageBeep(MB_ICONERROR); // A beep means operation is complete
shutdown(acpt,2);
closesocket(acpt);
closesocket(soc);
shutdown(soc,2);
acpt = NULL;
soc = NULL;
memset(FILEDATA,'\0',sizeof(FILEDATA));
}
int main(){
//printf("%d\n",strlen("------WebKitFormBoundary5BlPHcjSKYuZFzV3--")); 42
WSAStartup(MAKEWORD(2,2),&ws);
while(1){
Serve();
}
return 0;
}
使用 AJAX 上传器的 HTML
<!DOCTYPE html>
<html>
<body>
The content of the body element is displayed in your browser.
<input type="file" id="image-file">
<button onclick="Send()">Send</button>
<script>
function Send(){
let photo = document.getElementById("image-file").files[0]; // file from input
let req = new XMLHttpRequest();
let formData = new FormData();
formData.append("photo", photo);
req.open("POST", 'http://localhost:12345/');
req.send(formData);
}
</script>
</body>
</html>
上面的 C HTTP 服务器代码完美地处理并下载上传请求到服务器保存的目录中,而不会损坏文件。