admin 管理员组

文章数量: 887021


2023年12月22日发(作者:weight的其他形式)

.-

青 岛 科 技 大 学

操 作 系 统 课 程 设 计

学生班级 计算机132班

学生学号 **********

学生姓名 王永远

18

5

月 _________2016 年 ___日

.-

目录

设计题目:聊天室系统 .................................................................................................................................... 3

一、设计目的及要求 ..................................................................................................................................... 3

1.1设计目的 ................................................................................................................................................... 3

1.2设计要求: ............................................................................................................................................... 3

二、技术背景 ................................................................................................................................................. 3

2.1网络编程 ................................................................................................................................................... 3

2.2网络通信 ................................................................................................................................................... 3

2.3 Socket ........................................................................................................................................................ 3

2.4 TCP传输................................................................................................................................................... 4

2.5 Java的多线程机制 ................................................................................................................................... 4

三、需求分析 ................................................................................................................................................. 4

3.1客户端 ....................................................................................................................................................... 4

3.1.1登录 ................................................................................................................................................ 4

3.1.2多人聊天 ........................................................................................................................................ 4

3.1.3单人聊天 ........................................................................................................................................ 5

3.2服务器端 ................................................................................................................................................... 5

四、总体设计 ................................................................................................................................................. 5

4.1设计思想流程图 ....................................................................................................................................... 5

4.2设计思想分析 ........................................................................................................................................... 6

五、详细设计 ................................................................................................................................................. 6

5.1客户端设计 ............................................................................................................................................... 7

5.1.1登录界面 ........................................................................................................................................ 7

5.1.2聊天室界面 .................................................................................................................................... 7

5.1.3单人聊天界面 ................................................................................................................................ 7

5.2服务器端设计 ........................................................................................................................................... 8

六、系统测试 ................................................................................................................................................. 8

6.1登录测试 ................................................................................................................................................... 8

6.1.1用户名为英文字母 ........................................................................................................................ 8

6.1.2用户名为中文 ................................................................................................................................ 9

6.1.3用户名为标点符号 ........................................................................................................................ 9

6.1.4多个用户有重名 .......................................................................................................................... 10

6.1.5服务器地址不合法 ...................................................................................................................... 11

6.1.6端口号不合法 .............................................................................................................................. 12

6.1.7服务器未开启 .............................................................................................................................. 12

6.2群发消息测试 ......................................................................................................................................... 13

6.3私聊测试 ................................................................................................................................................. 15

6.4用户列表显示测试 ................................................................................................................................. 16

七、心得体会 ............................................................................................................................................... 16

八、参考资料 ............................................................................................................................................... 16

九、程序清单 ............................................................................................................................................... 16

.-

设计题目:聊天室系统

一、设计目的及要求

1.1设计目的

通过该聊天室系统,掌握网络编程的概念及基于网络的C/S模式软件系统开发,掌握基于TCP协议的Socket编程,掌握Java的多线程机制。

1.2设计要求:

实现多个用户之间类似于QQ的聊天程序,有聊天界面,多用户之间既可以实现群聊,也可以单独聊天。

二、技术背景

2.1网络编程

就是用来实现网络互连的不同计算机上运行的程序间可以进行数据交换。

2.2网络通信

(1)IP地址:网络中设备的标识,不易记忆,可用主机名

要想让网络中的计算机能够互相通信,必须为每台计算机指定一个标识号,通过这个标识号来指定要接受数据的计算机和识别发送的计算机,在TCP/IP协议中,这个标识号就是IP地址。

(2)端口号:用于标识进程的逻辑地址

物理端口 网卡口

逻辑端口 我们指的就是逻辑端口

A:每个网络程序都会至少有一个逻辑端口

B:用于标识进程的逻辑地址,不同进程的标识

C:有效端口:0~65535,其中0~1024系统使用或保留端口。

(3)传输协议:通信的规则,常见的有UDP,TCP

UDP:将数据源和目的封装成数据包中,不需要建立连接;每个数据报的大小在限制在64k;因无连接,是不可靠协议;不需要建立连接,速度快。

TCP:建立连接,形成传输数据的通道;在连接中进行大数据量传输;通过三次握手完成连接,是可靠协议;必须建立连接,效率会稍低。

