Showing posts with label Qt. Show all posts
Showing posts with label Qt. Show all posts

Saturday, May 16, 2009

[Perl & Qt] OC:Net -> Multiperson Chat

Introduction

Download: SimpleChat.zip

For multi-person chat programming, programmer might use pthread (multi-threading) or fork (multi-processing) for multiplexing. In the early of 1990s, select() was frequently used for non-blocking communication. and it still been used for nowadays server. As we know, socket is just a one of the file descriptor. Thus, someone could enable the non-blocking property through fcntl() (control open file descriptors) for non-blocking communcation. However, the performance issue might come after this setting. So, in this topic, I would like to show you how to use the conventional way to implement Chat Server via select(). For chat client, I will aslo introduce you two methods, one is select() in perl, another one is QThread in Qt.

Server-Side

#! /usr/bin/perl -w

###############################################################
# Project: Perl Server Socket for multiperson chat
# Description: A convention way to implement multiperson chat
# via select().
# Date: 2009.05.16
# Programmer: Lin Xin Yu
# Website:http://importcode.blogspot.com
###############################################################

use strict;
use warnings;
use IO::Socket;
use IO::Select;

#=============== Network Settings =============================
my $MAX_LEN = 1024;
my $host = 'localhost';
my $port = shift || 3456;

#==============================================================
#********** THIS IS THE MAIN PROGRAM **************************
#==============================================================

my $buf;

# Create the receiving socket
my $server = new IO::Socket::INET( LocalPort => $port,
Proto => 'tcp',
Listen => SOMAXCONN,
Reuse => 1,)
or die "Could not create socket: $!\n";

#=============== Select() Settings ============================

# create handle set for reading
my $readSet = new IO::Select();

# add server socket into readSet
$readSet->add($server);
$readSet->add(\*STDIN);
my %socketSet = ();

#=============== Main Process =================================

while (1) {

# get a set of readable socket (blocks until at least one socket is ready)
my ($readableSet) = IO::Select->select($readSet, undef, undef, 0);

# take all readable socket in turn
foreach my $socket (@$readableSet) {
# accpeting new client if the socket is serverSocket
if ($socket == $server) {
my $client = $server->accept();
$readSet->add($client);
$socketSet{$client} = $client;
}elsif($socket == \*STDIN){
$buf = <STDIN>;
if($buf =~ /^(exit|quit)/){
foreach my $closeSocket(values %socketSet){
syswrite($closeSocket, "Server is going to offline after 5 Seconds\n", $MAX_LEN);
}

sleep(5);
foreach my $closeSocket(values %socketSet){
close($closeSocket);
}
close($server);
exit(0);
}
}
# otherwise it is an ordinary socket and we should read and process the request
else {
sysread($socket, $buf, $MAX_LEN);
if($buf) {
if($buf =~ /^(:exit|:quit|:q)/i){
$buf =~ s/^(:exit|:quit|:q)[ ]+//;
delete $socketSet{$socket};
$readSet->remove($socket);
close($socket);
}
foreach my $closeSocket(values %socketSet){
if($closeSocket != $socket){
syswrite($closeSocket, $buf, $MAX_LEN);
}
}
} else { # the client has closed the socket or aborted
# remove the socket from the $readSet and close it
delete $socketSet{$socket};
$readSet->remove($socket);
close($socket);
}
}
}
}

#==============================================================
#********** END OF THE MAIN PROGRAM ***************************
#==============================================================

As you can see, server just simply check the available socket and given the suitable processing. In addition to use select(), programmer must remember to use system IO rather than buffering IO for non-blocking communication.

Client-Side

Perl Client

#! /usr/bin/perl
###############################################################
# Project: Perl Client Socket for multiperson chat
# Description: A convention way to implement multiperson chat
# via select().
# Date: 2009.05.16
# Programmer: Lin Xin Yu
# Website:http://importcode.blogspot.com
###############################################################

use strict;
use warnings;
use IO::Socket;
use IO::Select;

#=============== Network Settings =============================
my $MAX_LEN = 1024;
my $remote_host = shift || 'localhost';
my $remote_port = shift || 3456;
my $client;

