http传输的核心还是基于tcp/ip协议传输,服务器端启动,客户端通过指定的地址以及端口寻找服务器,然后进行连接;在windows下我们主要还是通过socket套接字建立连接:
/*服务器端*/
WSAStartup(MAKEWORD(2, 0), &wsa_data); /* 初始化 WinSock 资源 */
srv_soc = socket(AF_INET, SOCK_STREAM, 0); /* 创建 socket */
if (srv_soc == INVALID_SOCKET)
{
printf("[Web] socket() Fails, error = %d\n", WSAGetLastError());
return -1;
}
/* 服务器地址 */
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(port);
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
result = bind(srv_soc, (struct sockaddr *) &serv_addr, sizeof(serv_addr));
if (result == SOCKET_ERROR) /* 绑定失败 */
{
closesocket(srv_soc);
printf("[Web] Fail to bind, error = %d\n", WSAGetLastError());
return -1;
}
result = listen(srv_soc, SOMAXCONN);
printf("[Web] The server is running ... ...\n");
WSAStartup(MAKEWORD(2, 0), &wsa_data); /* 初始化 WinSock 资源 */
addr = inet_addr(host);
if (addr == INADDR_NONE)
{
host_ent = gethostbyname(host);
if (!host_ent)
{
printf("[Web] invalid host\n");
return -1;
}
memcpy(&addr, host_ent->h_addr_list[0], host_ent->h_length);
}
/* 客户端 */
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(port);
serv_addr.sin_addr.s_addr = addr;
http_sock = socket(AF_INET, SOCK_STREAM, 0); /* 创建 socket */
result = connect(http_sock, (struct sockaddr *) &serv_addr, sizeof(serv_addr));
if (result == SOCKET_ERROR) /* 连接失败 */
{
closesocket(http_sock);
printf("[Web] fail to connect, error = %d\n", WSAGetLastError());
return -1;
}
紧接着client要发送http消息头让服务器处理:
/* 发送 HTTP 请求 */
send_len = sprintf(cmd_buf, http_req_hdr_tmpl, argv[1], host, port, flag);
result = send(http_sock, cmd_buf, send_len, 0);
if (result == SOCKET_ERROR) /* 发送失败 */
{
printf("[Web] fail to send, error = %d\n", WSAGetLastError());
return -1;
}
可以看到,我们使用了send()函数,其中cmd_buf是消息头内容。此时服务器接收对应信息:
acpt_soc = accept(srv_soc, (struct sockaddr *) &from_addr, &from_len);
if (acpt_soc == INVALID_SOCKET) /* 接受失败 */
{
printf("[Web] Fail to accept, error = %d\n", WSAGetLastError());
break;
}
printf("[Web] Accepted address:[%s], port:[%d]\n",
inet_ntoa(from_addr.sin_addr), ntohs(from_addr.sin_port));
recv_len = recv(acpt_soc, cmd_buf, HTTP_BUF_SIZE, 0);
if (recv_len == SOCKET_ERROR) /* 接收失败 */
{
closesocket(acpt_soc);
printf("[Web] Fail to recv, error = %d\n", WSAGetLastError());
break;
}
我们用accept()函数接受连接建立,然后recv()函数接收客户端的内容。服务器端接收到控制指令后打开对应的文件并读取,回传:
res_file = fopen(filename, "rb+"); /* 用二进制格式打开文件 */
if (res_file == NULL)
{
printf("[Web] The file [%s] is not existed\n", filename);
return 0;
}
fseek(res_file, 0, SEEK_END);
file_len = ftell(res_file);
fseek(res_file, 0, SEEK_SET);
type = http_get_type_by_suffix(suffix); /* 文件对应的 Content-Type */
if (type == NULL)
{
printf("[Web] There is not the related content type\n");
return 0;
}
do /* 发送文件, HTTP 的消息体 */
{
read_len = fread(data_buf, sizeof(char), HTTP_BUF_SIZE, res_file);
if (read_len > 0)
{
send_len = send(soc, data_buf, read_len, 0);
file_len -= read_len;
}
} while ((read_len > 0) && (file_len > 0));
fclose(res_file);
return 1;
以此类推,上传文件基本操作也只是逆操作。
到这里,一个简易的上传下载文件项目已经完成;在这里应该注意的是:定义的消息体变量buf应设为外部变量,若只是局部变量,当索要内存过大时,压栈会stack overflow,而外部变量主要压入堆里。