Linux课程设计报告书

Linux操作系统与程序设

计课程设计A报告书

姓名:

学号:

班级:

专业:

指导老师:

提交日期:20xx年06月14日

1、实验项目目的

《Linux操作系统课程设计B》是一门在课程《Linux操作系统与程序设计B》后独立开设的实验课程。这一门实验课程的开设目的是为了通过学生独立完成一个基于Linux平台的较大型应用程序,巩固课堂上学到的Linux平台上的编程规范、技术和技巧,培养学生的编写较大型程序的能力和提高学生综合应用素质。

本课程设计实验主要围绕Linux平台上主流的基础技术展开,这些技术包括:Linux的进程、线程通信和同步技术; socket网络通信技术等,这些技术可以集中体现并应用在并发程序设计中。通过并发程序的设计与开发,培养学生底层软件开发的能力,并为将来从事UNIX/Linux平台开发、嵌入式开发等相对高端的软件开发工作打下基础。

2、实验项目的功能及模块划分

本软件是一个Linux下基于socket的聊天室程序,能让局域网内的用户通过该软件进行简单的文字通信。在此基础上增加了

聊天室成员之间的发送私聊信息;

当新的成员加入后能自动收取最近一段时间内的聊天上下文;

用户能够查看历史聊天记录;

软件界面基于Qt实现,图形化界面方便用户操作。

主要模块划分:

服务端:

数据包发送和接受模块,聊天记录数据库读写模块,数据包处理模块,聊天记录查询模块

客户端:

数据包发送和接受模块,数据包处理模块,聊天记录查询模块,用户界面与展示模块

3、设计与实现

本系统采用CS架构,服务端采用固定的端口通信,每个客户端动态设置端口。客户端启动后向服务端告知自己所使用的端口号,以便可以双向通信,同时服务器负责为每个客户端分配一个唯一的ID(服务器的ID为1)

客户端和服务端以及客户端和客户端之间采用约定的数据格式进行通信,以便接收方可以正确的解析命令和数据。

数据包通用格式定义如下

#define MAX_UDP_SIZE 1000

struct udp_packet{

int type;

int senderId;

long size;

char content[MAX_UDP_SIZE];

};

type: 表示该数据包的类型,直接决定content字段的含义

senderId:该数据包的发送者的ID,

size:整个数据包的数据长度

content:数据包的内容,其数据格式由type决定。

服务器和客户端接受到数据包后,根据type字段的值来解析content字段的数据,从而作出正确的处理和响应。

所有的数据包类型以及对应的content字段的数据结构全部定义在define.h文件中

https://upload.fanwen118.com/wk-img/img100/3788342_1.jpg

由上图可以看出,服务器和客户端程序在总体结构上相似。服务器和客户端的全部功在数据处理模块中实现,这也是整个程序的核心之处

由于数据包的接受在单独的线程中完成,而界面采用Qt实现。因此在接受线程中采用QCoreApplication::postEvent()方法向界面线程发送通知事件,在界面窗口中通过重载customEvent()方法响应该通知,然后从Server中获取数据并显示。整个过程中涉及到线程同步和多线程安全问题,觉采用信号量和互斥量解决。

4文件操作

聊天记录的保存是通过文件的操作实现的

具体代码

void ChatClient::saveRecord(){

saveRecords("chatRecords.txt");

}

void ChatClient::saveRecords(const QString &fileName){

QFile file(fileName);

if (!file.open(QFile::WriteOnly | QFile::Text)) {

QMessageBox::warning(this, tr("Application"),

tr("Cannot write file %1:\n%2.")

.arg(fileName).arg(file.errorString()));

return;

}

QTextStream out(&file);

out << chatBox->toPlainText();

}

5、测试与调试

https://upload.fanwen118.com/wk-img/img100/3788342_2.jpg

https://upload.fanwen118.com/wk-img/img100/3788342_3.jpg

https://upload.fanwen118.com/wk-img/img100/3788342_4.jpg

6总结