2.3 Socket

(1)Socket套接字

网络上具有唯一标识的IP地址和端口号组合在一起才能构成唯一能识别的标识符套接字。

(2)Socket原理机制

A:通信的两端都有Socket。

B:网络通信其实就是Socket间的通信。

C:数据在两个Socket间通过IO传输。

.-

2.4 TCP传输

(1)客户端思路

A建立客户端的Socket服务,并明确要连接的服务器。

B如果连接建立成功,就表明,已经建立了数据传输的通道.就可以在该通道通过IO进行数据的读取和写入.该通道称为Socket流,Socket流中既有读取流,也有写入流.

C通过Socket对象的方法,可以获取这两个流

D通过流的对象可以对数据进行传输

E如果传输数据完毕,关闭资源

(2)服务器端思路

A建立服务器端的socket服务,需要一个端口

B服务端没有直接流的操作,而是通过accept方法获取客户端对象,在通过获取到的客户端对象的流和客户端进行通信

C通过客户端的获取流对象的方法,读取数据或者写入数据

D如果服务完成,需要关闭客户端,然后关闭服务器,但是,一般会关闭客户端,不会关闭服务器,因为服务端是一直提供服务的

2.5 Java的多线程机制

进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1--n个线程。

线程:同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小。

多进程是指操作系统能同时运行多个任务(程序),多线程是指在同一程序中有多个顺序流在执行。在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。因为每当使用java命令执行一个类的时候,实际上都会启动一个JVM,启动JVM实际上就是在操作系统中启动了一个进程。在java中所有的线程都是同时启动的,至于什么时候,哪个先执行,完全看谁先得到CPU的资源。

三、需求分析

实现聊天的功能,采用Java Socket编程,服务器与客户端采用了TCP/IP连接方式,在设计聊天方案时,可将所有信息发往服务器端,再由服务器进行处理,服务器端是所有信息的中心。

3.1客户端

3.1.1登录

用户需要填写用户名、服务器地址、端口号才可以进入聊天室与在线用户聊天,此外,服务器地址默认是本机地址,端口号默认是5000。用户名可以中文,英文字母或标点符号,服务器地址必须是符合点分十进制的合法地址,端口号可以修改,但服务器端程序内默认端口号也必须修改。该聊天室不需要注册,直接登录即可聊天,退出后,系统不保留用户任何信息。

若新登录用户与在线用户的用户名重名,则系统会自动修改用户名。格式为:用户名+“_客户端线程ID”。

3.1.2多人聊天

每个在线用户都可以发送聊天信息,服务器端会一直监听,并把每一个在线用户发送的聊天信

.-

息转发到每一个客户端。

聊天室的聊天信息格式为:

用户名 时间(yyyy-MM-dd HH:mm:ss)

聊天信息

3.1.3单人聊天

用户可以选择某一个在线用户实现单人聊天,该聊天信息不会在聊天室显示,只有单聊的两个人能够看到。

单人聊天的聊天信息格式为:

用户名 时间(yyyy-MM-dd HH:mm)

聊天信息

3.2服务器端

服务器端主要处理客户端的请求,包括用户的登录,发送多人聊天信息,退出聊天室,单人聊天请求,发送单人聊天信息,并且随时更新在线用户列表。

四、总体设计

4.1设计思想流程图

Server服务器端Client客户端创建ServerSocket启动服务器等待并接受客户端请求请求连接创建Socket并请求连接服务器接受请求后建立Socket连接开始通信InputStreamOutputStream开始通信InputStreamOutputStream关闭ServerSocket关闭Socket

.-

4.2设计思想分析

首先启动服务器,它会建立一个专门用于接收客户端连接请求的“倾听Socket”,然后等待客户的连接请求。当用户登录输入信息后,与服务器建立Socket连接,服务器端的“倾听Socket”收到连接请求后,会接受连接请求,并生成一个服务器端socket,专门负责与此客户端socket的通信。一旦连接请求成功,客户端将信息及请求通过本方socket的输出流发送给服务器端相应的socket,服务端则通过服务器端Socket的输入流接受客户端传输过来的信息及请求,分析是何请求,然后根据请求类型,进行相应的处理(如登录、私聊等)。服务器端也可以根据需要,通过socket的输出流发送信息和请求给客户端。客户端和服务器端都可以通过关闭本方的socket而结束一次通信过程。

