C++:Linux:调用write()时TCP/IP程序崩溃

3

我有一个循环,通过TCP/IP向客户端不断写入数据。连接方式如下:

newsockfd = accept(sockfd,
            (struct sockaddr *) &cli_addr,
            &clilen);

以下代码会在一个循环中连续执行(每0.1秒休眠一次),以便将数据写入客户端:
n = write(newsockfd,data.c_str(),data.length()+1); //+1 to include NULL in null terminated string
if(n>=0)
{
    cout<<"success"<<endl;
}
else
{
    cout<<"Fail"<<endl;
    close(newsockfd);
    newsockfd = -1;
}

我希望服务器在任何原因断开连接时都能准备接收新的连接。所以如果写入失败,我会再次准备好使用第一个命令接受新的连接。
我的问题是:这个方法第一次成功了,所以如果从客户端中断连接,write() 返回负数,我可以立即知道连接出现了问题,那么我将关闭它并期待一个新的连接。服务器接收到了新的连接,但下一次使用 write() 时,程序会立即崩溃。
为什么会出现这种情况呢?请帮忙解决,我对TCP/IP不太熟悉。
如果需要更多信息,请提出要求。
请求来自助手:
堆栈跟踪:
错误:信号13:
    /mnt/hgfs/Dropbox/common_src/LinuxTCP/Server/ServerLinux-build-desktop-Qt_4_8_1_in_PATH__System__Release/ServerLinux[0x402155]
    /lib/x86_64-linux-gnu/libc.so.6(+0x364a0)[0x7ffc57ac04a0]
    /lib/x86_64-linux-gnu/libpthread.so.0(write+0x10)[0x7ffc5836dcb0]
    /mnt/hgfs/Dropbox/common_src/LinuxTCP/Server/ServerLinux-build-desktop-Qt_4_8_1_in_PATH__System__Release/ServerLinux[0x4023b6]
    /mnt/hgfs/Dropbox/common_src/LinuxTCP/Server/ServerLinux-build-desktop-Qt_4_8_1_in_PATH__System__Release/ServerLinux[0x401b54]
    /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xed)[0x7ffc57aab76d]
    /mnt/hgfs/Dropbox/common_src/LinuxTCP/Server/ServerLinux-build-desktop-Qt_4_8_1_in_PATH__System__Release/ServerLinux[0x402081]

变量定义:这是一个类。
正文:
int sockfd, portno, n;
struct sockaddr_in serv_addr;
struct hostent *server;

构造函数开始处理:
LinuxTCPServer::LinuxTCPServer(int port, bool nonblocking)
{
if(nonblocking)
    sockfd = socket(AF_INET, SOCK_NONBLOCK | SOCK_STREAM, 0);
else
    sockfd = socket(AF_INET, SOCK_STREAM, 0);

if (sockfd < 0)
   error("ERROR opening socket");
bzero((char *) &serv_addr, sizeof(serv_addr));
portno = port;

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);
}

这是学习一个必要工具——调试器的好机会,不要错过。 - n. m.
@didierc n是整数,data是std::string中的以空字符结尾的字符串。 - The Quantum Physicist
@FatalError 添加了堆栈跟踪,请检查它。 - The Quantum Physicist
@2to1mux,我已经在帖子中添加了这些信息,请查看一下。谢谢。 - The Quantum Physicist
accept函数的返回代码是什么? - didierc
显示剩余3条评论
2个回答

5

假设Linux版本>=2.2,请将下面的内容进行替换:

n = write(newsockfd,data.c_str(),data.length()+1);

使用以下内容:

n = send(newsockfd, data.c_str(), data.length()+1, MSG_NOSIGNAL);

send(2)函数将返回-1并将errno设置为EPIPE,而不是生成致命的SIGPIPE。 或者可以忽略SIGPIPE信号。

当收到SIGPIPE信号时,newsockfd连接已经断开。我们没有足够的代码来复现问题,客户端和服务器都可能存在问题,因此说出实际出错原因不是非常重要。但是,将SIGPIPE转换为EPIPE至少能让您的服务器有机会处理断开的连接。


谢谢您的回答,我还有一个问题。read()会产生SIGPIPE吗?它的替代方法是什么?我能否也使用MSG_NOSIGNAL呢?我在外面没有找到这个常见的用法。 - The Quantum Physicist
1
@SamerAfach,不会。read(2)/recv(2)/recvfrom(2)/recvmsg(2)将报告一个-1返回和ECONNRESET的断开连接。 - pilcrow

2

根据您的堆栈跟踪信息,程序崩溃并显示为信号13错误,这意味着您有一个损坏的管道。

这表明您的连接已中断,但您仍在尝试写入它。参见此线程,了解可能导致“broken pipe”错误的原因:What causes the Broken Pipe Error?

现在,关于如何解决此问题,我怀疑您的“accept”调用实际上并没有得到正确设置的有效连接。确保在调用write之前检查您的“accept”调用状态。

造成accept调用失败的问题可能在连接的另一侧。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接