上文中对面向连接的C/S模型中的服务器进行了简单描述,本文将说明如何编写一个客户端应用程序。与服务器程序不同,客户端程序要求有友好的交互界面,因此本文所示的客户端程序采用Qt和linux C共同完成。客户端的界面部分采用Qt完成,而与服务器间具体的通信部分则通过linux C语言完成。
1.界面设计
通过Qt Designer可快速设计好客户端界面,并对四个按钮所对应的信号和槽函数进行连接。客户端所对应的类为ClientUI。
2.与服务器间通信的编程
由于Qt是对C++的一种扩展,因此我们必须将使用Linux C编写的客户端通信程序封装成类,这里我们将其定义为ClientConnection。
我们已经对客户端的界面和通信部分分别定义了两个类,但是如何将两者结合在一起?方法很简单。将ClientConnection类的对象cli作为ClientUI类的成员,然后在具体的槽函数中对应相应的通信函数。和上文分析服务器编程的方法不同,本文将以分析客户端对应的槽函数的方式来说明如何编写客户端程序。
2.1连接按钮的槽函数
在连接按钮对应的槽函数中,首先创建客户端套接字描述符;然后从输入框中获得服务器的IP地址;再通过connect函数创建一个连接。
connect函数的原型如下:
#include < sys/socket.h > int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
connect函数用于在sockfd套接字上创建一个连接,sockfd即客户端套接字;addr即为服务器端的套接字地址;addrlen为addr的长度。我们将connect函数封装成ClientConnection类中的connectingSocket函数,该成员函数在连接按钮所对应的槽函数中被调用。:
int ClientConnection::connectingSocket() { if (::connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(struct sockaddr)) == -1) { return 0; } return 1; }
在该槽函数中,可以这样使用连接函数:
if (cli.connectingSocket() == 1) { ui->statusLabel->setText("connecting to server success!"); } else { ui->statusLabel->setText("ERROR:connecting to server fail!"); //exit(1); }
当客户端连接至服务器成功后,客户端就可以跟服务器端进行数据的传送。
2.2 修改按钮的槽函数
该槽函数的工作比较简单,使得文本编辑区可被编辑。
2.3 应用按钮的槽函数
在该槽函数中,将文本编辑区的数据发送至服务器端;或从服务器接受数据显示在这个文本编辑区中。发送和接受数据的API仍然是send和recv函数,不过这里我们将这两个函数封装成ClientConnection类中的sendData成员和recvData成员:
int ClientConnection::recvData(char *buf) { if ((recvbytes = recv(sockfd, buf, MAX_DATA_NUM, 0)) == -1) { return 0; } buf[recvbytes] = '\0'; return 1; } int ClientConnection::sendData(char *msg, int len) { if (send(sockfd, msg, len, 0) == -1) { printf("send error!\n"); return 0; } return 1; }
这两个成员函数在该槽函数的适当位置被调用。
2.4退出按钮的槽函数
退出按钮的槽函数中只需断开客户端和服务器之间的连接即可。
参考:
1.Qt Assistant
2.Linux C编程实战;童永清 著;人民邮电出版社;
@edsionte, 恩,最近仔细看了好几遍你写的文章,再加上一些自学,大致明白你的意思。
用按钮来触发槽来完成设计好的动作,比如连接按钮,可以用来触发 connection()这个槽函数,然后在这个槽函数里直接调用封装好的socket,比如你写的ClientConnection.connectingSocket(),就可以实现这个功能了。
为了能够在ClientUI中可以调用,在ClientUI.h中,把”ClientConnection.h”加入#include,且在public声明中建立这个对象,
ClientConnection cli;
这样在ClientUI.cpp中可以通过槽函数来调用了。
是这样吧?我是这样理解的,多亏了你这个想法。
但是,有2个小问题我一直没法解决,一个是,我在ClientUI.cpp中如果有个断开连接,想执行 delete cli; 为什么总是报错?
另外一个我没有解决的是,客户端如果发送数据成功的话,我想在客户端 的textBroswer显示,不知道怎么调用ClientConnection()里的私有变量buf?
这个东西是char型,没法return回去?
请问,有什么好方法么?
[回复一下]
你好,看到文章很高兴。毕业设计。在linux环境socket编写实现的简单文件服务器。纯Linux C编写,但是没有界面,用qt可实现但是具体不知道怎么做。可以指点一二吗?说说步骤也可以啊
[回复一下]
edsionte 回复:
3月 10th, 2013 at 20:37
@cc, 之前文章有介绍,你可以去自己查。
[回复一下]