对于客户端的各种请求,实际上都是通过在客户端发往服务器的各种字符流区分的,具体的方法就是在消息的内部添加特殊字符串,从而实现服务器对消息请求的识别。比如对于登陆信息,消息中添加的内容就是“@login”,而对于私聊中的消息,消息中添加的内容就是“@single”,其他的同理都添加了相应内容。当然,对于客户端来说,这些都是透明的,用户的操作并没有受到任何影响。在服务器端,消息被检测分析后,变回根据具体的目的进行处理,比如是私聊消息,服务器便会根据其内部添加的信息,向目标端转发该条消息,当目标端接收到连接请求后,会主动建立一个私聊窗口,从而实现私聊。

服务器端需要能同时接受多个用户的请求,为了实现这一点,一般使用多线程机制来处理,对每一个客户端连接通信,服务器端都有一个线程专门负责处理。

对于客户端的各种请求,内部添加的信息分别如下:

 @clientThread客户端线程启动

(() + "@clientThread");

客户端线程ID

 @login登录客户端

username+"@login"+getThreadID()+"@login"

用户名+客户端线程ID

 @userlist用户列表

(new Integer(threadID)) + "@userlist"+threadID + "@userlist"

用户名+客户端线程ID

 @chat群聊

username + "@chat" + getThreadID() + "@chat"+ mess + "@chat"

用户名+客户端线程ID+聊天信息

 @serverexit服务器退出

es+"@serverexit"

 @single单聊

me + "@single" + eadID() + "@single" +(int)(index) +

"@single" + mess + "@single";

用户名+客户端线程ID+客户端线程ID+聊天信息

 @exit退出群聊

username + "@exit" + getThreadID() + "@exit"

用户名+客户端线程ID

五、详细设计

.-

5.1客户端设计

5.1.1登录界面

5.1.2聊天室界面

5.1.3单人聊天界面

.-

5.2服务器端设计

六、系统测试

6.1登录测试

6.1.1用户名为英文字母

.-

6.1.2用户名为中文

6.1.3用户名为标点符号

.-

6.1.4多个用户有重名

.-

6.1.5服务器地址不合法

.-

6.1.6端口号不合法

6.1.7服务器未开启

.-

6.2群发消息测试

.-

.-

6.3私聊测试

.-

6.4用户列表显示测试

无论已经登录用户的退出,还是新用户成功登录,用户列表显示部分都能正确显示。

七、心得体会

这段时间通过不断的修改,我终于把聊天室系统完成了,虽然它只有简单的聊天功能,但通过它,我不但巩固了以前学的知识,而且学到了许多在课堂中学不到的知识。通过这次课程设计,我更坚定了理论与实际相结合是十分重要的想法,即使一个人读了再多的技术图书,但没有相关的实践经验,那么他也不会真正地掌握一门技术。只有把理论知识与实践相结合,才会深入的了解并提高自己的独立思考能力。

通过这次的课程设计,我将自己所学的Java语言得到了实际的应用,在完成的过程中也遇到了许多困难,但通过不断地查阅资料,最终还是解决了,在这个过程中,我学会了独立思考,同时也让我明白完成一件事情要不断开阔视野,拓展知识面,解放自己的思维。总之,在完成课程设计的过程中,我学会了如何克服开发中遇到的技术困难,学会了独立面对并解决问题。

八、参考资料

叶核亚 JAVA程序设计实用教程(第2版)电子工业出版社

朱福喜,路迟 JAVA语言与面向对象程序设计 武汉大学出版社

沈文炎 Java高级编程 机械工业出版社

九、程序清单

(1)

