我有以下服务器代码。我从一个网站上学习插件编程。
#include <stdio.h>
#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define BUFFER_SIZE 356
#define READ_SIZE 255
void error(const char *msg)
{
std::cerr << msg;
exit(1);
}
int main(int argc, char *argv[])
{
int sockfd, newsockfd, portno;
socklen_t clilen;
char buffer[BUFFER_SIZE];
struct sockaddr_in serv_addr, cli_addr;
int n;
if(argc < 2) error("ERROR, no port provided\n");
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) error("ERROR opening socket\n");
bzero((char *) &serv_addr, sizeof(serv_addr));
portno = atoi(argv[1]);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
if (bind(sockfd, (struct sockaddr *) &serv_addr,
sizeof(serv_addr)) < 0)
error("ERROR on binding");
listen(sockfd,5);
clilen = sizeof(cli_addr);
while(true){
newsockfd = accept(sockfd,
(struct sockaddr *) &cli_addr,
&clilen);
if (newsockfd < 0)
error("ERROR on accept");
bzero(buffer,BUFFER_SIZE);
n = read(newsockfd,buffer,READ_SIZE);
printf("Here is the message: %s\n",buffer);
std::cout << "hellow" << "\n";
char *message = "HTTP/1.1 200 OK\r\n\r\n<html><body><h1>Hello. Please don't close</h1></body></html>";
n = write(newsockfd,message,strlen(message));
if (n < 0) error("ERROR writing to socket");
close(newsockfd);
}
close(sockfd);
return 0;
}
问题似乎与read()
功能有关。当我在特定端口上运行此服务器并使用Firefox作为客户端时,浏览器会报告Connection was reset
。
以下是关闭连接时tcpdump
的输出:
15:29:44.315802 IP (tos 0x0, ttl 64, id 7572, offset 0, flags [DF], proto TCP (6), length 52)
localhost.8882 > localhost.36360: Flags [R.], cksum 0xfe28 (incorrect -> 0xebb8), seq 80, ack 291, win 350, options [nop,nop,TS val 3928986 ecr 3928985], length 0
因此服务器直接发送RST标志,并且不遵循FIN / ACK过程。
但是,如果我将READ_SIZE
的值从255更改为BUFFER_SIZE-1
,则代码可以正常工作。
以下是tcpdump
对应于连接关闭的新跟踪:
15:30:21.353901 IP (tos 0x0, ttl 64, id 3241, offset 0, flags [DF], proto TCP (6), length 52)
localhost.8882 > localhost.36437: Flags [F.], cksum 0xfe28 (incorrect -> 0x20b7), seq 80, ack 302, win 350, options [nop,nop,TS val 3938245 ecr 3938245], length 0
15:30:21.354071 IP (tos 0x0, ttl 64, id 38322, offset 0, flags [DF], proto TCP (6), length 52)
localhost.36437 > localhost.8882: Flags [F.], cksum 0xfe28 (incorrect -> 0x20be), seq 302, ack 81, win 342, options [nop,nop,TS val 3938245 ecr 3938245], length 0
15:30:21.354093 IP (tos 0x0, ttl 64, id 3242, offset 0, flags [DF], proto TCP (6), length 52)
localhost.8882 > localhost.36437: Flags [.], cksum 0xfe28 (incorrect -> 0x20b6), ack 303, win 350, options [nop,nop,TS val 3938245 ecr 3938245], length 0
为什么read()
函数会导致发送RST标志?为什么通过增加要读取的数量来解决问题?
注意:每次都会发生这种情况。这不是由于一些随机中断造成的。
编辑:根据Aif的建议,我尝试多次调用read()
。这也给出了类似的问题。以下是循环
while(true){
std::cout << toread << "\n";
readed = read(newsockfd, a, std::min(toread, 1));
// readed = read(newsockfd, a, 1);
std::cout << readed << "\n";
if(readed < 0){std::cout << "error reading"; exit(-1);}
if(readed == 0) break;
a += readed;
toread -= readed;
if(toread == 0) break;
}
我每次都读1个字节。现在,这给出了一个非常奇怪的问题。它只读取294个字节,然后无限期地停止。我不知道该程序是如何得出这个数字的。很快,即使这种重复阅读也不起作用。有什么想法吗?
答案 0 :(得分:1)
编辑:
read()
哦,顺便说一句,这是做套接字的旧方法,你现在应该使用getaddrinfo
。有关套接字编程的(非常非常)好的教程,我建议Beej's guide to skcet programming。
编辑2:
确实减少缓冲区大小时我不明白。无论如何,我对你的代码进行了一些修改,使其有效。
#include <stdio.h>
#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h> // Use errno
#define BUFFER_SIZE 10 // Very small buffer, to test the behaviour
#define READ_SIZE 255
void error(const char *msg)
{
std::cerr << msg;
exit(1);
}
int main(int argc, char *argv[])
{
int sockfd, newsockfd, portno;
socklen_t clilen;
char buffer[BUFFER_SIZE];
std::string msg; // Use a string for the "large" buffer
struct sockaddr_in serv_addr, cli_addr;
int n;
if(argc < 2) error("ERROR, no port provided\n");
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) error("ERROR opening socket\n");
bzero((char *) &serv_addr, sizeof(serv_addr));
portno = atoi(argv[1]);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
if (bind(sockfd, (struct sockaddr *) &serv_addr,
sizeof(serv_addr)) < 0)
error("ERROR on binding");
listen(sockfd,5);
clilen = sizeof(cli_addr);
while(1){
int pkt = 0;
newsockfd = accept(sockfd,
(struct sockaddr *) &cli_addr,
&clilen);
if (newsockfd < 0)
error("ERROR on accept");
bzero(buffer,BUFFER_SIZE);
//n = read(newsockfd,buffer,sizeof(buffer));
do
{
n = recv(newsockfd, buffer, sizeof(buffer), MSG_DONTWAIT); // Use recv instead of read
if (n>0)
{
buffer[n] = 0;
msg += buffer; // Actually increase the "large" buffer
pkt++;
}
} while (n > 0 || ((n == -1) && ((errno == EAGAIN) || (errno == EWOULDBLOCK)) && pkt == 0));
if (n<0)
{
if ((errno != EAGAIN) && (errno != EWOULDBLOCK))
error("Reading error\n");
else
std::cout << "Noting left to read" << std::endl;
}
std::cout << "Here is the message: " << msg << std::endl;
std::cout << "hellow" << "\n";
char *message = "HTTP/1.1 200 OK\r\n\r\n<html><body><h1>Hello. Please don't close</h1></body></html>";
n = write(newsockfd,message,strlen(message));
if (n < 0) error("ERROR writing to socket");
else std::cout << "Wrote " << n << " bytes" << std::endl;
close(newsockfd);
}
close(sockfd);
return 0;
}
主要变化是:
recv
代替read
来简化非阻止行为的使用希望这有帮助。