问题1:
在信号与槽(可能)的中断机制下,服务器无法实现同步处理多个客户端的业务请求。
——》客户端的业务:与另一个客户进行通信(数据的收发)。
——》数据的收发:数据从请求端转到服务器,通过服务器的中转,发送到目标客户端。
——》服务器的中转:服务器能够轻易地获取到所有客户端的IP与端口号,使用连接好的套接字即可将数据发送至对应客户端。
情报1:
- QTcpSocket的读写缓冲区是依对象单独存在的。
——》数据不会混乱,就算使用线程,也不需要考虑线程同步的 问题。
- 服务器读取任一客户端数据的流程是:
使用QByteArray接受该客户端的数据流,然后转换成合适的格式 显示到指定的区域中。
——》只要我在任一客户端连入时使用独立的QByteArray接受并 转发数据,自然也就避免了去考虑信息混用,或是说线程同步的 问题。
——》
解决方案1:
服务器可能会在同一时间处理多个客户端的业务请求,首先考虑到的解决方案应当是多线程。
---》服务器对多客户端处理的调度
每当有新客户端连入(触发newConnection()信号)时,做以下事情:
{
得到client:使用TcpServer内置nextpendingconnection() 方法得到一个TcpSocket类型的指针。
创建新线程(new的方式)
线程启动
}
但这样依然会报错。
问题2:
QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread
翻译一下,套接字对象不能在另一个线程里设置是否可用。
情报2:
也就是说套接字对象不能跨线程调用。
根本原因在于QTcpServer内置方法nextPendingConnection()与inComingConnection()的区别。
查看QT自带文档:
nextPendingConnection()返回一个已连接并挂起(pending)的QTcpSocket对象,并且会在QTcpServer销毁时自动回收(自动指定父对象)。
重点看下面,下面明确写到:返回的QTcpSocket对象不可以用于另外一个线程!如果你希望在另一个线程中使用即将到来的连接,你需要重写inComingConnection()。
帮助文档里已经指明了多线程服务器应当采用何种建立方式。
再查看inComingConnection()函数,
这个虚函数在有新连接可用时自动调用。该套接字描述符(参数)是为接入连接准备的本地套接字描述符。
(这个方法)的基本实现(底层实现)是创建一个QTcpSocket对象,然后设置它的套接字描述符,将封装好的这个QTcpSocket对象挂在连接表的末端(internal,最后位置),最后发射newConnection()信号。
解决方案2:
所以我们要做的是,编写一个类继承QTcpServer,重写它的inComingConnection()方法。
需要注意的是需要在inComingConnection()函数内部,发射带有套接字描述符的信号,通过信号的方式,将这个套接字描述符传递给服务器主线程。
我这里通过主线程又传递给了子线程,因为子线程来实现一对一的客服业务。
这样,就实现了套接字的跨线程。
(并没有传递QTcpSocket对象,而是传递了套接字描述符到目标线程,在它的里面进行封装)。
问题3(undone):
“无法为一个跨线程的父对象创建子对象”。
这个问题在目前阶段,还没有干扰到服务器的功能。当然,如果不解决的话,肯定会导致不可预知的错误。
问题4(undone):
按照我目前的框架,每有一个客户端连入服务器,就会新建一个线程,该线程就绑定了该客户,成为了该客户的客服线程。
-----》也就是说,该线程中使用QTcpSocket套接字对象绑定了qintptr socketDscriptor套接字描述符。
那么问题来了,
一旦有客户发数据,服务器要完成转发,务必需要知道QTcpSocket类型的套接字对象。而目标客户只要它在线,那服务器中就一定建立过一个绑定了唯一的套接字描述符的套接字了。
继续运行就会产生如下报错:
QSocketNotifier: Multiple socket notifiers for same socket xxx and type Read
这个问题的关键难点在于:套接字描述符不可被多个套接字对象绑定。
而在现在的多线程服务器框架下,要避免套接字跨线程调用失效,就必须使用套接字描述符传递再封装的方法,在这之后,服务器子线程要实现消息的转发还必须用到套接字对象,
可一旦子线程中给套接字对象绑定了套接字描述符,在另一个客服子线程中需要转发的时候就用不了套接字了。
这成了一个悖论。