【发布时间】:2015-10-06 13:12:13
【问题描述】:
下面是我能得到的最小示例。它确实需要位于单独的文件中,因为这似乎是导致分段错误错误的原因。
我正在使用 Mingw x32 4.8.1 和 Asio 独立 1.10.6 。我还使用 TDM GCC 4.7.1 和 Mingw x64 4.8.1 进行了测试。所有这些在 Windows 下都会产生相同的段错误。最新版本的 GCC 在 Linux 下不存在这样的问题。
编辑:我刚刚完成了对 Mingw-w64 5.2.0(它的 Mingw-Builds 构建)的测试。同样的问题。
我还测试了在新虚拟机上使用 TDM GCC 4.8.1 编译它并得到相同的段错误。编辑:我现在也在使用 TDM GCC 4.8.1 的完全不同的机器上进行了测试
在所有情况下,我都使用-std=c++11、-g 和-Wall。我也用-g 编译并得到相同的结果。我需要 C++11 标志,因为我不想依赖于 boost,只需要 asio。
使用单个main.cpp 文件中的以下代码没有问题,并且程序似乎按预期运行。但是,如果我将每个类放入它自己的 *.hpp 和 *.cpp 文件中,我会在 Server 类构造函数中遇到段错误。
我进一步返回并将所有内容放回main.cpp,并开始逐个移动每个班级。段错误在最后一课之后开始发生,Client 被放入它自己的文件中。
此外,当我将所有类放入一个文件并移动它们等时,我确保任何不需要的目标文件都没有链接到我的 .exe。
此代码开始时要大得多,但已缩小到此范围。
Server.hpp
#ifndef SERVER_HPP_INCLUDED
#define SERVER_HPP_INCLUDED
#include <string>
#include <memory>
#define ASIO_STANDALONE
#include <asio.hpp>
using namespace asio::ip;
namespace network
{
class Server
{
public:
Server(asio::io_service& ioService, uint16_t port);
private:
tcp::acceptor m_acceptor;
};
}
#endif // SERVER_HPP_INCLUDED
Server.cpp
#include "Server.hpp"
using namespace network;
#include <iostream>
Server::Server(asio::io_service& ioService, uint16_t port)
: m_acceptor(ioService, tcp::endpoint(tcp::v4(),port))
{
}
Client.hpp
#ifndef CLIENT_HPP_INCLUDED
#define CLIENT_HPP_INCLUDED
#include <vector>
#define ASIO_STANDALONE
#include <asio.hpp>
using namespace asio::ip;
namespace network
{
class Client
{
public:
Client(asio::io_service& ioService);
private:
asio::steady_timer m_timer;
};
}
#endif // CLIENT_HPP_INCLUDED
Client.cpp
#include "Client.hpp"
using namespace network;
#include <iostream>
Client::Client(asio::io_service& ioService)
: m_timer(ioService)
{
}
main.cpp
#include <iostream>
#define ASIO_STANDALONE
#include <asio.hpp>
using namespace asio::ip;
#include "Server.hpp"
int main()
{
try
{
uint16_t peerRequestPort = 63000;
asio::io_service io_service;
network::Server server(io_service,peerRequestPort);
}
catch(std::exception& e)
{
std::cout << e.what() << std::endl;
}
return 0;
}
这是来自 GDB 的调用堆栈:
#0 00406729 asio::detail::service_registry::keys_match(key1=..., key2=...) (F:/GameDev/asio-1.10.6/asio-1.10.6/include/asio/detail/impl/service_registry.ipp:89)
#1 ?? 0x0040696e in asio::detail::service_registry::do_use_service (this=0x5d2f10, key=..., factory=0x406b44 <asio::detail::service_registry::create<asio::socket_acceptor_service<asio::ip::tcp> >(asio::io_service&)>) (F:/GameDev/asio-1.10.6/asio-1.10.6/include/asio/detail/impl/service_registry.ipp:113)
#2 004068B6 asio::detail::service_registry::use_service<asio::socket_acceptor_service<asio::ip::tcp> >(this=0x5d2f10) (F:/GameDev/asio-1.10.6/asio-1.10.6/include/asio/detail/impl/service_registry.hpp:47)
#3 00403857 asio::use_service<asio::socket_acceptor_service<asio::ip::tcp> >(ios=...) (F:/GameDev/asio-1.10.6/asio-1.10.6/include/asio/impl/io_service.hpp:32)
#4 004039B3 asio::basic_io_object<asio::socket_acceptor_service<asio::ip::tcp>, true>::basic_io_object(this=0x28fe48, io_service=...) (F:/GameDev/asio-1.10.6/asio-1.10.6/include/asio/basic_io_object.hpp:182)
#5 00403B29 asio::basic_socket_acceptor<asio::ip::tcp, asio::socket_acceptor_service<asio::ip::tcp> >::basic_socket_acceptor(this=0x28fe48, io_service=..., endpoint=..., reuse_addr=true) (F:/GameDev/asio-1.10.6/asio-1.10.6/include/asio/basic_socket_acceptor.hpp:137)
#6 00401D3B network::Server::Server(this=0x28fe48, ioService=..., port=63000) (F:\GameDev\Dischan\Tests\Server.cpp:7)
#7 004018F1 main() (F:\GameDev\Dischan\Tests\main.cpp:17)
最后是 Dr Memory 的输出:
Dr. Memory version 1.8.0 build 8 built on Sep 9 2014 16:27:02
Dr. Memory results for pid 5296: "tests.exe"
Application cmdline: "tests.exe"
Recorded 108 suppression(s) from default C:\Program Files (x86)\Dr. Memory\bin\suppress-default.txt
Error #1: UNADDRESSABLE ACCESS: reading 0x00000007-0x0000000b 4 byte(s)
# 0 asio::detail::service_registry::keys_match [F:/GameDev/asio-1.10.6/asio-1.10.6/include/asio/detail/impl/service_registry.ipp:89]
# 1 asio::detail::service_registry::do_use_service [F:/GameDev/asio-1.10.6/asio-1.10.6/include/asio/detail/impl/service_registry.ipp:113]
# 2 asio::detail::service_registry::use_service<> [F:/GameDev/asio-1.10.6/asio-1.10.6/include/asio/detail/impl/service_registry.hpp:47]
# 3 asio::use_service<> [F:/GameDev/asio-1.10.6/asio-1.10.6/include/asio/impl/io_service.hpp:32]
# 4 asio::basic_io_object<>::basic_io_object [F:/GameDev/asio-1.10.6/asio-1.10.6/include/asio/basic_io_object.hpp:182]
# 5 asio::basic_socket_acceptor<>::basic_socket_acceptor [F:/GameDev/asio-1.10.6/asio-1.10.6/include/asio/basic_socket_acceptor.hpp:137]
# 6 network::Server::Server [F:/GameDev/Dischan/Tests/Server.cpp:7]
# 7 main [F:/GameDev/Dischan/Tests/main.cpp:17]
Note: @0:00:00.780 in thread 7464
Note: instruction: mov 0x04(%eax) -> %eax
Error #2: LEAK 36 direct bytes 0x02530860-0x02530884 + 124 indirect bytes
# 0 replace_operator_new [d:\drmemory_package\common\alloc_replace.c:2609]
# 1 asio::io_service::io_service [F:/GameDev/asio-1.10.6/asio-1.10.6/include/asio/impl/io_service.ipp:39]
# 2 main [F:/GameDev/Dischan/Tests/main.cpp:15]
===========================================================================
FINAL SUMMARY:
DUPLICATE ERROR COUNTS:
SUPPRESSIONS USED:
ERRORS FOUND:
1 unique, 1 total unaddressable access(es)
0 unique, 0 total uninitialized access(es)
0 unique, 0 total invalid heap argument(s)
0 unique, 0 total GDI usage error(s)
0 unique, 0 total handle leak(s)
0 unique, 0 total warning(s)
1 unique, 1 total, 160 byte(s) of leak(s)
0 unique, 0 total, 0 byte(s) of possible leak(s)
ERRORS IGNORED:
14 potential error(s) (suspected false positives)
(details: C:\Users\User\AppData\Roaming\Dr. Memory\DrMemory-tests.exe.5296.000\potential_errors.txt)
12 potential leak(s) (suspected false positives)
(details: C:\Users\User\AppData\Roaming\Dr. Memory\DrMemory-tests.exe.5296.000\potential_errors.txt)
24 unique, 24 total, 2549 byte(s) of still-reachable allocation(s)
(re-run with "-show_reachable" for details)
Details: C:\Users\User\AppData\Roaming\Dr. Memory\DrMemory-tests.exe.5296.000\results.txt
我只是不明白为什么会出现段错误。即使注释掉所有有意义的代码,它仍然会出现。
编辑
我已经编辑了上面的代码,以表明只有 Server 构造函数似乎会导致问题以及每个文件的内容。 (我再次确保只有这些目标文件被编译和链接)。
编辑 2
我已经使用 TDM GCC 4.7.1、Mingw Builds x64 4.8.1 和 Mingw Builds x32 4.8.1 对此进行了测试。所有的结果都是一样的。
编辑 3
我进一步减少了代码。现在,在Client 中,如果我删除任何需要构造asio::io_service& 的asio 对象,那么就没有段错误。但是到目前为止我尝试过的任何asio 类型都产生了相同的段错误。这在 Server 类中不是问题,例如,它有一个 asio::acceptor。遥远的事情是没有创建Client 的实例,所以为什么它会影响程序并在Servers 构造函数中产生段错误是很奇怪的。
编辑 4
我现在已经完全删除了 Server.hpp 和 Server.cpp 并将 main.cpp 更新为:
main.cpp
#include <iostream>
#define ASIO_STANDALONE
#include <asio.hpp>
using namespace asio::ip;
int main()
{
try
{
uint16_t peerRequestPort = 63000;
asio::io_service io_service;
auto protocol = tcp::v4();
tcp::endpoint endpoint(protocol,peerRequestPort);
tcp::acceptor m_acceptor(io_service, endpoint);
}
catch(std::exception& e)
{
std::cout << e.what() << std::endl;
}
return 0;
}
我仍然得到段错误并且调用堆栈反映了缺少Server 构造函数。段错误仍然在同一个地方。 DrMemory 结果看起来也差不多。
和以前一样,如果我不链接Client 目标文件,我没有问题。
编辑 5
根据要求,这是来自 Code::Blocks 的构建日志
g++.exe -std=c++11 -Wall -D_WIN32_WINNT=0x0501 -g -I..\..\asio-1.10.6\asio-1.10.6\include -c F:\GameDev\Dischan\Tests\Client.cpp -o obj\Debug\Client.o
g++.exe -std=c++11 -Wall -D_WIN32_WINNT=0x0501 -g -I..\..\asio-1.10.6\asio-1.10.6\include -c F:\GameDev\Dischan\Tests\main.cpp -o obj\Debug\main.o
g++.exe -o Build\Debug\Windows\Tests.exe obj\Debug\Client.o obj\Debug\main.o -lws2_32 -lwsock32
Output file is Build\Debug\Windows\Tests.exe with size 723.02 KB
Process terminated with status 0 (0 minute(s), 3 second(s))
0 error(s), 0 warning(s) (0 minute(s), 3 second(s))
编辑 6
我现在开始超出我的能力范围,但我设法找到了一些问题(而且我正在学习一些很酷的新东西)。
似乎在创建需要asio::io_service& 的对象时,它会将“服务”添加到io_service。这些在所有 io_service 实例中都是静态的。因此,当发出服务请求时,会出现一个循环,通过该循环似乎是已创建服务的链接列表。如果请求的服务尚未创建;然后创建它。
此信息来自 reference for io_service 和查看 service_registry.ipp(第 111 行)。
这是通过调用service_registry::do_use_service 在内部完成的。 service_registry 有一个名为 first_service_ 的成员,类型为 asio::io_service::service*。第一个服务应该有一个名为next_ 的成员,这是我提到的链表部分。
然而,在第一次调用service_registry::do_use_service 时(构造asio::acceptor 时),first_service_ 成员的值为0xffffffff,这显然是不正确的。所以我相信这是段错误的根源。
为什么这个成员有0xffffffff 的值是我无法理解的。我的理解是只有旧的/古怪的机器为null 指针保留了这个地址......但我承认我可能会走得很远。
我只是通过这样做快速检查了这一点:
int* p = nullptr;
if(p)
std::cout << "something" << std::endl;
并设置断点来读取值。 p 的值是 0x0,而不是 0xffffffff。
所以,我在service_registry (asio/detail/impl/service_registry.hpp) 的构造函数和析构函数(以防它在某处显式调用)以及do_has_service、do_use_service 和do_add_service 三个方法上设置了一个断点.我的想法是尝试跟踪 first_service_ 在什么时候得到错误值。
我没有运气。这些是唯一可能改变first_service_ 值的地方。
我认为这意味着某些东西损坏了堆栈并更改了first_service_ 的地址。但是,我只是个业余爱好者……
我确实检查了构造函数的 this 指针的地址与用于调用 do_use_service 的地址相同,以确保没有创建两个实例或类似的东西。
编辑 7
好的,所以我现在发现,如果我使用 ASIO_DISABLE_THREADS 进行编译,我将不再遇到段错误!
但是,这会导致引发异常,因为我尝试使用线程,即使我已禁用它们。我的意思是我将被限制为同步调用而不是异步调用。 (即,使用 asio 有什么意义?)
reference material here 确实说ASIO_DISABLE_THREADS
明确禁用 Asio 的线程支持,与 Boost 是否支持线程无关。
所以我认为这个定义会阻止 asio 使用线程,无论是否提升;这是有道理的。
为什么线程会导致问题,我不知道。我不热衷于深入研究。
我放弃了
我放弃了asio。在查看了代码和文档之后,它似乎是在开发时考虑到了 boost-so 而不是作为一个独立的库。显然到了需要在 C++11 上使用 Boost 的地步,我只是不想这样做。
Best C/C++ Network Library 看起来有很多选择。
说实话,考虑到我将获得的控制权,在我自己的线程中运行我自己的同步套接字调用听起来是一个更好的主意。至少在 asio 进入标准库并在 Mingw-w64 中实现之前。
考虑到 asio 看起来像是标准库中的主要候选者,或者至少是它的风格,坚持使用它可能是个好主意。
【问题讨论】:
-
评论不用于扩展讨论;这个对话是moved to chat。
-
我很担心你的健康
-
这似乎与您的问题非常相似:stackoverflow.com/questions/28427000/…
-
@Jonathan 这听起来像同样的问题;但是,没有比这里更多的答案了。 :(
-
这可能是一个愚蠢的评论。但是你不应该调用io服务对象的run方法吗?
标签: c++ boost boost-asio