修改
问题:2 和问题:3 通过跟随@melpomene注释解决,即使用读取的字节数来打印缓冲区。
但仍然遇到了问题:1 。
我编写了一个TCP服务器客户端程序。后来出于好奇,我想知道HTTP服务器。
我之前的问题:Simple TCP server can't output to web browser
现在,我正在使用GET
和POST
(form-data
和x-www-form-urlencoded
暂时查看数据传输到服务器的内容和方式。
我正在关注How to cURL POST from the Command Line发送POST
次请求。
当我发送x-www-form-urlencoded
时:
curl -d "data=example1&data2=example2" localhost:8080
服务器上的输出:
POST / HTTP/1.1
Host: localhost:8080
User-Agent: curl/7.54.0
Accept: */*
Content-Length: 28
Content-Type: application/x-www-form-urlencoded
data=example1&data2=example2
这是预期的。
问题:1
现在出现了问题。当我尝试发送form-data
时,不会产生输出。
当我发送form-data
时:
curl -X POST -F "name=user" -F "password=test" localhost:8080
服务器上的输出:
POST / HTTP/1.1
Host: localhost:8080
User-Agent: curl/7.54.0
Accept: */*
Content-Length: 244
Expect: 100-continue
Content-Type: multipart/form-data; boundary=------------------------78b7f8917ad1992c
我正在接受边界,但我没有得到下一部分,就像我发送的数据一样。
问题:2
另一个奇怪的事情是我在发送x-www-form-urlencoded
后尝试发送form-data
。
当我在x-www-form-urlencoded
之后发送form-data
时:
curl -d "data=example1&data2=example2" localhost:8080
服务器上的输出:
POST / HTTP/1.1
Host: localhost:8080
User-Agent: curl/7.54.0
Accept: */*
Content-Length: 28
Content-Type: application/x-www-form-urlencoded
data=example1&data2=example2------------78b7f8917ad1992c
为什么我在这里boundary
?
问题:3
同时发送GET
作为:
curl localhost:8080
服务器上的输出:
GET / HTTP/1.1
Host: localhost:8080
User-Agent: curl/7.54.0
Accept: */*
ontent-Length: 28
Content-Type: application/x-www-form-urlencoded
data=example1&data2=example2------------78b7f8917ad1992c
我收到Content-Type
和x-www-form-urlencoded
数据以及boundary
。
我做错了什么? 我的代码或我的理解有问题吗?
Server.c :
// Server side C program to demonstrate Socket programming
#include <stdio.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <string.h>
#define PORT 8080
int main(int argc, char const *argv[])
{
int server_fd, new_socket; long valread;
struct sockaddr_in address;
int addrlen = sizeof(address);
char buffer[1024] = {0};
char *hello = "HTTP/1.1 200 OK\nContent-Type: text/plain\nContent-Length: 12\n\nHello world!";
// Creating socket file descriptor
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0)
{
perror("In socket");
exit(EXIT_FAILURE);
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons( PORT );
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address))<0)
{
perror("In bind");
exit(EXIT_FAILURE);
}
if (listen(server_fd, 10) < 0)
{
perror("In listen");
exit(EXIT_FAILURE);
}
while(1)
{
printf("\n+++++++ Waiting for new connection ++++++++\n\n");
if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen))<0)
{
perror("In accept");
exit(EXIT_FAILURE);
}
valread = read( new_socket , buffer, 1024);
printf("%s\n",buffer );
write(new_socket , hello , strlen(hello));
printf("------------------Hello message sent-------------------\n");
close(new_socket);
}
return 0;
}
答案 0 :(得分:2)
问题1
我检查了请求标头并找到了Expect: 100-continue
标头。这是我第一次见到这个标题。
Google中的简单搜索显示这导致了问题。
Expect: 100-Continue' Issues and Risks(我只是为了避免死链接而粘贴所有内容)
预期如何:100-Continue标题工作
当Expect:100-Continue不存在时,HTTP大致遵循以下流程(来自 客户的观点):
1. 请求启动与服务器的TCP连接。
2. 当建立与服务器的连接时,完整请求(包括请求标头和请求正文)将传输到服务器。
3. 客户端等待服务器的响应 (由响应头和响应主体组成)。
4. 如果是HTTP 支持keep-alives,可选地重复请求 第2步。
当客户端使用Expect:100-Continue功能时, 发生以下事件:
1. 请求启动与服务器的TCP连接。
2。当 建立与服务器的连接,请求 - 包括 标题,Expect:100-Continue标题,没有请求正文 - 是 然后传送到服务器。
3。客户端随后等待回复 从服务器。如果状态代码是最终状态代码,请使用 客户端上面的先前步骤在没有Expect的情况下重试请求: 100-Continue标题。如果状态代码为100-Continue,则为请求 正文被发送到服务器。
4. 客户端将等待响应 来自服务器(由响应头和响应主体组成)。
5. 如果支持HTTP keep-alives,则可选择重复该请求 从第2步开始。
为什么要使用Expect:100-Continue?
API POST请求 包括Expect:100-Continue标头之间的节省带宽 客户端和服务器,因为服务器可以拒绝API请求 在请求主体甚至传输之前。对于API POST请求 对于非常大的请求主体(例如文件上传),服务器可以, 例如,检查无效的身份验证并拒绝该请求 在发送推送体之前,导致显着的带宽 节省。
没有期望:100-继续:
没有期望:100-继续 功能,整个API请求,包括(可能很大) 在服务器可以连接之前必须传输推送体 确定语法或身份验证是否有效。但是,自从 我们的大多数API请求都有小的POST主体,这样做的好处是 将请求标头与请求主体分开可以忽略不计。
请求标题和正文单独发送时的问题
由于 Urban Airship处理的大量请求,涉及多个级别 我们的客户和负责的服务器之间存在复杂性 用于响应API请求。这不是一种异常现象 大多数服务器配置和策略,但确实引入了 使用。的任何API POST请求升级请求失败的风险 期待:100-Continue标题。这是由于请求的事实 标题和请求正文彼此分开发送,并且 必须在整个API中通过相同的连接 服务器基础设施。
使用代理数,负载平衡服务器和后端 请求处理已实现的服务器,请求处理 期望:100-Continue标题的成功概率增加 彼此分开,因此返回错误。
期待什么:
我们一直试图支持Expect:100-Continue。 但是,我们已确定使用Expect的客户: 由于,100-Continue正在接收次优服务质量 提升请求失败。
此外,我们的大多数API请求都有小的POST主体, 因此,将请求标题与...分开的好处 要求机构可以忽略不计。这些原因激励着我们 禁用对Expect的支持:100-Continue服务范围。
我们的建议:
我们建议不要使用Expect: 100继续。如果您收到HTTP错误417(期望失败), 在没有Expect的情况下重试请求:100-Continue。
因此,要阻止Expect: 100-continue
POST
中的form-data
标题,请在您的`curl
-H 'Expect:'
curl -X POST -F "name=user" -F "password=test" localhost:8080 -H 'Expect:'
现在,您可以像在评论中所说的那样一次性收到您的全部数据(就像Postman一样)。
问题2&amp; 3 强>
正如@melpomene在评论中所说,read()
在阅读之后并未放置\0
。这就是您看到之前请求数据的原因。
所以,正如我在评论中所说的那样,只需使用valread
迭代字符串进行打印,或者只是在while循环中声明变量。
<强>代码强>:
while(1)
{
printf("\n+++++++ Waiting for new connection ++++++++\n\n");
if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen))<0)
{
perror("In accept");
exit(EXIT_FAILURE);
}
char buffer[30000] = {0}; // This way you get new variable everytime. So, there is no need to iterate over the string using valread value.
valread = read( new_socket , buffer, 30000);
printf("%s\n",buffer );
write(new_socket , hello , strlen(hello));
printf("------------------Hello message sent-------------------%lu\n", valread);
close(new_socket);
}