关于c/c++ 网络编程,无论在linux还是windows,要说到自由性,和安全性,socket无疑是比较好的!对于socket,因为它的传输协议只有两种tcp和udp,属于网络层,这里我们不去重点讨论。

       关于应用层协议http,如何用C/C++的socket来实现数据传输和下载呢?

1. http是超文本协议,用在html文件中,那么对于html是如何传输数据呢?

    通过post或者get传输表单数据,当然http还有其他的方式head,put ,delete,option,trace等方式。head和get差不多,唯一的区别就是head只返回协议头,put和post也很相似,但是可惜html表单数据不支持这一特性,put和post的区别在于,put说出来资源放置于服务器的位置,而post没有,post将这项权利给予服务器来使用。delete顾名思义,就是指定删除在服务器上的资源,option一般用来获取当前URl所支持请求的方法(就是上诉的六种)。

对于c/c++传输单数据,get方法:

get方法,  形如: http://i.cnblogs.com/EditPosts.aspx?opt=1

这个表单传输的数据就是1,其中键值就是opt,这个需要和服务器上的保持一致

对于一个简单的html

 <html>
<head><title>右边</tile></head>
<body>
<form >
<input type="text", name="opt" > </input>
</form>
</body>
</html>

opt就是键值

那么用socket如何实现:

首先,windows下,我们

1. 先要启动异步套接字启动命令

//初始化套结字动态库
if (WSAStartup(MAKEWORD(, ), &wsd) != ) //异步套接字启动命令
/版本(次,主) //返回socket实现细节信息
{
system("WSAStartup failed!\n");
system("pause");
return -;
}

2.在想linux下一样,创建套接字

  sHost = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

3.绑定端口号,和设置要访问的服务器主机地址

      //设置服务器地址

      servAddr.sin_family = AF_INET;
servAddr.sin_addr.s_addr = inet_addr("203.195.192.24");
servAddr.sin_port = htons((short));

4.连接服务器

retVal = connect(sHost, (LPSOCKADDR)&servAddr, sizeof(servAddr));

5.然后接收信息字段

 char *pHttpGet = "GET %s?%s HTTP/1.1\r\n"
"Host: %s:%d\r\n\r\n";
char strHttpGet[] = { };
//ZeroMemory(strHttpGet, BUF_SZIE); //初始化内存 char msg[]="username=Gjxun&pwd=sssssss";
sprintf(strHttpGet, pHttpGet, addr, msg, host, port);
int var = send(sHost, strHttpGet, strlen(strHttpGet), );
    recv(sHost,rebuf ,sizeof(rebuf),0);

最后关闭的时候。需要用这个来关闭异步套接字

 WSACleanup( );

这是http的基本流程,对于get发送单个或者多个表单数据如上面所示

对于post而言,情况 会多些,也会复杂些

1.如果发送的是单个或者多个字段信息,那么我们的处理方式大致可以有下面这两种

第一种: 就像get一样,只不过单纯的将数据放置于协议的后面,需要注意点的是,格式比较重要,特别协议头和正文部分之间需要各一个空行:

下面的msg亦可以和get一样写成 msg="username=Gxjun&pwd=ssssss"; 还有content-Length的长度: 是正文和正文数据以及尾部长度之后不需要算协议头长度,不然会,当将连接改为Connection: Keep-Alive 出现服务器长时间接受现象。---指导服务器接受到结尾帧或者数据长度达到那个长度为止,才会响应刚才的动作!!!!

 void sendPost1(char* addr, char * host, char *msg, int port) {
char *pHttpPost = "POST %s HTTP/1.1\r\n"
"Host: %s:%d\r\n"
"Content-Type: application/x-www-form-urlencoded\r\n"
"Content-Length: %d\r\n\r\n"
"%s"; char strHttpPost[] = { };
//ZeroMemory(strHttpGet, BUF_SZIE); //初始化内存
sprintf(strHttpPost, pHttpPost, addr, host, port, strlen(msg), msg);
int var = send(sHost, strHttpPost, strlen(strHttpPost), );
if (var < ) {
MessageBoxA(NULL, "请求发送失败!", , );
return;
}
}

