Linux_基于socket的局域网聊天软件的设计与实现
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
提交日期:2012-06-20 基于socket的局域网聊天软件的设计与实现1.实验目的
《Linux操作系统课程设计B》是一门在课程《Linux操作系统与程序设计B》后独立开设的实验课程。这一门实验课程的开设目的是为了通过学生独立完成一个基于Linux平台的较大型应用程序,巩固课堂上学到的Linux平台上的编程规范、技术和技巧,培养学生的编写较大型程序的能力和提高学生综合应用素质。
本课程设计实验主要围绕Linux平台上主流的基础技术展开,这些技术包括:Linux的进程、线程通信和同步技术;socket网络通信技术等,这些技术可以集中体现并应用在并发程序设计中。通过并发程序的设计与开发,培养学生底层软件开发的能力,并为将来从事UNIX/Linux平台开发、嵌入式开发等相对高端的软件开发工作打下基础。
2.软件功能及模块划分
本软件是一个linux下基于socket的聊天室程序,能让局域网内的用户通过该软件进行简单的文字通信。在此基础上增加了
1.聊天室成员之间的发送私聊信息;
2.当新的成员加入后能自动收取最近一段时间内的聊天上下文;
3.用户能够查看历史聊天记录;
4.软件界面基于Qt实现,图形化界面方便用户操作。
主要模块划分:
服务端:
数据包发送和接受模块,聊天记录数据库读写模块,数据包处理模块,聊天记录查询模块
客户端:
数据包发送和接受模块,数据包处理模块,聊天记录查询模块,用户界面与展示模块
3.设计与实现
3.1系统概述与总体结构
本系统采用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文件中
[系统总体结构]
由上图可以看出,服务器和客户端程序在总体结构上相似。服务器和客户端的全部功在数据处理模块中实现,这也是整个程序的核心之处
由于数据包的接受在单独的线程中完成,而界面采用Qt实现。因此在接受线程中采用QCoreApplication::postEvent()方法向界面线程发送通知事件,在界面窗口中通过重载customEvent()方法响应该通知,然后从Server中获取数据并显示。整个过程中涉及到线程同步和多线程安全问题,觉采用信号量和互斥量解决。
3.2服务端的实现
3.2.1服务总体实现与概述
服务端的全部功能在类Server中实现,由于在整个系统中有且只有一个服务端出现,应此该类采用单例模式实现,通过Server::Instance()方法获得Server的一个实例,通过调用Init()函数完成服务端的初始化(包括信号量的初始化、数据库的连接、设置要发送事件通知的界面对象Object*),最后通过Start()方法创建socket,绑定端口,创建数据包接受线程,至此,完成服务器的启动
部分代码:
class Server
{
friend class ServerPacketProcessor;
public:
virtual ~Server();
static Server* Instance();
bool Init(QObject *mainWindow);
bool Start();
void WaitForServerStop();
protected:
Server();
void* ThreadStartRoutine(void* arg);
private:
static void* ThreadRoutineTransponder(void* arg);
void postEvent(QEvent::Type eventType);
static Server* m_server;
sockaddr_in m_localaddr;
int m_sockfd;
pthread_t m_thread;
bool bCancel;
pthread_mutex_t m_mutex_clients;
pthread_mutex_t m_mutex_msgs;
QObject* m_ui;
。。。。。。
Server.cpp
#include "server.h"
Server* Server::m_server=0;
bool Server::Init(QObject* mainWindow)
{
m_ui=mainWindow;
bCancel=false;
pthread_mutex_init(&m_mutex_clients,0);
pthread_mutex_init(&m_mutex_msgs,0);
return true;
}
Server* Server::Instance()
{
if(m_server==0)
m_server=new Server();
return m_server;
}
bool Server::Start()
{
m_sockfd=socket(AF_INET,SOCK_DGRAM,0);
if(m_sockfd==-1)
{
cerr<<"create udp socket failed!"< return false; } m_localaddr.sin_port=htons(SRV_PORT); m_localaddr.sin_family=AF_INET; m_localaddr.sin_addr.s_addr=htonl(INADDR_ANY); if(-1==bind(m_sockfd,(struct sockaddr*)&m_localaddr,sizeof(m_localaddr))) { cerr<<"bind socket failed!"< return false; } if(!m_chatRecordMgr.ConnectDB("canghai","canghai","chat_record_schema")) { cerr<<"server connect datebase error!"< return false; } if(0!=pthread_create(&m_thread,0,ThreadRoutineTransponder,0)) {