对于此次课程设计,我早就在开课之前借了Linux相关书籍参看,但上面讲的主要是有关Linux 操作方面的内容,编程方面讲得很少,而且在开课之前也并不知道课设的题目是什么,因此此次课设基本上都是在开学后的这两周内完成的。

以前做过的软件方面的课设如C语言课设、数据结构课设都是在假期完成的,由于自己是一个十分追求完美的人,因此几乎每次都花了将近大半个假期的时间来做,如C语言就花了一个多月的时间来做,分数当然也较高,有90来分。对于课程设计,我历来都是相当认真的,此次操作系统的课程设计当然也不例外。可是Linux以前没怎么接触过,学校也没怎么系统地讲过,在刚接到题目时除了知道如何用GCC编译等等,几乎可以算作处于一无所知的状态。时间紧任务重,要从对Linux一无所知的状态到独立出色地完成课设,这是件不容易的事情啊!

但最后,我终于明白,分数不过是个数字,知识才是自己的。通过这次课程设计,我确实学到了很多东西,多年后我可能已经忘记这次课设最后打了多少分,但这些学到的东西却可以使我受益终生。除了知识技术上的东西,我更锻炼了自己的快速学习能力;我学会了如何快速有效地从图书馆、网络获取自己需要的信息;我尝到了在周围很多同学拷来拷去时孤军奋战的痛苦;我体会了夜以继日完成一个项目时中途过程的艰辛及最终完成后巨大的成就感……我更加深了人生的信心,以后面对任何一个困难的项目,我想我都不会惧怕,并最终能够成功地将其完成。

感谢老师,感谢此次课程设计。虽然在其中吃了不少苦头,但我毫不后悔,因为我满载而归。

附录

#include <QtGui>

#include <QtNetwork>

#include "chatclient.h"

ChatClient::ChatClient(QWidget *parent)

: QWidget(parent){

QString gbkText;//中文化

leftLayout = new QVBoxLayout;

leftTopLayout = new QHBoxLayout;

gbkText = gbkText.fromLocal8Bit("聊天室IP:");

hostIpLabel = new QLabel(gbkText);

hostIpBox = new QLineEdit(tr("127.0.0.1"));

gbkText = gbkText.fromLocal8Bit("端口:");

hostPortLabel = new QLabel(gbkText);

hostPortBox = new QLineEdit(tr("7654"));

gbkText = gbkText.fromLocal8Bit("昵称:");

userLabel = new QLabel(gbkText);

userBox = new QLineEdit;

gbkText = gbkText.fromLocal8Bit("连接");

connectBtn = new QPushButton(gbkText);

leftTopLayout->addWidget(hostIpLabel);

leftTopLayout->addWidget(hostIpBox);

leftTopLayout->addWidget(hostPortLabel);

leftTopLayout->addWidget(hostPortBox);

leftTopLayout->addWidget(userLabel);

leftTopLayout->addWidget(userBox);

leftTopLayout->addWidget(connectBtn);

//左上结束

chatBox = new QTextEdit;

chatBox->setReadOnly(true);

gbkText = gbkText.fromLocal8Bit("欢迎使用简单聊天室-v0.2 -- Jason Lee @ fzu");

chatBox->setText(gbkText);

//左中

sendLineLayout = new QHBoxLayout;

gbkText = gbkText.fromLocal8Bit("关闭");

closeBtn = new QPushButton(gbkText);

gbkText = gbkText.fromLocal8Bit("发送");

sendBtn = new QPushButton(gbkText);

sendLineLayout->addWidget(closeBtn);

sendLineLayout->addWidget(sendBtn);

leftBottomLayout = new QVBoxLayout;

msgBox = new QLineEdit;

leftBottomLayout->addWidget(msgBox);

leftBottomLayout->addLayout(sendLineLayout);

//左下

leftLayout->addLayout(leftTopLayout);

leftLayout->addWidget(chatBox);

leftLayout->addLayout(leftBottomLayout);

//左边栏

rightLayout = new QVBoxLayout;

gbkText = gbkText.fromLocal8Bit("公告栏\n");

newsBox = new QTextEdit;

newsBox->append(gbkText);

gbkText = gbkText.fromLocal8Bit("Linux实验三");

newsBox->append(gbkText);

rightLayout->addWidget(newsBox);

gbkText = gbkText.fromLocal8Bit("历史用户列表:\n");

userListBox = new QListWidget;

userListBox->addItem(gbkText);

rightLayout->addWidget(userListBox);//右栏结束

midLayout = new QHBoxLayout;

midLayout->addLayout(leftLayout);

midLayout->addLayout(rightLayout);

topLayout = new QVBoxLayout;//菜单栏

menuBar = new QMenuBar();

fileMenu = menuBar->addMenu(tr("&File"));

gbkText = gbkText.fromLocal8Bit("保存聊天记录");

saveAct = fileMenu->addAction(tr("&save"));

connect(saveAct, SIGNAL(triggered()), this, SLOT(saveRecord()));

topLayout->addWidget(menuBar);

bottomLayout = new QHBoxLayout;// 如状态栏等

mainLayout = new QVBoxLayout;

mainLayout->addLayout(topLayout);

mainLayout->addLayout(midLayout);

mainLayout->addLayout(bottomLayout);

setLayout(mainLayout);

connect(connectBtn, SIGNAL(clicked()), this, SLOT(enterChatroom()));

connect(sendBtn, SIGNAL(clicked()), this, SLOT(sendMsg()));

connect(closeBtn, SIGNAL(clicked()), this, SLOT(close()));

}

