我通过socket在c中创建服务器。客户端向服务器发送请求。服务器解析它并发回数据(html,png,jpg或bash脚本输出)。我对此有一些疑问。
当我读取html文件并将其发送给客户端时。如果文件是大数据不发送和浏览器重置连接The connection was reset
我怎么能等到所有数据都被发送?在这个循环中。
while ((ret = read(html, buff, 1023)) > 0)
{
write(client_socketfd, buff, ret);
}
是否可以像html一样发送图像(png或jpg),只更改html标题中的内容类型?
如果html文件中的标签是src =" another.html"点击它后客户端发送GET请求?
如果在html文件中是img标签,它是如何工作的?
最后一个问题关闭无限循环服务器的最佳方法是什么。在Linux中如果我用CTRL C
套接字关闭它就不会关闭。
如果出现其他问题,我将非常感谢您的建议。
int main(int argc, char *argv[])
{
int result;
int socket_desc, client_socketfd, c, read_size, buffer = 0;
struct sockaddr_in server, client;
char sprava[256];
int arg;
int port = 0;
char *homeDir = NULL;
//get command line arguments -p port -d home dir
while ((arg = getopt(argc, argv, "p:d:")) != -1) {
switch (arg) {
case 'p':
port = atoi(optarg);
break;
case 'd':
homeDir = optarg;
break;
default:
fprintf(stderr, "Please speicify -p port and -d home directiory\n");
exit(1);
}
}
if (port < 1500 || homeDir == NULL) {
fprintf(stderr, "BAD arguments use: -p port(greather then 1500) -d home dir\n");
exit(1);
}
//Create socket
socket_desc = socket(AF_INET, SOCK_STREAM, 0);
if (socket_desc == -1)
{
printf("Could not create socket");
return 1;
}
//Prepare the sockaddr_in structure
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons(port);
//Bind
if (bind(socket_desc, (struct sockaddr *)&server, sizeof(server)) < 0)
{
fprintf(stderr, "bind failed\n");
exit(1);
}
printf("bind done\n");
//Listen max 3
listen(socket_desc, 3);
//Accept and incoming connection
int loop = 1;
while (loop) {
printf("Waiting for incoming connections...\n");
c = sizeof(struct sockaddr_in);
loop = 0; //only for testing, if everything run ok loop will be infinity
client_socketfd = accept(socket_desc, (struct sockaddr *)&client, (socklen_t*)&c);
if (client_socketfd < 0) {
fprintf(stderr, "Accept failed\n");
exit(1);
}
//In child proc we are sending data
if (fork() == 0) {
close(socket_desc);//we dont need desc in child
bzero(sprava, 256);//all on '\0'
result = read(client_socketfd, sprava, 255);
printf("Server read: %s\n", sprava);
char* path;
int kod = parser(sprava, &path);//in path is path to file
if (kod == ERROR_FILE_TYPE)
{
printf("BAD request!!!!\n");
shutdown(client_socketfd, SHUT_RDWR);
close(client_socketfd);
}
if (kod == HTML || kod == BASH || kod == JPG || kod == PNG)
{
if (kod == BASH)
{
FILE *pipe;
char *cmd = path;
strcat(cmd, " 2>&1");//error output send to pipe
printf("New command is=%s\n", cmd + 1);//we dont need first /
//open pipe without first /
pipe = popen(cmd + 1, "r");
if (pipe != NULL) {
char text[1035];
while (fgets(text, sizeof(text) - 1, pipe) != NULL) {
printf("output=%s", path);
write(client_socketfd, text, strlen(text));
}
}
pclose(pipe);
}
else if (kod == HTML)
{
int html;
long len;
char buff[1024] = { 0 };
int ret;
printf("Try to open file=%s\n", path + 1);
html = open(path + 1, O_RDONLY);
if (html == -1) {
fprintf(stderr, "Error opening file\n");
}
len = (long)lseek(html, (off_t)0, SEEK_END);//len of file
lseek(html, (off_t)0, SEEK_SET);
sprintf(buff, "HTTP/1.1 200 OK\nServer: nweb/%d.0\nContent-Length: %ld\nConnection: close\nContent-Type: %s\n\n", 20, len, "text/html");
//send html header to client
printf("Length of file=%d\n", len);
write(client_socketfd, buff, strlen(buff));
printf("Header was send\n");
while ((ret = read(html, buff, 1023)) > 0)
{
printf("number of bytes read=%d\n", ret);
//write data to client,it will make connection reset
write(client_socketfd, buff, ret);
}
}
free(path);
}
shutdown(client_socketfd, SHUT_RDWR);
close(client_socketfd);
exit(0);
}
//in parent close client
else {
close(client_socketfd);
wait(&wt);//this wait is only for testing
}
}
close(socket_desc);
return 0;
}
答案 0 :(得分:0)
当我读取html文件并将其发送给客户端时。如果文件是大数据的话 不发送和浏览器重置连接
The connection was reset
如何 我可以等到所有数据都发送出去吗?在这个循环中。
虽然您提到的循环存在潜在问题,但您的问题很可能没有答案 我可以使用您的代码重现该问题,并在搜索了一篇非常有趣的帖子后The ultimate SO_LINGER page, or: why is my tcp not reliable。关键部分是来自section 4.2.2.13 of RFC 1122的这句话:
如果此类主机在接收到的数据仍然存在时发出CLOSE呼叫 在TCP中挂起,或者如果在调用CLOSE后收到新数据,则为 TCP应该发送一个RST来显示数据丢失。
您的程序可能(并且在我的测试中确实)已收到待处理数据,因为在对话开始时它调用read(client_socketfd, sprava, 255)
,因此,如果HTTP GET请求超过255个字节,则留下部分等待阅读的请求。现在最后,所有发送数据都已由write
提交给操作系统,当调用close
时,待处理数据仍然存在,因此操作系统通过发送强制RST中止连接立即,可能会丢弃其缓冲区中尚未传输的任何发送数据。因此,我们有一个令人惊讶的情况,即在开始时遗漏会导致最终丢失传输数据
一个快速而又脏的修复方法是将sprava
的大小和字节数增加到read
,以便读取整个请求 - 但是请求的最大长度是多少?正确的方法是循环中read
,直到请求被仅由\r\n
组成的空行终止。