来自QQ群 Linux && 技术分享 311078264
此文档由elikang整理,如有疑问请进群讨论。
部分ARM开发板中QTCPServer不能接受Socket连接
问题描述:
当Tcp服务器端在PC上运行,客户端在开发板上运行的情况下,可以正常通信。
当反过来,Tcp的客户端运行在开发板上等待客户端连接的时候却出现了问题。
在连接过程中,客户端显示已经连接成功了,但是服务器端却没有检测,说明连接过程存在问题;
而且服务器和客户端之间也不能通信。
下面是netstat显示的连接信息:
1、
TCP服务器端监听状态
2、
TCP客户端连接服务器
TCP客户端断开连接
===============================
在网上看到有位前辈这么说的,个人感觉关联不大:
##############################################
针对TQ部分ARM开发板中QTCPServer不能接受数据的问题 2016-04-19
这种现象在TQ6410和TQIMX6上会有,其他开发板不清楚。
这种问题是交叉编译器的BUG!(好坑啊,还以为是编译qt时配置出问题了)
解决办法是:
用4.3.3的交叉编译器,编译qt源码,再将编译的qt源码放到ARM中替换原来的qt源码(/opt/PDA/);
##########################################################
我使用的编译器是:
arm-none-linux-gnueabi-gcc 4.4.3版本
更换了arm-linux-gnueabi-gcc 4.7.3版本
之后问题依然存在,下面是我找到与accept()相关的QT底层的部分代码。
错误就是由于socketEngine->accept()返回了-1才产生的,接下来继续寻找accept的相关代码:
见 qglog.h文件定义:
#define Q_D(Class) Class##Private * const d = d_func()
#define Q_Q(Class) Class * const q = q_func()
d指针是在主类中使用的,来获取 私有子类成员指针
q指针是在私有数据类中使用的,来获取 主类对象指针
上面accept函数中的d是QSocks5SocketEnginePrivate的实例,由于返回了sd=-1,才产生了错误。
===============================================
通过两天时间的探索,并没有找到最终原因,只是定位了问题所在之处;
接下来决定采用一个替代方案来继续后面的操作,替代方案即是使用C语言来实现tcp的bind()、listen()和accept(),然后再将Socket传给Qt使用。
这种做法实质就是取代QTcpServer的功能,测试的效果还是蛮好的。
1、在头文件MainWindow.h中加入下面的声明:
public:
void server_listen();
signals:
void recvConnection(QTcpSocket *socket);
private slots:
void getConnection(QTcpSocket *socket);
void slot_disconnected();
2、在.cpp文件中实现
在构造函数中加入下面代码:
connect(this, SIGNAL(recvConnection(QTcpSocket *)), this, SLOT(getConnection(QTcpSocket *)));
server_listen();
server_listen()实现:
void MainWindow::server_listen()
{
ui->pushBtn_start->setDisabled(true);
int newsd, sd;
struct sockaddr_in laddr,raddr;
socklen_t raddr_len;
sd = socket(AF_INET,SOCK_STREAM,0/*IPPROTO_TCP,IPPROTO_SCTP*/);
if(sd < 0)
{
perror("socket()");
exit(1);
}
laddr.sin_family = AF_INET;
laddr.sin_port = htons(atoi("6678"));
inet_pton(AF_INET,"0.0.0.0",&laddr.sin_addr);
if(bind(sd, (const sockaddr *)&laddr,sizeof(laddr)) < 0)
{
perror("bind()");
return;
}
if(listen(sd,200) < 0)
{
perror("listen()");
return;
}
raddr_len = sizeof(raddr);
while(1)
{
newsd = accept(sd,(sockaddr *)&raddr,&raddr_len);
if(newsd < 0)
{
if(errno == EINTR || errno == EAGAIN)
continue;
perror("accept()");
exit(1);
}
break;
}
QTcpSocket *socket = new QTcpSocket(this);
socket->setSocketDescriptor(newsd);//socketDescriptor
// or get socket_server directly
// socket_server->setSocketDescriptor(socketDescriptor);
emit recvConnection(socket);
}
getConnection实现:
void MainWindow::getConnection(QTcpSocket *socket)
{
socket_server = socket;
connect(socket_server, SIGNAL(disconnected()), this, SLOT(slot_disconnected()));
connect(socket_server, SIGNAL(readyRead()), this, SLOT(readMessage()));
ui->label_status->setText("Server:client connection get");
}
slot_disconnection()实现:
void MainWindow::slot_disconnected()
{
ui->pushBtn_start->setDisabled(false);
ui->label_status->setText("Socket: disconnected");
}
当服务端运行起来之后,可以通过telnet来测试是否可以连接:
telnet 192.168.122.60 6678
按下'Ctrl+Alt+]', 可以离开当前状态,然后输入disconnect即可断开连接(在XShell中的操作,其它终端类似) 。