另一种方式:多种数据表单的形式:协议头部分,将Content-Type: multipart/form-data; 同时还需要加上一个分割标识,即boundary = Gxjunnndgx ,

整体上就是设置为 Content-Type: multipart/form-data; boundary=71b23e4066ed\r\n";

其他部分参开rfc2038部分。

所以对于单个或者多个字段表单而言:

比如: 需要像如下的html文件一样将username和pwd的键值数据发送给服务器数据数据:

<html>
<head></head>
<body>
<form action="xxx.xxx.xxxx" method="post">
<input type="text" name="username">Gxjun</input>
<input type="password" name="pwd">ssssss</input>
<form>
</body>
</html>
 void sendPost(char* addr, char * host,string username,string psw, int port){

         std::string header("");
std::string u_content(""); //用户名
std::string p_content(""); //密码 //----------------------post头开始--------------------------------
header += "POST ";
header += addr;
header += " HTTP/1.1\r\n";
header += "Host: ";
header += host;
header += "\r\n";
header += "Connection: Keep-Alive\r\n";
header += "Accept: */*\r\n";
header += "Pragma: no-cache\r\n";
header += "Content-Type: multipart/form-data; boundary=71gxjun\r\n"; //用户名数据表单
u_content += "--71gxjun\r\n";
u_content += "Content-Disposition: form-data; name=\"u\"\r\n\r\n";
u_content += username+"\r\n"; //密码数据表单
p_content += "--71gxjun\r\n";
p_content += "Content-Disposition: form-data; name=\"p\"\r\n\r\n";
p_content += psw+"\r\n";
//post尾时间戳
std::string strContent("--71gxjun--\r\n\r\n");
char temp[] = { };
//注意下面这个参数Content-Length,这个参数值是:http请求头长度+请求尾长度+文件总长度
// 就分块传送
sprintf(temp, "Content-Length: %d\r\n\r\n",
p_content.length()+u_content.length() + strContent.length());
header += temp;
std::string str_http_request;
str_http_request.append(header); //----------------------post头结束-----------------------------------
//发送post头
send(sHost, str_http_request.c_str(), str_http_request.length(), );
Sleep(0.2);
send(sHost, p_content.c_str(), p_content.length(), );
Sleep(0.2);
send(sHost, u_content.c_str(), u_content.length(), );
Sleep(0.2);
::send(sHost, strContent.c_str(), strContent.length(), );
Sleep(0.2);
}

对于boundary=abcdegxjun  这部分的数据可以随意定义,但不要太简单,不然可能会和数据混淆,上面是两个字段的发送,所以需要两部分的正文加正文数据,对于尾部的结束标识,前面需要“--”两个横短线后面也需要两个横短线“--”,对于中间的分割标志,只需要前面有“--”就可以了!  还需要注意的是数据发送完之后,需要换行,然后再接上分割标识。

4.然后对于文件和照片的传输    ---在linux下,一切接文件,在window下我们也可以将照片看做二进制文件处理

其实文件的传输,都可以作为二进制文件来传输,我们可以将文件

     char * ReadFile(char *pathpic, int &pic_len){
//将图片读取出来
FILE *fp = fopen(pathpic, "rb"); //打开文件
if (!fp){
MessageBoxA(NULL, "没有找到文件位置", , );
return NULL;
}
fseek(fp, , SEEK_END); //一直寻找到文件尾部
pic_len = ftell(fp); //得到图片的长度
rewind(fp); //rewind将文件指针指向开头
char *pic_buf = new char[pic_len + ]; //开辟一个空间在堆上
memset(pic_buf, , pic_len + ); //清空文件指针
//读取文件内容
fread(pic_buf,sizeof(char),pic_len,fp);
//测试将文件再保存于D:中
/*
MessageBoxA(NULL, "文件开始", 0, 0);
FILE *fpw = fopen("C:\\AA.jpg","wb");
fwrite(pic_buf,sizeof(char), pic_len, fpw);
fclose(fpw); //关闭文件流
MessageBoxA(NULL, "文件结束", 0, 0);
*/
fclose(fp); return pic_buf;
}