public class Client extends Thread{

public Socket c_socket ;//套接字

private Client_chatFrame c_chatFrame;//聊天室聊天界面

private Client_enterFrame c_enterFrame;//客户端登录界面

private Client_singleFrame c_singleFrame;//单人聊天界面

public DataInputStream dis = null;//IO输入

public DataOutputStream dos = null;//IO输出

private boolean flag_exit = false;//客户端未启动标记

private int threadID;//聊天室客户端线程标记

public Map c_singleFrames;//单人聊天,用户名为键,单聊客户端为值

public List username_online;//在线用户

public List clientuserid;//用户ID

public String username = null;//用户名

public String chat_re;//通道内相关信息

//getter, setter方法

public Client_chatFrame getC_chatFrame() {

return c_chatFrame;

}

public Client_singleFrame getC_singlFrame() {

.-

//

return c_singleFrame;

}

public void setC_singlFrame(Client_singleFrame c_singlFrame) {

this.c_singleFrame = c_singlFrame;

}

public void setC_chatFrame(Client_chatFrame c_chatFrame) {

this.c_chatFrame = c_chatFrame;

}

public Client_enterFrame getC_enterFrame() {

return c_enterFrame;

}

public void setC_enterFrame(Client_enterFrame c_enterFrame) {

this.c_enterFrame = c_enterFrame;

}

public int getThreadID() {

return threadID;

}

public void setThreadID(int threadID) {

ID = threadID;

}

//客户端构造函数

public Client(){

c_singleFrames = new HashMap();

username_online = new ArrayList();

clientuserid = new ArrayList();

signlechatuse = new ArrayList();

}

//main方法,设置进入时登录界面

public static void main(String[] args) {

Client client = new Client();

Client_enterFrame c_enterFrame = new Client_enterFrame(client);

_enterFrame(c_enterFrame);

c_ible(true);

}

//登录客户端

public String login(String username, String hostIp, String hostPort) {

me = username;//用户名

String login_mess = null;//错误信息或true

try {

c_socket = new Socket(hostIp, nt(hostPort));

} catch (NumberFormatException e) {

login_mess = "连接的服务器端口号port为整数,取值范围为:1024

return login_mess;

} catch (UnknownHostException e) {

.-

login_mess = "主机地址错误";

return login_mess;

} catch (IOException e) {

login_mess = "连接服务器失败,请稍后再试";

return login_mess;

}

return "true";

}

//创建一个客户端聊天界面,启动一个线程

public void showChatFrame(String username) {

getDataInit();

c_chatFrame = new Client_chatFrame(this,username);

c_ible(true);

flag_exit = true;//客户端已启动

();//启动线程

}

//初始化,建立连接通道

private void getDataInit() {

try {

dis = new DataInputStream(c_utStream());

dos = new DataOutputStream(c_putStream());

} catch (IOException e) {

tackTrace();

}

}

//该类声明为 Thread 的子类,重写 Thread 类的 run 方法

public void run() {

while(flag_exit){//客户端已启动

try {

//readUTF(): 读入一个已使用 UTF-8 修改版格式编码的字符串。

chat_re = F();

} catch (IOException e) {

flag_exit = false;

if(!chat_ns("@serverexit")){

chat_re = null;

}

}

if(chat_re != null){

if(chat_ns("@clientThread")){

int local = chat_f("@clientThread");

setThreadID(nt(chat_ing(0, local)));//启动客户端线程,设置ThreadID

try {

TF(username + "@login" + getThreadID() + "@login");

.-

} catch (IOException e) {

tackTrace();

}

}else{

if(chat_ns("@userlist")){

c_Users(chat_re);

}else{

if(chat_ns("@chat")){

c_Mess(chat_re);

}else{

if(chat_ns("@serverexit")){

c_lient();

}else{

if(chat_ns("@single")){

c_gleFrame(chat_re);

}

}

}

}

}

}

}

}

//发送聊天信息

public void transMess(String mess) {

try {

TF(username + "@chat" + getThreadID() + "@chat"+ mess + "@chat");

} catch (IOException e) {

tackTrace();

}

}

//聊天室中退出聊天

public void exitChat() {

try {

TF(username + "@exit" + getThreadID() + "@exit");

flag_exit = false;

(0);

} catch (IOException e) {

tackTrace();

}

}

//登录之前退出

public void exitLogin() {

(0);

.-

}

//服务器关闭,退出聊天室客户端,在Client_chatFrame中调用

public void exitClient() {

flag_exit = false;

(0);

}

}

(2)

