学而不思则罔,思而不学则殆。学习和思考是相辅相成的,通过这几天对网络编程的学习,收获颇丰。接下来我将利用Qt做的一个以TcpIp协议为传输方式的简单的局域网聊天服务端与大家分享下:
首先谈谈我个人对Tcp协议的理解:Tcp就是网上购物,买家和买家之间的物品传递,快递公司的扮演。快递公司将卖家所要寄出的物品进行包装,给予独特的号码,并从卖家获取目的地地址,得知这些明确信息后准确将物品送到买家,买家签收后,卖家通过快递单号查询到买家签收的消息。
其次是这个简单局域网聊天服务器的创建思路。如下图是思路的框图:
一个服务器的建立,必须要有对外双向通讯的接口就是套接字(socket),所以需要建一个Mysocket的类,这样才可以将不同客户端的消息发给其他客户端。之后还需要再创建一个Myserver的类,将每个客户端发送的消息通过Mysocket发送信号被接受。然后在每个客户端对应的每个Myserver中将各自发送的信号发送给其他客户端,并再发送给server在UI界面上显示。如图:在Mysocket和Myserver以及Myserver和server之间的联系都是通过发送信号,并利用槽函数进行处理,所以这整个程序的关键是合理运用信号和槽函数,将信息顺利传达。
然后是程序的展示和一些关键点的处理方式。
//========================Mysocket.c=============================//
MyTcpSocket::MyTcpSocket(QObject *parent) :/*在构造函数中进行信号和槽函数的连接*/
QTcpSocket(parent)
{
connect(this,SIGNAL(readyRead()), /*读信号和对应的槽函数处理*/
SLOT(slotReadyread()));
connect(this,SIGNAL(disconnected()), /*断开连接的信号和对应的槽函数处理*/
SLOT(slotDisconnect()));
}
void MyTcpSocket::slotReadyread()
{
QString msg;
QByteArray ba; /*采用QByteArray字节数组的方式客户端发送消息进行读取* /
ba.resize(this->bytesAvailable()); /*判断是否可读*/
this->read(ba.data(),ba.size()); /*读取客户端发送的消息*/
msg=QString(ba); /*将接收到的消息转化为QString类型*/
emit updatemsg(msg,this->socketDescriptor(),this->peerAddress().toString());/*将客户端的信息通过信号的方式发送出去(信号包括客户端输入内容、客户端对应的描述符以及客户端的IP地址)*/
}
void MyTcpSocket::slotDisconnect()
{
qDebug("Disconnect host");
emit signalDisconnect(this->socketDescriptor()); /*将断开的客户端所对应的描述符信号发送出去*/
}
//========================Myserver.c=============================//
MyTcpserver::MyTcpserver(QObject *parent) : /*在构造函数中监听*/ QTcpServer(parent)
{
listen(QHostAddress::Any,8080);/*监听任何客户端IP地址的连入,以及端口号为8080的*/
/**/
}
void MyTcpserver::incomingConnection(qintptr socketDescriptor){/*incomingConnection这个函数在服务端被客户端连接上后自动调用此函数,传入的参数为该客户端特有的描述符*/
qDebug("incomingConnection");
MyTcpSocket* sock = new MyTcpSocket; /*新建一个Mysocket的对象*/
sock->setSocketDescriptor(socketDescriptor); /*设置sock的描述符*/
connect(sock,SIGNAL(signalDisconnect(qintptr)),
this,SLOT(slotDisconnect(qintptr))); /*断开连接的信号和对应的槽函数处理*/
connect(sock,SIGNAL(updatemsg(QString,qintptr,QString)),
this,SLOT(slotupdatemsg(QString,qintptr,QString)));/*将Mysocket发送过来客户端的信息进行处理,用于发送给UI界面*/
connect(sock,SIGNAL(updatemsg(QString,qintptr,QString)),
this,SLOT(slotrecivermsg(QString,qintptr,QString)));/*将Mysocket发送过来客户端的信息进行处理,再发送给其他连入的客户端*/
clients << sock; /*创建的一个QList的容器类名为clients,将每个不同的客户端放入到该容器中,以便后续处理*/
}
void MyTcpserver::slotDisconnect(qintptr sockfd)/*断开槽函数处理主要是将断开的客户端从容器中移除*/
{
int i=0;
for(i=0;i<clients.count();i++){ /*遍历容器中存入的客户端*/
if(clients.at(i)->socketDescriptor()==sockfd){/*当找到容器中对应的那个断开的客户端后,将其移除*/
clients.removeAt(i);
break;
}
}
}
void MyTcpserver::slotupdatemsg(QString msg,qintptr sockfd,QString IP)
{
qDebug()<<"slotupdatemsgServer";
emit updatemsgServer(msg,IP); /*将客户端信息通过信号的方式传送给UI界面*/
}
void MyTcpserver::slotrecivermsg(QString msg,qintptr sockfd,QString IP)
{
QString message;
message=IP+" : "+msg; /*设定回复给其他客户端的信息格式*/
int i=0;
for(i=0;i<clients.count();i++){
if(clients.at(i)->socketDescriptor()!=sockfd){ /*遍历容器中存入的客户端*/
clients.at(i)->write(message.toLatin1(),message.length());/*将消息发送给除自己以外的所有连入的客户端*/
}
}
}
//========================server.c=============================//
tcpserver::tcpserver(QWidget *parent) : /*在构造函数中进行信号和槽函数的连接*/
QWidget(parent),
ui(new Ui::tcpserver)
{
ui->setupUi(this);
server = new MyTcpserver; /*新建一个MyTcpserver对象*/
connect(server,SIGNAL(updatemsgServer(QString,QString)),
this,SLOT(recivermsg(QString,QString)));/*将MyTcpservert发送过来客户端的信息进行界面显示处理*/
}
tcpserver::~tcpserver() /*析构函数中释放ui*/
{
delete ui;
}
void tcpserver::recivermsg(QString msg,QString IP)
{
qDebug("recivermsg");
qDebug()<<msg;
ui->listWidget->addItem(IP+" : "+msg); /*显示发送的信号到UI界面上*/
}
最后感谢汇文,在汇文培训学习也有三个多月了,在这个融洽的大家庭中,感觉每天都很充实,在学习中实践,在实践中收获,愿自己在将来能找到一份合适的工作。附上汇文网站可供浏览:www.huiwen.com