linux下多人聊天室.docx
《linux下多人聊天室.docx》由会员分享,可在线阅读,更多相关《linux下多人聊天室.docx(30页珍藏版)》请在冰豆网上搜索。
linux下多人聊天室
南京工程学院
嵌入式系统开发
课程设计
专业:
计算机科学与技术(嵌入式)
班级:
计算机091
学号:
202090134
姓名:
陈之魁
设计题目:
局域网多人聊天室
开发日期:
2012年12
1.绪论
1.1研究背景
在网络无所不在的今天,在Internet上,有ICQ、MSN、Gtalk、OICQ等网络聊天软件,极大程度上方便了处于在世界各地的友人之间的相互联系,也使世界好像一下子缩小了,不管你在哪里,只要你上了网,打开这些软件,就可以给你的朋友发送信息,不管对方是否也同时在线,只要知道他有号码。
Linux操作系统作为一个开源的操作系统被越来越多的人所应用,它的好处在于操作系统源代码的公开化!
只要是基于GNU公约的软件你都可以任意使用并修改它的源代码。
但对很多习惯于Windows操作系统的人来说,Linux的操作不够人性化、交互界面不够美观,这给Linux操作系统的普及带来了很大的阻碍。
因此制作一个Linux操作系统下的拥有人性化界面的实时通讯工具,将给那些刚刚接触Linux操作系统的用户带来极大的方便,而且通过设计这样的一个应用程序还能更好的学习网络编程知识和掌握LINUX平台上应用程序设计开发的过程,将大学四年所学知识综合运用,以达到检验学习成果的目的
Linux是一种针对PC计算机和工作站的操作系统,它具有像Windows和Mac那样的功能齐全的图形用户界面(GUI,GraphicalUserInterface)。
LinusTorvald和其它的遍布世界各地的编程人员共同开发的。
作为一种操作系统,它具有与Unix,Mac,Windows和WindowsNT同样的功能。
提到Linux我们不能不提GNU和Unix。
RichardM.Stallman建立的自由软件联盟出版了两种许可证,GNU通用公共许可证(GNUGneralPublicLicense,GPL)和GNU函数库通用公共许可证(GNULibraryGneralPublicLicense,LGPL)。
大部分GNU工程的软件和文档是以GNU通用公共许可证发行的,但是有一些库是以GNU函数库通用公共许可证发行的。
按照GNU通用公共许可证的规定,Linux的源代码可以自由获取,这满足了我们学习该系统的强烈愿望。
GPL充分体现了Stallman的思想:
只要用户所做的修改是同等自由的,用户可以自由地使用、拷贝、查询、重用、修改甚至发布这个软件。
通过这种方式,GPL保证了Linux(以及同一许可证下的大量其他软件)不仅现在自由可用,而且皮后经过任何修改这后都仍然可以自由使用。
Unix是由AT—T贝尔实验室的KenThompson和DennisRitchie于1969年在一台已经废弃了的PDP-7上开发的;最初它是一个用汇编语言写成的单用户操作系统。
后来,他们又在PDP-11上用C语言重新编写(发明C语言的部分目的就在于此),把Unix做成为了一个文本处理系统,这使Unix在贝尔实验室得到广泛的应用。
Unix的最初版本免费提供给许多知名的大学的计算机系使用。
加州大学伯克利分校的计算机系就是其中的一名,并地Unix进行了修改增加了许多新的特点,这就是主为人知的BSC版本的Unix。
与此同时,其它独立开发的Unix版本也开始萌生。
Unix不断发展了,各种版本被应用到不同的计算机使用。
而Linux最初是专门为基于Intel的个人计算机设计的。
(1)Linux的昨天
1991年,一名叫LinusTorvalds的芬兰大学生对Unix各种版本对于80386类的机器的脆弱支持十分不满,他决定要开发出一个全功能的、支持POSIX标准的、类Unix的操作系统内核,该系统吸收了BSD和SystemV的优点,同进摒弃了它们的缺点。
他独立把这个内核开发到0.02版,这个版本已经可以运行gcc、bash和很少的一些应用程序。
后来,他又开始了在因特网上寻求广泛的帮助。
1994年,Linux已经升级到1.0版本。
它的源代码量也呈指数形式增长,实现了基本的TCP/IP功能,此时Linux已经拥有大约10万的用户。
(2)Linux的今天
作为一各服务器级的操作系统,Linux已经成熟了。
现在的Linux内核由150多行代码组成,能作为Web服务器平台,也为越来越多的商业用户提供文件和打印服务。
它既被当作邮件服务器的一种候选平台,也被当作一种强壮而安全的防火墙。
Linux的企业级特性,比如支持多处理器、支持大型文件系统、日志文件系统以及密集型计算和高可用性集群技术,也逐步成熟。
桌面上的Linux也在继续完善。
KDE桌面提供的图形用户界面在易用性和可配置方面都能和微软的Windows相媲美。
(3)Linux的明天
Linux最强大的生命力在于其公开的开发过程。
每个人都有可以自由获取内核源程序,每个人都有要不得以运载源程序加以修改,而后他人也可以自由获取你修改后的源程序。
Linux这种独特的自由流畅的开发模型已被命名为bazaar(集市模型)。
Bazaar开发模型通过重视实验,征集并充分利用早期的反馈,对巨大数量的脑力资源进行平衡配置,可以开发出更优秀的软件。
本联盟就是想通过bazaar开发模型,在网上召集一些Linux的爱好者,开发出更优秀的操作系统或软件。
2.1功能需求:
项目基本功能:
1.用户注册
2.用户登录
3.不能重复登录
4.显示在线用户
5.私聊
6.群聊
7.创建管理员
8.管理员踢出用户
9.管理员禁言用户
10.传递文件
用户登录
用户输入用户名和密码,提交给服务器确认,根据确认的信息判断用户是否符合登录条件。
如果用户名和密码正确的话,则登录到聊天界面;如果还没有账号,可以先进行注册,然后便可进入聊天室。
用户注册
用户在还没有账号的情况下,先进入到注册界面,填写完完整的姓名及密码信息后便可获得账号,随后就可进入到聊天室。
用户查看在线用户
用户进入聊天室后可按查看在线用户按键即可查看当前有哪些用户在线
用户私聊
用户成功进入聊天室后可选择与在线的任一非自己用户进行私聊,选择私聊选项,输入对方姓名后即可输入私聊内容,对方即可看到
用户群聊
用户成功进入到聊天界面后可以和所有进入到聊天界面的用户进行公共聊天,此时聊天的内容每一个用户都可以看到。
管理员用户功能
踢出用户
管理员可以选择把在线的任一非自己用户踢出聊天室
禁言用户
管理员可以选择让在线的任一非自己用户只能接收信息而不能私聊、群聊
退出
用户选择退出选项即可退出聊天室
3.应用技术
3.1TCP和UDP通信的概念
3.1.1UDP通信
UDP是用户数据报协议的简称。
它是以中午连接的逻辑通信信道。
UDP在传送数据之前不需要先建立连接,远地主机的传输层在收到udp数据报后,不需要给出任何确认,所以不能保证其交付时可靠。
它的特点是:
因无连接,故提供的是不可靠的信道,但也是因无连接而具有很好的传输效率。
3TCP通信
TCP是传输控制协议的简称,它是提供一条全双工的、可靠的信道。
TCP提供面向连接的服务,在传送数据之前必须先建立连接,数据传送结束后要释放连接。
TCP不提供广播和多播服务。
由于TCP要提供可靠的、面向连接的运输服务,所以不可避免地增加了许多系统开销,比如确认、流量控制、计时器以及连接管理等都需要占用许多系统的时空资源。
两个计算机之间如果使用TCP通信,其连接过程需要三次握手实现,如实验图1-1所示。
图1-1用三次握手建立TCP连接
对于两个计算机之间连接的释放过程也需要类似的3次握手的互相确认的过程,如实验图1-2所示。
图1-2TCP连接的释放过程
客户/服务器模型
在客户/服务器模型中,多个相互通信的计算机都作为客户端,与网络服务器进行连接,并通过服务器进行信息的传递。
所以多个客户端之间的通信就变为了客户端与服务端的通信。
所以,采用客户/服务器模型进行网络聊天需要分别编写服务器端和客户端的程序,服务器和客户端之间相互通信的同步关系和各自的程序流程如实验图1-3所示。
图1-3Socket通信流程图
网络套接字(socket)的概念
Socket接口上TCP/IP网络应用程序接口(API),它提供了许多函数和例程,程序员可以使用它们来开发TCP/IP网络应用程序。
使用Socket接口进行网络通信的过程如图1-3所示,简要步骤如下:
(1)建立一个Socket.
(2)按要求配置socket,将socket连接到远程主机或给socket指定以各本地协议端口。
(3)按要求通过socket发送和接受数据。
(4)关闭此socket。
这是通过Socket实现点对点通信需要掌握的4个编程要点。
多线程的概念
上述点对点通信的实现知识完成了主机进程与服务器进程之间的连接,建立连接的进程之间是一对一的联系,即主机的一个进程与服务器的一个进程之间建立的连接。
而每个进程进行通信的环节都包括了发送信息和接口信息两个任务,这两个任务通过一个端口地址发送和接收。
对于多个并发的任务需要创建多个线程或线程去实现。
使用一个进程去完成发送信息是没有问题的,因为发送总是主动的;而使用同一个进程再去完成接受信息去不一定会成功,因为接受信息是被动的,所以当没有信息可以接收时,该进程就会被阻塞,从而导致发送任务也一起被阻塞。
同一个端口的发送和接收是两个并发任务,应该由两个不同的任务去分别完成信息的发送和接收。
这样,当接收信息任务因没有信息而被阻塞时,不至于影响发送任务的执行。
那么,发送和接收两个任务是使用两个进程还是两个进程去完成呢?
在网络通信中,端口地址是以进程为单位进程分配的,而一个进程与外界的消息发送与接收必须通过分配给它的同一个端口进行。
因此,不能通过创建进程方式来解决上诉问题,因为两个进程会分别对应两个不同的端口,而发送和接收必须使用同一端口。
线程不是资源分配的单位,所以如果使用两个线程不会对线程分配新的端口。
因此,本实验需要使用两个线程去分别完成发送和接收信息的任务,这两个线程共享其进程拥有的统一个端口地址。
由于创建进程的进程本身会作为一个线程来调度,所以只需要再创建一个线程专门负责接收信息就可以了。
因此,对于从每个客户端发来的请求,服务器端都要创建相应的线程去接收并处理;同理,对于客户端而言,也要创建一个线程去读取服务器端发来的信息。
Linux提供的有关Socket的系统调用
(1)Socket()
作用:
socket函数为客户机或服务器创建一个sokcet
格式:
intsocket(intfamily,inttype,intprotocol);
参数说明:
Family:
表示地址族,可以去AF_UNLX和AF_INT。
其中,AF_UNLX只能够用于单一的UNIX系统进程间通信;AF_INT是针对Internet的,因而可以允许在远程主机之间通信,实验中使用AF_INT。
Type:
网络程序所采用的通信协议,可以取SOCK_STREAM或SOCK_DGRAM。
其中,SOCK_STREAM表明使用的是TCP协议,这样提供按顺序的、可靠的、双向、面向连接的比特流;SOCKE_DGRAM表明使用的是UDP协议,这样只会提供定长、不可靠、无连接的通信。
(2)bind()
格式:
intbind(intsockfd,structsockaddr*addr,intaddrlen);
参数说明:
Sockfd:
socket的文件描述符号。
Sockaddr:
表示名字所用的一个数据结构,用来保存地址(包括IP地址和端口)
Addrlen:
设置结构大小长度。
(3)listen()
格式:
intlisten(intsockfd,intbacklog);
作用:
监听连接信号,和accepted函数合同。
参数说明:
Sockfd:
表示socket调用返回的文件描述符。
Backlog:
表示接入队列允许的连接数目,大多数系统允许20个,也可以子定义5~10个。
(4)accept()
格式:
Intaccept(intsockfd,void*addr,int*addrlen);
作用:
与listen函数合用,监听信息、接收客户端请求。
参数说明:
Sockfd:
表示socket的文件描述符。
Addr:
表示指向局部的数据结构structsockaddr-in的指针。
Addrlen:
表示地址的长度。
(5)connect()
格式:
intconnect(intsockfd,structsockaddr*serv_addr,intaddrlen);
作用:
在面向连接的系统中客户及连接服务器时使用,connect必须在bind后使用。
参数作用:
Sockfd:
表示socket的文件描述符。
Serv-addr:
表示村访目的端口和ip地址(套接字)的数据结构。
(6)send()和recv()
格式1:
Intsend(intsockfd,constvod*msg,intlen,intflags);
功能:
发送信息。
格式2:
Intrecv(intsockfd,void*buf,intlen,usignedintflags);
作用:
用于流式socket、数据报socket内部之间的通信。
(7)close()和shutdown()
格式:
Close(intsockfd)
或
Intshutdown(intsockfd,inthow);
参数说明:
How的值为下面一种:
0----不允许继续接收;
1----不允许继续发送;
2---不允许继续发送和接收。
(8)有关线程的系统调用函数pthread_create()、pthread_join()
实验过程说明(使用TCP/IP)
(1)监听连接
利用socket、bind、listen建立连接,步骤是:
1)先用socket函数初始化socket,创建新的sockfd。
Sockfd=socket(AF_INT,SOCK_STREAM,0)
2)此步骤涉及到IP地址及其处理过程。
参数说明:
inet_addr函数INADDR_ANY
该函数把由小数点分开的十进制IP地址转为unsingedlong类型,而在实验中所使用的为INADDR_ANY,使用利用自已的IP地址自动填充。
a)利用bind函数绑定端口和IP地址。
My_addr.sin_family=AF_INET;/*将地址族类型设定好*/
My_addr.sin_port=htons(MYPORT;/*将端口给其赋值*/
My_addr.sin_addr.s_addr=INADDR_ANY;/*用连接地址自动填充ip*/
Bind(sockfd,(stuctsockaddr*)&my_addr,sizeof(stuctsockaddr));
/*sockfd是分配的socket名字,my-addr则便是分配好的端口与IP,用bind绑定*/
b)利用listen监听请求
(2)发送请求
1)利用gethostbyname获取主机信息。
2)初始化socket端口。
3)利用connect函数将自己的IP地址等信息发送到主机,等待主机调用accept函数来接受请求。
(3)主机接收请求,进行数据通信
1)主机利用accept接收请求。
2)创建子进程,显示欢迎信息;
3)接收返回信息,显示连接成功,并推出连接;
4)关闭客户端口socket;
5)关闭服务端socket,结束子线程。
(1)
程序测试环境:
linux操作系统。
测试软件:
vmware虚拟机
(1)在编写完TCP服务端程序server.c后,用gcc–lpthread–oserver.cserver生成程序server。
(2)在编写完TCP客户端程序client.c后,用gcc–lpthread–oclient.cclient生成程序client
(3)在主机上打开一窗口,运行server。
(4)再打开若干个窗口(或者在另一个主机上打开),运行client,输入服务器的IP地址,并检查器结果的正确性。
输入:
【主】#./server
【从】#./client192.168.11.44(127.0.0.1为本机的ip地址)
(5)客户端之间实现私聊,群聊,管理员踢人,禁言。
6.1主要功能实现如下
.1登陆模块
失败
成功
.2查看在线用户
.3私聊模块
.4群聊信息
7.主要代码
common.h所有服务器的头文件及出现的函数
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#defineBUF_SIZE1024
#defineNO_ERROR0
#defineSOCK_ERROR1
#defineOPEN_ERROR2
#defineOTHER_ERROR3
//#defineOFF0
//#defineON1
#defineSEND0
#defineLOGIN1
#defineVIEW2
#defineQUIT3
typedefstruct{
charname[20];
intfd;
}ONLINE;
typedefstruct{
intflag;
charbuffer[BUF_SIZE];
charonline[20][20];
intonline_num;
charsend_name[20];
charrecv_name[20];
charpassword[20];
}MESSAGE;
typedefstruct{
charname[20];
charpassword[20];
}USER;
7.2
#include"common.h"
#definePORT_NUMBER8888
ONLINEonline_list[20];
USERuserinfo_list[20];
/*-------------------------------------------------------------------*/
staticvoid
RemovePeopleFromOnlineList(intfd)
{
inti;
for(i=0;i<20;i++){
if(online_list[i].fd==fd){
online_list[i].fd=-1;
memset(online_list[i].name,0,20);
break;
}
}
}
/*-------------------------------------------------------------------*/
staticvoid
AddPerpleToOnlineList(char*name,intfd)
{
inti;
for(i=0;i<20;i++){
if(online_list[i].fd<0){
online_list[i].fd=fd;
strcpy(online_list[i].name,name);
break;
}
}
}
/*-------------------------------------------------------------------*/
staticvoid
Login(MESSAGEmesg,intfd)
{
inti;
charbuffer1[32];
charbuffer2[32];
strcpy(buffer1,"Logins");
strcpy(buffer2,"Loginf");
for(i=0;i<20;i++){
if(0==strcmp(online_list[i].name,mesg.send_name)){
write(fd,buffer2,sizeof(buffer2));
return;
}
}
for(i=0;i<20;i++){
if(0==strcmp(userinfo_list[i].name,mesg.send_name)){
if(0==strcmp(userinfo_list[i].password,mesg.password)){
AddPerpleToOnlineList(mesg.send_name,fd);
printf("User%sisonline!
\n",mesg.send_name);
write(fd,buffer1,sizeof(buffer1));
return;
}
}
}
write(fd,buffer2,sizeof(buffer2));
}
/*-------------------------------------------------------------------*/
staticvoid
SendOnlineMessage(MESSAGEmesg,intfd)
{
inti;
intj=0;
for(i=0;i<20;i++){
if(online_list[i].fd>0){
strcpy(mesg.online[j++],online_list[i].name);
}
}
mesg.online_num=j;
write(fd,(char*)&mesg,sizeof(MESSAGE));
}
/*-------------------------------------------------------------------*/
staticvoid
SendMessage(MESSAGEmesg,constint*client,intfd)
{
inti;
for(i=0;i<20;i++){
if(0==strcmp(mesg.recv_name,online_list[i].name))