对于不同的类型,需要修改不同的Content-Type 比如图片jpg,jpeg等就是需要这种 ,"Content-Type: image/jpeg,对于其他的的类型,不妨去这儿找找,比较详细

http://tool.oschina.net/commons

然后下面是一个关于多个字段和多个照片,运用一个form表单,通过一次post,将数据上传到服务器上!  注: 这里是在c\s模式, 客户端是c++ ,服务器是php

代码如下:

 char * ReadFile(char *pathpic, int &pic_len){
//将图片读取出来
FILE *fp = fopen(pathpic, "rb"); //打开文件
if (!fp){
MessageBoxA(NULL, "没有找到文件位置", , );
return NULL;
}
fseek(fp, , SEEK_END); //一直寻找到文件尾部
pic_len = ftell(fp); //得到图片的长度
rewind(fp); //rewind将文件指针指向开头
char *pic_buf = new char[pic_len + ]; //开辟一个空间在堆上
memset(pic_buf, , pic_len + ); //清空文件指针
//读取文件内容
fread(pic_buf,sizeof(char),pic_len,fp);
//测试将文件再保存于D:中
/*
MessageBoxA(NULL, "文件开始", 0, 0);
FILE *fpw = fopen("C:\\AA.jpg","wb");
fwrite(pic_buf,sizeof(char), pic_len, fpw);
fclose(fpw); //关闭文件流
MessageBoxA(NULL, "文件结束", 0, 0);
*/
fclose(fp); return pic_buf;
} void sendPic(char* addr, char * host, char *pathpic, char* picname, int port, string username, string psw) { //先读取文件流
//实名图片读取,等级图片读取
int Spic_len, Dpic_len;
char *Spic_data=NULL, *Dpic_data=NULL; Spic_data=ReadFile(pathpic, Spic_len);
Dpic_data = ReadFile(picname, Dpic_len);
std::string header("");
std::string content(""); //实名文件
std::string nex_content(""); //等级文件
std::string u_content(""); //用户名
std::string p_content(""); //密码 //----------------------post头开始--------------------------------
header += "POST ";
header += addr;
header += " HTTP/1.1\r\n";
header += "Host: ";
header += host;
header += "\r\n";
header += "Connection: Keep-Alive\r\n";
header += "Accept: */*\r\n";
header += "Pragma: no-cache\r\n";
header += "Content-Type: multipart/form-data;boundary=71b23e4066ed\r\n"; //用户名数据表单
u_content += "--71b23e4066ed\r\n";
u_content += "Content-Disposition: form-data; name=\"u\"\r\n\r\n";
u_content += username+"\r\n"; //密码数据表单
p_content += "--71b23e4066ed\r\n";
p_content += "Content-Disposition: form-data; name=\"p\"\r\n\r\n";
p_content += psw+"\r\n"; //发送文件数据
content += "--71b23e4066ed\r\n";
content += "Content-Disposition: form-data; name=\"picurl\"; filename=\"";
content += pathpic;
content += "\"\r\n";
content += "Content-Type: image/jpeg \r\n\r\n"; //发送文件数据
nex_content += "\r\n--71b23e4066ed\r\n";
nex_content += "Content-Disposition: form-data; name=\"id_account\"; filename=\"";
nex_content += picname; //picname;
nex_content += "\"\r\n";
nex_content += "Content-Type: image/jpeg\r\n\r\n"; //post尾时间戳
std::string strContent("\r\n--71b23e4066ed--\r\n");
char temp[] = { };
//注意下面这个参数Content-Length,这个参数值是:http请求头长度+请求尾长度+文件总长度
// 就分块传送
sprintf(temp, "Content-Length: %d\r\n\r\n",
content.length() + nex_content.length() +p_content.length()+u_content.length() + Spic_len + Dpic_len + strContent.length());
header += temp;
std::string str_http_request;
str_http_request.append(header); //----------------------post头结束-----------------------------------
//发送post头
send(sHost, str_http_request.c_str(), str_http_request.length(), );
char fBuff[1024];
int buffsize = ; // 每个数据包存放文件的buffer大小
int nStart;//记录post初始位置
int nSize;//记录剩余文件大小
Sleep(0.2);
//发送用户名表单
send(sHost, u_content.c_str(), u_content.length(), );
Sleep(0.2);
//发送密码表单
send(sHost, p_content.c_str(), p_content.length(), );
Sleep(0.2);
//发送尾部
//发送格式
send(sHost, content.c_str(), content.length(), );
Sleep(0.2);
send(sHost, Spic_data, Spic_len, );
Sleep(0.2);
//发送等级图片数据
send(sHost, nex_content.c_str(), nex_content.length(), );
Sleep(0.2);
send(sHost, Dpic_data, Dpic_len, );
Sleep(0.2);
//如果数据是在够大,需要作调整,可以使用如下的方式,切割文件发送数据
/*
for (int i = 0; i < Spic_len; i += bufsize)
{
nStart = i;
if (i + bufsize + 1> Spic_len){
nSize = Spic_len - i;
}
else{
nSize = bufsize;
} memcpy(fBuff, Spic_data + nStart, nSize);
::send(sHost, fBuff, nSize, 0);
Sleep(0.2); //防止毡包
} //发送等级图片数据
::send(sHost, nex_content.c_str(), nex_content.length(), 0);
Sleep(0.2);
bufsize = 4096;
for (int i = 0; i < Dpic_len; i += bufsize)
{
nStart = i;
if (i + bufsize + 1> Dpic_len){
nSize = Dpic_len - i;
}
else{
nSize = bufsize;
} memcpy(fBuff, Dpic_data + nStart, nSize);
::send(sHost, fBuff, nSize, 0);
Sleep(0.2); //防止毡包
}
*/
/*
for (int i = 0; i < Dpic_len; i += nPacketBufferSize)
{
nStart = i;
if (i + nPacketBufferSize + 1> Dpic_len){
nSize = Dpic_len - i;
}
else{
nSize = nPacketBufferSize;
} memcpy(fBuff, Dpic_data + nStart, nSize);
::send(sHost, fBuff, nSize, 0);
Sleep(0.2); //防止毡包
}*/ send(sHost, strContent.c_str(), strContent.length(), );
Sleep(0.2); if (Spic_data == NULL)
{
MessageBox(NULL, L"文件数据为空", , );
}
//释放内存
delete Spic_data;
delete Dpic_data; }

