《LINUX网络聊天室(共52页).docx》由会员分享,可在线阅读,更多相关《LINUX网络聊天室(共52页).docx(52页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、精选优质文档-倾情为你奉上淮海工学院计算机工程学院课程设计报告书课程名:Linux程序设计 题 目: 网络聊天室 班 级: 软件132班 成 员: XXXXXXX XXX 评语:成绩: 指导教师: 批阅时间: 年 月 日1.绪论1.1背景Linux是一套免费使用和自由传播的类Unix操作系统,是一个基于POSIX和UNIX的多用户、多任务、支持多线程和多CPU的操作系统。它能运行主要的UNIX工具软件、应用程序和网络协议。它支持32位和64位硬件。Linux继承了Unix以网络为核心的设计思想,是一个性能稳定的多用户网络操作系统。Linux操作系统诞生于1991 年10 月5 日(这是第一次正
2、式向外公布时间)。Linux存在着许多不同的Linux版本,但它们都使用了Linux内核。Linux可安装在各种计算机硬件设备中,比如手机、平板电脑、路由器、视频游戏控制台、台式计算机、大型机和超级计算机。严格来讲,Linux这个词本身只表示Linux内核,但实际上人们已经习惯了用Linux来形容整个基于Linux内核,并且使用GNU 工程各种工具和数据库的操作系统。1.2linux介绍基本思想Linux的基本思想有两点:第一,一切都是文件;第二,每个软件都有确定的用途。其中第一条详细来讲就是系统中的所有都归结为一个文件,包括命令、硬件和软件设备、操作系统、进程等等对于操作系统内核而言,都被视
3、为拥有各自特性或类型的文件。至于说Linux是基于Unix的,很大程度上也是因为这两者的基本思想十分相近。 完全免费Linux是一款免费的操作系统,用户可以通过网络或其他途径免费获得,并可以任意修改其源代码。这是其他的操作系统所做不到的。正是由于这一点,来自全世界的无数程序员参与了Linux的修改、编写工作,程序员可以根据自己的兴趣和灵感对其进行改变,这让Linux吸收了无数程序员的精华,不断壮大。 完全兼容POSIX1.0标准这使得可以在Linux下通过相应的模拟器运行常见的DOS、Windows的程序。这为用户从Windows转到Linux奠定了基础。许多用户在考虑使用Linux时,就想到
4、以前在Windows下常见的程序是否能正常运行,这一点就消除了他们的疑虑。 多用户、多任务Linux支持多用户,各个用户对于自己的文件设备有自己特殊的权利,保证了各用户之间互不影响。多任务则是现在电脑最主要的一个特点,Linux可以使多个程序同时并独立地运行。 良好的界面Linux同时具有字符界面和图形界面。在字符界面用户可以通过键盘输入相应的指令来进行操作。它同时也提供了类似Windows图形界面的X-Window系统,用户可以使用鼠标对其进行操作。在X-Window环境中就和在Windows中相似,可以说是一个Linux版的Windows。 支持多种平台Linux可以运行在多种硬件平台上,
5、如具有x86、680x0、SPARC、Alpha等处理器的平台。此外Linux还是一种嵌入式操作系统,可以运行在掌上电脑、机顶盒或游戏机上。2001年1月份发布的Linux 2.4版内核已经能够完全支持Intel 64位芯片架构。同时Linux也支持多处理器技术。多个处理器同时工作,使系统性能大大提高。2.需求分析2.1 功能需求:项目基本功能:1. 用户登录(不能重复登录)2. 用户注册(不能重复注册)3. 显示在线用户4. 好友上下线提醒5. 私聊6. 群聊用户注册用户输入用户名和密码,提交给服务器确认,根据确认的信息判断用户是否符合注册条件。如果用户名和密码在用户信息组中不存在的话,则注
6、册成功,跳转到首页。用户登录用户输入用户名和密码,提交给服务器确认,根据确认的信息判断用户是否符合登录条件。如果用户名和密码正确的话,则登录到模式选择界面。用户私聊用户成功进入聊天室后可选择单人聊天模式与在线的任一用户进行私聊,输入“用户名:”+私聊内容,enter键发送即可。用户群聊用户成功进入聊天室后可选择单人聊天模式与在线的任一用户进行私聊,输入“all:”+聊天内容,enter键发送即可。 查看在线用户用户进入聊天室后系统会显示在线用户的姓名。好友上下线提醒用户成功进入聊天室后,好友上线和下线时,会收到系统提醒。聊天记录和用户信息系统后台可以查看所有用户的聊天记录和用户信息。3.应用技
7、术3.1 TCP和UDP通信的概念3.1.1 UDP通信 UDP是用户数据报协议的简称。它是以中午连接的逻辑通信信道。UDP在传送数据之前不需要先建立连接,远地主机的传输层在收到udp数据报后,不需要给出任何确认,所以不能保证其交付时可靠。它的特点是:因无连接,故提供的是不可靠的信道,但也是因无连接而具有很好的传输效率。 3.1.2 TCP通信 TCP是传输控制协议的简称,它是提供一条全双工的、可靠的信道。TCP提供面向连接的服务,在传送数据之前必须先建立连接,数据传送结束后要释放连接。TCP不提供广播和多播服务。 由于TCP要提供可靠的、面向连接的运输服务,所以不可避免地增加了许多系统开销,
8、比如确认、流量控制、计时器以及连接管理等都需要占用许多系统的时空资源。 两个计算机之间如果使用TCP通信,其连接过程需要三次握手实现,如实验图1-1所示。图1-1 用三次握手建立TCP连接 对于两个计算机之间连接的释放过程也需要类似的3次握手的互相确认的过程,如实验图1-2所示。图1-2 TCP连接的释放过程3.2客户/服务器模型在客户/服务器模型中,多个相互通信的计算机都作为客户端,与网络服务器进行连接,并通过服务器进行信息的传递。所以多个客户端之间的通信就变为了客户端与服务端的通信。所以,采用客户/服务器模型进行网络聊天需要分别编写服务器端和客户端的程序,服务器和客户端之间相互通信的同步关
9、系和各自的程序流程如实验图1-3所示。图1-3 Socket通信流程图3.3网络套接字(socket)的概念Socket接口上TCP/IP网络应用程序接口(API),它提供了许多函数和例程,程序员可以使用它们来开发TCP/IP网络应用程序。使用Socket接口进行网络通信的过程如图1-3所示,简要步骤如下:(1) 建立一个Socket.(2) 按要求配置socket,将socket连接到远程主机或给socket指定以各本地协议端口。(3) 按要求通过socket发送和接受数据。(4) 关闭此socket。这是通过Socket实现点对点通信需要掌握的4个编程要点。3.4多线程的概念上述点对点通信
10、的实现知识完成了主机进程与服务器进程之间的连接,建立连接的进程之间是一对一的联系,即主机的一个进程与服务器的一个进程之间建立的连接。而每个进程进行通信的环节都包括了发送信息和接口信息两个任务,这两个任务通过一个端口地址发送和接收。 对于多个并发的任务需要创建多个线程或线程去实现。使用一个进程去完成发送信息是没有问题的,因为发送总是主动的;而使用同一个进程再去完成接受信息去不一定会成功,因为接受信息是被动的,所以当没有信息可以接收时,该进程就会被阻塞,从而导致发送任务也一起被阻塞。同一个端口的发送和接收是两个并发任务,应该由两个不同的任务去分别完成信息的发送和接收。这样,当接收信息任务因没有信息
11、而被阻塞时,不至于影响发送任务的执行。那么,发送和接收两个任务是使用两个进程还是两个进程去完成呢?在网络通信中,端口地址是以进程为单位进程分配的,而一个进程与外界的消息发送与接收必须通过分配给它的同一个端口进行。因此,不能通过创建进程方式来解决上诉问题,因为两个进程会分别对应两个不同的端口,而发送和接收必须使用同一端口。线程不是资源分配的单位,所以如果使用两个线程不会对线程分配新的端口。因此,本实验需要使用两个线程去分别完成发送和接收信息的任务,这两个线程共享其进程拥有的统一个端口地址。由于创建进程的进程本身会作为一个线程来调度,所以只需要再创建一个线程专门负责接收信息就可以了。因此,对于从每
12、个客户端发来的请求,服务器端都要创建相应的线程去接收并处理;同理,对于客户端而言,也要创建一个线程去读取服务器端发来的信息。 4.工作原理4.1 Linux提供的有关Socket的系统调用(1) Socket() 作用:socket函数为客户机或服务器创建一个sokcet格式:int socket(int family,int type,int protocol); 参数说明: Family:表示地址族,可以去AF_UNLX和AF_INT。其中,AF_UNLX只能够用于单一的UNIX系统进程间通信;AF_INT是针对Internet的,因而可以允许在远程主机之间通信,实验中使用AF_INT。
13、Type:网络程序所采用的通信协议,可以取SOCK_STREAM或SOCK_DGRAM。其中,SOCK_STREAM表明使用的是TCP协议,这样提供按顺序的、可靠的、双向、面向连接的比特流;SOCKE_DGRAM表明使用的是UDP协议,这样只会提供定长、不可靠、无连接的通信。(2) bind( )格式: int bind(int sockfd,struct sockaddr *addr,int addrlen); 参数说明: Sockfd:socket的文件描述符号。 Sockaddr:表示名字所用的一个数据结构,用来保存地址(包括IP地址和端口) Addrlen:设置结构大小长度。(3) l
14、isten()格式: int listen(int sockfd, int backlog); 作用:监听连接信号,和accepted函数合同。 参数说明: Sockfd:表示socket调用返回的文件描述符。 Backlog:表示接入队列允许的连接数目,大多数系统允许20个,也可以子定义510个。(4) accept() 格式: Int accept (int sockfd, void *addr, int *addrlen); 作用:与listen函数合用,监听信息、接收客户端请求。 参数说明: Sockfd:表示socket的文件描述符。 Addr:表示指向局部的数据结构struct s
15、ockaddr-in的指针。 Addrlen:表示地址的长度。(5) connect()格式: int connect( int sockfd , struct sockaddr *serv_addr , int addrlen); 作用:在面向连接的系统中客户及连接服务器时使用,connect必须在bind后使用。 参数作用: Sockfd:表示socket的文件描述符。 Serv-addr:表示村访目的端口和ip地址(套接字)的数据结构。(6) send() 和 recv() 格式1: Int send (int sockfd, const vod *msg,int len, int fl
16、ags); 功能:发送信息。 格式2: Int recv (int sockfd , void *buf,int len, usigned int flags); 作用:用于流式socket、数据报socket内部之间的通信。(7) close( ) 和 shutdown ( )格式: Close( int sockfd)或 Int shutdown(int sockfd , int how); 参数说明: How的值为下面一种: 0-不允许继续接收; 1-不允许继续发送; 2-不允许继续发送和接收。(8) 有关线程的系统调用函数pthread_create()、pthread_join()4
17、.2 实验过程说明(使用TCP/IP)(1) 监听连接 利用socket、bind、listen建立连接,步骤是:1) 先用socket函数初始化socket,创建新的sockfd。Sockfd = socket(AF_INT,SOCK_STREAM,0)2) 此步骤涉及到IP地址及其处理过程。参数说明: inet_addr 函数 INADDR_ANY该函数把由小数点分开的十进制IP地址转为unsinged long 类型,而在实验中所使用的为INADDR_ANY,使用利用自已的IP地址自动填充。a) 利用bind函数绑定端口和IP地址。My_addr.sin_family=AF_INET;
18、/*将地址族类型设定好 */My_addr.sin_port=htons(MYPORT; /* 将端口给其赋值*/My_addr.sin_addr.s_addr=INADDR_ANY; /*用连接地址自动填充ip*/Bind(sockfd,(stuct sockaddr*)&my_addr,sizeof(stuct sockaddr);/*sockfd 是分配的socket名字,my-addr则便是分配好的端口与IP,用bind绑定*/b) 利用listen监听请求(2) 发送请求1)利用gethostbyname获取主机信息。2)初始化socket端口。3)利用connect函数将自己的IP
19、地址等信息发送到主机,等待主机调用accept函数来接受请求。(3) 主机接收请求,进行数据通信1)主机利用accept接收请求。2)创建子进程,显示欢迎信息;3)接收返回信息,显示连接成功,并推出连接;4)关闭客户端口socket;5)关闭服务端socket,结束子线程。 5.系统设计程序测试环境:linux操作系统。测试软件:SecureCRT(1) 在编写完TCP服务端程序server.c后,用 gcc server.co server wall-lpthread 生成程序server。(2) 在编写完TCP客户端程序client.c后,用gcc client.c o client wa
20、ll 生成程序client(3) 在主机上打开一窗口,运行server。(4) 再打开若干个窗口(或者在另一个主机上打开),运行client 6.详细设计与实现 6.1主要功能实现如下6.1.1 登陆模块 客户端发送标志位2,账号,密码到服务器服务器接收到帐号密码,与之前设定好的进行匹配匹配是否成功成功失败登录成功 6.1.2 注册模块 客户端发送标志位2,账号,密码到服务器服务器接收到帐号密码,与之前设定好的进行匹配匹配是否存在在功存在 不存在注册成功6.1.3 私聊模块客户端发送标志位4(私聊对象名字)私聊内容服务器遍历在线链表,找到私聊对象,把私聊内容发给私聊对象私聊对象接收私聊内容6.
21、1.3 群聊模块客户端发送标志位4(all)群聊内容服务器遍历在线链表,找到所有在线对象,把私聊内容发给所有在线对象所有在线对象接收群聊内容6.1.3 显示在线用户服务器接受客户端登陆请求服务器遍历在线链表,找到所有在线对象,把所有在线对象名字发给当前登陆对象当前登陆对象接收所有在线人姓名6.1.3 好友上下线提醒服务器接受客户端上下线请求服务器遍历链表,找到上下线对象,把上下线对象名字发给当前所有在线对象当前所有在线对象接收上下线人姓名7. 主要代码 7.1 fun.h 所有服务器的头文件及出现的函数struct user /用户名和密码 char name20; char code32;
22、int flag; struct user* next;struct msg /在线用户的信息结构体(时时更新) int fd; /拥有者的服务器端的连接套接字 char from_name20; /信息的拥有者 char to_name20; /信息的读者 char info1024; /拥有者 时间 char message1024; /信息内容 struct msg *next;7.2 server.c #include #include #include #include #include #include #include #include #include #include #in
23、clude #include #include #include #include #include #include #include #include #include #include fun.h#define LISTEN_QUEUE_NUM 5#define BUFFER_SIZE 1024#define ECHO_PORT 2029struct user client; /用户名和密码。struct msg *head; /在线用户的链表头 /struct user *h; /已注册用户信息的链表头 pthread_mutex_t mutex_1; /互斥锁,用于在线用户链表/聊天
24、记录文件/删除指定用户(描述符为fd)void delete_user (struct msg *head, int fd) struct msg *p, *s; for(p = head; p != NULL; p = p-next) if(p-next-fd = fd) s = p-next; p-next = s-next; free(s); break; /线程执行函数void thread_routine (void *param) int new_sock = *(int *)param); /连接套接字 FILE *logsfp; int byteread; char bufBU
25、FFER_SIZE; char onlinenameBUFFER_SIZE; /在线用户列表 int onlinefd; /发送给客户端的连接套接字 struct timeval timeout; /select等待时间 fd_set readset, r_readset; int maxfd; int nfound; /截取名字 int k; int i; /循环变量 char *getname20; char *q; struct msg msgs; struct msg *s, *p; struct msg tmp; /退出信息 getname0 = (char *)malloc(40)
26、; getname1 = (char *)malloc(40); getname2 = (char *)malloc(40); getname3 = (char *)malloc(40); getname4 = (char *)malloc(40); getname5 = (char *)malloc(40); getname6 = (char *)malloc(40); getname7 = (char *)malloc(40); /除了all,若群聊:只能和=7个人聊 /发送当前的套接字 onlinefd = new_sock; if(send(new_sock, &onlinefd, s
27、izeof(int), 0) from_name, client.name); s-fd = new_sock; s-next = head-next; head-next = s; /向当前用户发送消息“在线用户列表” bzero(onlinename, sizeof(onlinename); strcpy(onlinename, 在线的人:); for(p = head-next; p != NULL; p = p-next) strcat(onlinename, p-from_name); strcat(onlinename, ); send(new_sock, onlinename,
28、sizeof(onlinename), 0); /向其他用户发送信息“当前用户上线了” sprintf(tmp.message, %s 03332;1m上线了,您可以和他/她聊天了0330mn, client.name); for(p = head-next; p != NULL; p = p-next) if(p-fd != new_sock) if(send(p-fd, &tmp, sizeof(struct msg), 0) 0) perror(send : ); pthread_mutex_unlock(&mutex_1); FD_ZERO(&readset); FD_SET(new_
29、sock, &readset); maxfd = new_sock + 1; while(1) r_readset = readset; timeout.tv_sec = 0; timeout.tv_usec = ; /io复用 if(nfound = select(maxfd, &r_readset, (fd_set *)0, (fd_set *)0, &timeout) 0) perror(select); continue; else if(nfound = 0) continue; if(FD_ISSET(new_sock, &r_readset) /接收消息 byteread = r
30、ecv(new_sock, &msgs, sizeof(msgs), 0); if(byteread 0) /recv出错 perror(recv - :); break; else if(byteread = 0) /连接中止,发送下线信息 break; /若没“:”,无法处理,直接下一次。 if(strrchr(msgs.message, :) = NULL) continue; /截取聊天信息中的用户名和内容 k = 0; strcpy(getnamek, strtok(msgs.message, :); k+; while(q = strtok(NULL, :) strcpy(getn
31、amek, q); k+; strcpy(msgs.message, getnamek - 1);pthread_mutex_lock(&mutex_1); /记录聊天情况 if(logsfp = fopen(log.txt, a) = NULL) perror(open file : ); for(i = 0; i next; p != NULL; p = p-next) if(strcmp(getnamei, p-from_name) = 0) send(p-fd, &msgs, sizeof(msgs), 0); /向所有人发送消息 if(strcmp(getname0, all) =
32、0) for(p = head-next; p != NULL; p = p-next) if(strcmp(msgs.from_name, p-from_name) != 0) send(p-fd, &msgs, sizeof(msgs), 0); fclose(logsfp);pthread_mutex_unlock(&mutex_1); /end if /end whilepthread_mutex_lock(&mutex_1); /清理用户(下线的) delete_user(head, new_sock); close(new_sock); /发送下线消息 sprintf(tmp.message, %s 下线了n, msgs.from_name); for(p = head-next; p != NULL; p = p-next)