public class WinCenter {

public static void center(Window win){

Toolkit tkit = aultToolkit();//使用系统工具包

Dimension sSize = eenSize();//屏幕尺寸

Dimension wSize = e();//窗体尺寸

if( > ){

= ;

}

if( > ){

= ;

}

ation(( - )/ 2, ( - )/ 2);//窗体居屏幕中央

}

}

(3)

public class Server {

private ServerFrame serverFrame;

private ServerThread serverThread;

public ServerFrame getServerFrame() {

return serverFrame;

}

public void setServerFrame(ServerFrame serverFrame) {

Frame = serverFrame;

}

public Server(){}

//启动服务器线程

public void startServer() {

try{

serverThread = new ServerThread(serverFrame);

}catch(Exception e){

(0);

}

g_exit(true);

();

.-

}

//停止服务器线程

public void stopServer(){

synchronized (es) {//同步代码块:synchronized:当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。

String str = "@serverexit";

(str);

}

Mess("@exit");//清除聊天信息

Users("@exit");//清除在线用户

rver();//停止服务器线程

}

//main方法

public static void main(String[] args) {

Server server = new Server();

ServerFrame serverFrame = new ServerFrame(server);

verFrame(serverFrame);

ible(true);

}

//停止服务器线程并退出

public void close() {

if(serverThread != null){

if(e()){

rver();

}

}

(0);

}

}

(4)

public class ServerThread extends Thread {

//Vector类可以实现可增长的对象数组。与数组一样,它包含可以使用整数索引进行访问的组件。

//但是,Vector 的大小可以根据需要增大或缩小,以适应创建 Vector 后进行添加或移除项的操作。

public ServerSocket serverSocket;//服务器套接字

public Vector messages;//(() + "@clientThread")

public Vector clients;//客户端线程

public Map users;//客户端线程ID和用户姓名

public BroadCast broadcast;

public int Port = 5000;

public boolean login = true;

public ServerFrame serverFrame;

private boolean flag_exit = false;//服务器未启动

public ServerThread(ServerFrame serverFrame){

.-

Frame = serverFrame;

messages = new Vector();

clients = new Vector();

users = new HashMap();

try {

serverSocket = new ServerSocket(Port);

} catch (IOException e) {

rtAndStopUnable();

(0);

}

broadcast = new BroadCast(this);

g_exit(true);

();//启动广播线程

}

@Override

public void run() {

Socket socket;

while(flag_exit){

try {

if(ed()){

flag_exit = false;

}else{

try{

socket = ();//接收客户端请求,无请求则阻塞

}catch(SocketException e){

socket = null;

flag_exit = false;

}

if(socket != null){

ClientThread clientThread = new ClientThread(socket, this);

g_exit(true);

();//启动客户端线程

/*

* synchronized:当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。

* 同步代码块 线程同步(目的是为了保护多个线程访问一个资源时对资源的破坏)

*/

synchronized (clients) {//客户端线程

ment(clientThread);

}

synchronized (messages) {

((int) (), "@login@");

(() + "@clientThread");

.-

}

}

}

} catch (IOException e) {

tackTrace();

}

}

}

//停止服务器

public void stopServer() {

try {

if(e()){

();

setFlag_exit(false);

}

} catch (Throwable e) {}

}

//设置服务器启动标志

public void setFlag_exit(boolean b) {

flag_exit = b;

}

}

(5)