#==============================================================
#********** THIS IS THE MAIN PROGRAM **************************
#==============================================================

# get use name from input
print "please enter your name:";
chomp(my $name = <STDIN>);
print STDERR $name.":";

# Socket connection for Client.
$client = new IO::Socket::INET( PeerAddr => $remote_host,
PeerPort => $remote_port,
Proto => 'tcp',
Type => SOCK_STREAM,
Timeout => 20)
or die "Couldn't connect to $remote_host:$remote_port : $@\n";

my $buf;

#=============== Select() Settings ============================

# create handle set for reading
my $readSet = new IO::Select();

# add client socket into readSet
$readSet->add($client);
$readSet->add(\*STDIN);


while (1) {

# get a set of readable socket (blocks until at least one socket is ready)
my ($readableSet) = IO::Select->select($readSet, undef, undef, 0);

# take all readable socket in turn
foreach my $socket (@$readableSet) {
if($socket == \*STDIN){
chomp($buf = <STDIN>);
if($buf =~ /^(:exit|:quit|:q)/){
close($client);
exit(0);
}else{
syswrite($client, "$name:$buf\n", $MAX_LEN);
print STDERR $name.":";
}
}
# otherwise it is an ordinary socket and we should read and process the request
else {
sysread($socket, $buf, $MAX_LEN);
if($buf) {
my $i=0;
while($i++<256){
print STDERR "\b";
}
print STDERR $buf;
print STDERR $name.":";
} else { # the server has close the socket or aborted
# remove the socket from the $readSet and close it
close($client);
exit(0);
}
}
}
}

#==============================================================
#********** END OF THE MAIN PROGRAM ***************************
#==============================================================


One of the major problem for this perl chat client is your standard output might be interrupted by incoming message. Do NOT report it as a bug, it's a characteristic of system IO.

Qt Client

I don't want to show the whole bunch source code on the blog. Here I just post the most important sections of incoming and outcoming.

incoming parts in charclient.cpp

bool ChatClient::onSendClicked(){
int ret;
char msg[256];

QString qmsg = nickName+":"+ui.lineEdit_input->text();

ui.textEdit_output->append(qmsg);
ui.lineEdit_input->setText("");
qmsg += "\n";
QStringToChar(qmsg, msg, qmsg.length());

try{
ret = client->send(msg);
if(ret <= 0 ) throw ret;
}catch(int err){
if(err == 0) ui.textEdit_output->append("Server is offline!\n");
else if(err == -1) ui.textEdit_output->append("Fail to send message to Server!\n");
}

return true;
}


outcoming parts in ClientRecvThread.cpp

void ClientRecvThread::run(){
int ret;
char buf[MAX_BUF];

while((ret = socket->recv(buf)) > 0){
buf[ret-1]='\0';
ui->textEdit_output->append(buf);
}

if(ret == 0){
ui->textEdit_output->append("Server is offline or close the connection");
}else{
ui->textEdit_output->append("Fail to receive message from server!");
}

exit(0);
}

The reasonable solution for chat client is a GUI program. Thus, we can separate input and output message into two widgets, and processing it respectively. Here, I use C++ and Qt framework for this fancy work.

After a successful connection, this program will have two threads at the same time. One is the main thread which was created by main program itself, another one is ClientRecvThread which will been activated when the connection success.

The ClientRecvThread class which inherit from QThread was created for listening the incoming message only. It must be a great challenge while doing the multi-threading programming in this project, especially when you encountering the race condition or thread collection. Any single mistabke might kick your program in the hell. For instance, in Qt chat client, those two threads(main and ClientRecvThread) will sometimes use the same resource (QTextEdit) at the same time, and probably crash the whole program. A possible solution for these situation is locking the resource while one thread is now processing with it. but in this simple demonstration, I didn't fix it.

#For rapidly development, it's better to use QThread and QTcpSocket rather than pthread and self-implemented socket.

#QThread is similar to Java's runnable, so you might feel comfortable if you already know Java.

Compiling