ChatClient::~ChatClient(){

}

void ChatClient::closeEvent(QCloseEvent *event){//关闭窗口时调用

QString gbkTitle, gbkContent;

gbkTitle = gbkTitle.fromLocal8Bit("提醒:");

gbkContent = gbkContent.fromLocal8Bit("您确定关闭简单聊天室?");

QMessageBox::StandardButton ret;

ret = QMessageBox::warning(this, gbkTitle,gbkContent,

QMessageBox::Close | QMessageBox::Cancel);

if(ret == QMessageBox::Cancel) event->ignore();

}

void ChatClient::enterChatroom(){

QString gbkTitle, gbkContent;

if(this->hostIpBox->text() == "" || this->hostPortBox->text() == "" ||

this->userBox->text() == ""){

gbkTitle = gbkTitle.fromLocal8Bit("提醒:");

gbkContent = gbkContent.fromLocal8Bit("聊天室信息不完整!");

QMessageBox::warning(this, gbkTitle, gbkContent);

return;

}

hostAddr = this->hostIpBox->text();

port = this->hostPortBox->text().toInt();

nickname = this->userBox->text();

tcpSocket = new QTcpSocket(this);

tcpSocket->connectToHost(hostAddr, port);

connectBtn->setEnabled(false);

connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(receiveMsg()));///////

}

void ChatClient::sendMsg(){

QString gbkTitle, gbkContent;

if(this->msgBox->text() == ""){

gbkTitle = gbkTitle.fromLocal8Bit("提醒:");

gbkContent = gbkContent.fromLocal8Bit("信息不能为空!");

QMessageBox::warning(this, gbkTitle, gbkContent);

return;

}

QString msg = this->userBox->text() + ": " + this->msgBox->text();

tcpSocket->write(msg.toLocal8Bit(), msg.length());

msgBox->clear();

}

void ChatClient::receiveMsg(){

while(tcpSocket->bytesAvailable() > 0){

QByteArray datagram;

datagram.resize(tcpSocket->bytesAvailable());

tcpSocket->read(datagram.data(), datagram.size());

QString msg;

msg = msg.fromLocal8Bit(datagram.data());

chatBox->append(msg);

}

}

void ChatClient::saveRecord(){

saveRecords("chatRecords.txt");

}

void ChatClient::saveRecords(const QString &fileName){

QFile file(fileName);

if (!file.open(QFile::WriteOnly | QFile::Text)) {

QMessageBox::warning(this, tr("Application"),

tr("Cannot write file %1:\n%2.")

.arg(fileName).arg(file.errorString()));

return;

}

QTextStream out(&file);

out << chatBox->toPlainText();

}

相关推荐