public class ClientThread extends Thread {

public Socket clientSocket;//客户端套接字

public ServerThread serverThread;

public DataInputStream dis;

public DataOutputStream dos;

//public String client_userID;

private boolean flag_exit = false;//客户端线程启动标记

public ClientThread(Socket socket, ServerThread serverThread){

clientSocket = socket;

Thread = serverThread;

try {

dis = new DataInputStream(utStream());

dos = new DataOutputStream(putStream());

} catch (IOException e) {

tackTrace();

}

}

@Override

public void run() {

while(flag_exit){

try {

.-

//从通道读出信息

String Message = F();

if(ns("@login")){//有新登录用户

String [] userInfo = ("@login");

int userID = nt(userInfo[1]);

(userID);

if(nsValue(userInfo[0])){//新登录的与原来的有重名

for(int i = 0; i < (); i++){

int id = (int)(i).getId();

if((id).equals(userInfo[0])){//用户名相同

(id);

(id, userInfo[0] + "_" + id);//额外添加标记(线程ID),更新新登录的用户

break;

}

}

(nt(userInfo[1]), userInfo[0] + "_" +

userInfo[1]);//更新原来用户

}else{

(userID, userInfo[0]);

}

//更新服务器端用户列表

Message = null;

StringBuffer sb = new StringBuffer();

synchronized (s) {//同步代码块:客户线程

for(int i = 0; i < (); i++){

int threadID = (int) tAt(i).getId();//客户线程ID

((String)(new Integer(threadID)) +

"@userlist");//用户姓名+“@userlist”

(threadID + "@userlist");//用户姓名+“@userlist”+客户端线程ID+“@userlist”

}

}

String userNames = new String(sb);

Users(userNames);//更新用户列表

Message = userNames;

}else{

if(ns("@exit")){//有退出,更新用户列表

String [] userInfo = ("@exit");

int userID = nt(userInfo[1]);

(userID);//客户端退出,清除用户ID

Message = null;

StringBuffer sb = new StringBuffer();

.-

synchronized (s) {

for(int i = 0; i < (); i++){

int threadID = (int) tAt(i).getId();

if(userID == threadID){//移除退出客户端线程

ElementAt(i);

i--;

}else{//保留未退出客户端信息

((String)(new

Integer(threadID)) + "@userlist");

HH:mm:ss");

(threadID + "@userlist");

}

}

}

String userNames = new String(sb);

if(("")){

Users("@userlist");

}else{

Users(userNames);

}

Message = userNames;

}else{

//群聊展示聊天信息

if(ns("@chat")){

String[] chat = ("@chat");

StringBuffer sb = new StringBuffer();

SimpleDateFormat form = new SimpleDateFormat("yyyy-MM-dd

String date = (new Date());

(chat[0] + " " + date + "n");

(chat[2] + "@chat");

String str = new String(sb);

Message = str;

Mess(Message);

}else{

if(ns("@single")){

}

}

}

}

synchronized (es) {

if(Message != null){

ment(Message);

}

}

.-

if(ns("@exit")){

();

flag_exit = false;

}

} catch (IOException e) {}

}

}

//关闭客户端线程

public void closeClienthread(ClientThread clientThread) {

if(Socket != null){

try {

();

} catch (IOException e) {

n("server's clientSocket is null");

}

}

try {

setFlag_exit(false);

} catch (Throwable e) {

tackTrace();

}

}

public void setFlag_exit(boolean b) {

flag_exit = b;

}

}

(6)

public class BroadCast extends Thread {

ClientThread clientThread;

ServerThread serverThread;

String str;

private boolean flag_exit = false;//广播线程未启动

public BroadCast(ServerThread serverThread){

Thread = serverThread;

}

@Override

public void run() {

boolean flag = true;//有人在线标记

while(flag_exit){

synchronized (es) {

if(y()){

continue;

}else{

str = (String)lement();

.-

Element(str);

if(ns("@clientThread")){

flag = false;

}

}

}

synchronized (s) {

for(int i=0; i < (); i++)

{

clientThread = tAt(i);

if(flag){

try

{

//向纪录的每一个客户端发送数据信息

if(ns("@exit")){

(i);

lienthread(clientThread);

TF(str);

}

if(ns("@chat") || ns("@userlist")

ns("@serverexit")){

TF(str);

}

if(ns("@single")){

String[] info = ("@single");

int id_thread = nt(info[2]);

for(int j = 0; j < (); j++){

if(id_thread == (j).getId()){

(j).TF(str);

i = ();

break;

}

}

}

}

catch(IOException E){}

}else{

String value = ((int)());

if(("@login@")){

flag = true;

try

{

//向纪录的每一个客户端发送数据信息

TF(str);

||

.-

if(ns("@exit")){

(i);

lienthread(clientThread);

}

}

catch(IOException E){}

break;

}

}

}

}

if(ns("@serverexit")){

();

flag_exit = false;

}

}

}

public void setFlag_exit(boolean b) {

flag_exit = b;

}

public void stopBroadCase() {

flag_exit = false;

}

}


本文标签: 客户端 线程 用户 请求 连接