There is no need to tell you how to compile perl program ( casue it's a interpreted language ). For Qt Client, you just need to follow the following steps:

  • Download and unzip source code
  • Type 'make' to compile it
  • Waiting for fews seconds
  • Run the program or just click it

This program was developed with Qt 4.5.1, so I don't pretty sure that the old version compiler can work without any error.

Screenshot

Client - Xinyu


Client - Lin Xin Yu

Conclusion

  • Socket is a typical solution for the integration with several different programming languages.
  • Select() is still a good way for non-blocking communication.
  • Multi-threading can save more resources than multi-processing, but difficult to handle.
  • A good C++ style program can even have the same performance with C.
  • Let the heavy loading program as simple as possible.
Any suggestions would be appreciated, but may not be taken.
For more detailed information, please see the source code.

Monday, April 6, 2009

[Qt] QCalendarWidget

Introduction:

Qt provides lots of useful widgets for developer. The programmer just need to know how to use it in their own project. Today, I want to give a briefy demonstration for the use of the QCalendarWidget.
  • Project: Qt Simple Calendar
  • Skill Requirement:Basic C++ and Qt
  • Difficulty: Beginner
Create a Project:

Create a Qt gui project, and edit your ui file. Drag a Calendar widget from Qt Widget Box to your Qt Designer Editor. We also need three labels and three LineEdit.


then, click the "Edit Signals/Slots" to setup a slot.



Select the "clicked(QDate)" slots and click the right-hand side's "edit" button to setup a function name - "onDateSelected(QDate)" for the slots. Cause the slots will pass a QDate object to your slot handler, so don't forget the parameter type.

Coding:

Edit your header file and add the slot into your header.

#ifndef SIMPLECALENDAR_H
#define SIMPLECALENDAR_H

#include <Qtgui/QWidget>
#include "ui_simplecalendar.h"

class SimpleCalendar : public QWidget
{
Q_OBJECT

public:
SimpleCalendar(QWidget *parent = 0);
~SimpleCalendar();

public slots:
void onDateSelected(QDate);

private:
Ui::SimpleCalendarClass ui;
};

#endif // SIMPLECALENDAR_H


Edit your class source code. When the clicked signal was emitted, this program will show the year, month and day on their own Line Edit.

#include "simplecalendar.h"

SimpleCalendar::SimpleCalendar(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
}

SimpleCalendar::~SimpleCalendar()
{

}

void SimpleCalendar::onDateSelected(QDate qdate){
QString str;
ui.yearLine->setText(str.number(qdate.year(),10));
ui.monthLine->setText(str.number(qdate.month(),10));
ui.dayLine->setText(str.number(qdate.day(),10));
}


Compile and Run:

Right-Click your Qt project to compile it. Your program could be similar to the screenshot.


Conclusion:

It's quite simple and efficient for creating a calendar in Qt.
If you want to know more about the QCalendarWidget or other widgets. Please visit Qt Reference Documentation.

[Eclipse] Qt Eclipse Integration for C++

Introduction:

Even though Qt has its own powerful IDE tool - QT Creator, I still prefer to use eclipse for developing QT programs.

Installation:

The basic requirements for installing the plugin are Eclipse and CDT. You can get the related software from these two posts [Eclipse] Eclipse HowTo [Eclipse] Integraded with C/C++.

Download Qt Eclipse Integration for C++ here

Unpack the archive to your eclipse folder and restart the eclipse.

Create a New Qt GUI Project:

After the installation completed, click "File->New->Project->Qt Gui Project". Click "Next" to continue.


Given a name for your project and click "next" to continue.


Select a UI type to inherited. In this example, I choose the QManWindow.


Select some modules or just leave it as default. Click "Finish" to create the project.

Coding:

Qt is a object orientation framework, so you must be familiar with object orientation programming. To getting started with the QT API, please see the official Qt tutorial

http://www.qtsoftware.com/developer/getting-started

Compile and Run:

Right-Click your Qt project, and choose the "Run As", click "Local C/C++ Application" to compile and run the Qt program.

Qt make GUI design easier, hope you will love it.