当这些基本做好了之后,就需要看返回的结果:

对于返回http返回结果协议头的简单解析: 如果需要深入研究去看 rfc2616,这里就简单的罗列一些100-500的简单的含义吧

100-199 用于指定客户端应相应的某些动作。 
  200-299 用于表示请求成功。 
      300-399 用于已经移动的文件并且常被包含在定位头信息中指定新的地址信息。 
      400-499 用于指出客户端的错误。 
      500-599 用于支持服务器错误。

详细的文档,可以看看这个在线文档,http://tool.oschina.net/commons?type=5

学习的过程中参考过几位博主,此处表达谢意,终于对http在以前认知的基础上,再次的又重新的知识了一番!! 记录些这些,希望对以后学习的人,能够提供一点点帮助!!!

C/C++-----------http协议发送字段,文件,单个和多张图片的更多相关文章

  1. TCP和UDP 协议发送数据包的大小

    在进行UDP编程的时候,我们最容易想到的问题就是,一次发送多少bytes好? 当然,这个没有唯一答案,相对于不同的系统,不同的要求,其得到的答案是不一样的,这里仅对像ICQ一类的发送聊天消息的情况作分 ...

  2. http 协议上传文件multipart form-data boundary 说明--转载

    原文地址:http://xixinfei.iteye.com/blog/2002017 含义 ENCTYPE="multipart/form-data" 说明: 通过 http 协 ...

  3. python使用简单http协议来传送文件

    python使用简单http协议来传送文件!在ubuntu环境下,局域网内可以使用nc来传送文件,也可以使用基于Http协议的方式来下载文件我们可以使用python -m SimpleHTTPServ ...

  4. C++实现RTMP协议发送H.264编码及AAC编码的音视频

    http://www.cnblogs.com/haibindev/archive/2011/12/29/2305712.html C++实现RTMP协议发送H.264编码及AAC编码的音视频 RTMP ...

  5. C++实现RTMP协议发送H.264编码及AAC编码的音视频(转)

    C++实现RTMP协议发送H.264编码及AAC编码的音视频(转) RTMP(Real Time Messaging Protocol)是专门用来传输音视频数据的流媒体协议,最初由Macromedia ...

  6. Android用http协议上传文件

    http协议上传文件一般最大是2M,比较适合上传小于两M的文件   [代码] [Java]代码   001import java.io.File;  002import java.io.FileInp ...

  7. TCP协议传输大文件读取时候的问题

    TCP协议传输大文件读取时候的问题 大文件传不完的bug 我们在定义的时候定义服务端每次文件读取大小为10240, 客户端每次接受大小为10240 我们想当然的认为客户端每次读取大小就是10240而把 ...

  8. (转)C++实现RTMP协议发送H.264编码及AAC编码的音视频,摄像头直播

    转:http://www.cnblogs.com/haibindev/archive/2011/12/29/2305712.html C++实现RTMP协议发送H.264编码及AAC编码的音视频 RT ...

  9. java-TCP协议发送和接收数据

    TCP协议接收数据的步骤: A:创建接收数据的Socket对象 创建对象的时候要指定端口 B:监听客户端连接 等待客户端连接 C:获取Socket对象的输入流(字节流) D:读数据,并显示在控制台 E ...

随机推荐

  1. 前端开发利器-Brackets IDE

    是什么? http://brackets.io/ A modern, open source text editor that understands web design. 现代, 开源的文本编辑器 ...

  2. dubbo源码分析2-reference bean发起服务方法调用

    dubbo源码分析1-reference bean创建 dubbo源码分析2-reference bean发起服务方法调用 dubbo源码分析3-service bean的创建与发布 dubbo源码分 ...

  3. EXCEL导入导出自己整理的一些方法

    //导入Excel代码 protected DataTable ExcelHelper(string filePaht) { string sFilePath2003 = Server.MapPath ...

  4. Linux 下修改配置实现在当前目录下寻找可执行文件

    # vim .bash_profile 添加 export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:. # vreboot

  5. Python 时间和日期模块的常用例子

    获取当前时间的两种方法 import datetime,time now = time.strftime("%Y-%m-%d %H:%M:%S") print now now = ...

  6. Dynamics AX 2012 R2 配置报表服务器

    今天Reinhard在使用报表的过程中,发现以下错误: The default Report Server Configuration ID could not be found in the SRS ...

  7. Dynamics AX 2012 R2 堆栈跟踪:不能对客户端调用'unchecked'

    有一个Custom Service一直在正常使用.今天,Reinhard尝试在JOB中以X++代码Debug Custom Service的Method时,收到以下错误提示: 'unchecked' ...

  8. Codeforces 749C:Voting(暴力模拟)

    http://codeforces.com/problemset/problem/749/C 题意:有n个人投票,分为 D 和 R 两派,从1~n的顺序投票,轮到某人投票的时候,他可以将对方的一个人K ...

  9. Knights of the Round Table-POJ2942(双连通分量+交叉染色)

    Knights of the Round Table Description Being a knight is a very attractive career: searching for the ...

  10. elasticsearch安装与基础用法

    来自官网,版本为2.3 注意elasticsearch依赖jdk,2.3依赖jdk7 下载rpm包并安装 wget -c https://download.elastic.co/elasticsear ...