《网络与通信》 课程设计报告
设计报告题目:网络聊天 学院:信息科学与工程学院 专业:电子信息0802 姓 名 :王昊 学 号 :0909080925 指导教师:邓晓衡
完 成 时 间 :2011年7月15日
目录
一
、
言............................................3
二
、
课
程
设
计
题
目
及
求..............................3
三
、
实
验
通
信
理....................................4
四
、
功
能
模
块
述....................................6
五
、
实
验
部
分
主
要
序................................6
六
、
实
验
程
序
图....................................9 七
、
关
键
部
分
码....................................13 八
、
问
题
及
解
决
案..................................22 九
、
心
得
体
会
与
结..................................22
前
要
原
描
程
截
源
方
总
一.前言:
随着计算机网络日新月异的发展,人们的交流方式越来越多,传统的交流方式,如:信件、电报、电话等已经难以满足人们的交流要求,在互联网上即时的和好友取得联系,已经成为当今社会人们主流的联系方式。因此,两台计算机之间进行即时通讯、发送文件等交流方式已经成为必然潮流。因此出现了QQ等聊天工具,然而QQ等聊天工具虽然方便、实用,但是,娱乐功能太多,有很多吸引人的娱乐功能,从一定程度上来说,是一种娱乐工具,不能作为用于即时通讯、文件共享的专用工具。目前,用于实现单一的即时通讯、文件共享的软件实在太少,而且,它们中大部分都是绝对的C/S模式,对远程服务器有非常大的依赖性,没有彻底实现点对点的交流。为此,我决定开发一个专用于实现两台计算机之间即时通讯、文件共享的软件,以方便两台计算机之间信息的交流。避免了服务器忙或与服务器无法连接时,浪费过多时间用于和服务器建立连接。因此,这个软件是极具适应性和实用性的即时通讯软件。
二.课程设计题目及要求:
网络聊天
任务:实现网络聊天的功能,采用Windows Socket(或Java Socket)编程,服务器与客户端采用了TCP/IP连接方式,在设计聊天方案时,可将所有信息发往服务器端,再由服务器进行处理,服务器端是所有信息的中心。服务器端要保存用户信息,可利用数据库来实现这一功能,需要建立用户信息数据库。在客户端可采用文件系统保存聊天纪录和用户号码。可通过闪动托盘图标和播放不同的音乐提示信息到来及好友上线等信息。可通过建立消息链表来保存用户接收的各种消息。
功能要求: (1)登录功能。
(2)客户可以通过服务器转发,实现一对一和多对多聊天。 (3)实现呼叫功能。
(4)客户端程序应该可以实时显示目前其它用户的状态。 (5)应该具有易用、美观的图形界面。 客户端功能划分:
(1)登陆功能:建立与服务器的连接并登陆,能显示登陆错误信息。 (2)界面显示:将在线好友显示在好友列表中,并实现系统托盘,加入工具栏便于操作。
(3)聊天功能:与好友聊天。
(4)聊天纪录:能保存聊天纪录,并能察看聊天纪录。
(5)信息提示:闪动托盘图标提示到来信息,并播放不同音乐来提示。 (6)其他:用户登陆成功,将保存其号码,以便下次登陆时,不必再输入而可以直接选择,显示登陆时间。 可行性分析:
本系统是一个类似QQ的网络聊天室系统,现有的技术以较为成熟。硬件、软件的性能要求、环境条件等各项条件良好,利用现有技术条件应完全可以达到该系统的功能目标。
技术实现如下:
CS模式:Java(多线程,Socket,Swing组件技术) +XML文档解析技术
三.实验通信原理:
总的设计思想是利用java里的套接字Socket和ServerSocket(客户/服务器)
来建立进程之间的通信
套接字工作原理:
要通过互联网进行通信,你至少需要一对套接字,其中一个运行于客户机端,我们称之为ClientSocket,另一个运行于服务器端,我们称之为ServerSocket。 根据连接启动的方式以及本地套接字要连接的目标,套接字之间的连接过程可以分为三个步骤:服务器监听,客户端请求,连接确认。 【所谓服务器监听】是服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态。 【所谓客户端请求】是指由客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求。 【所谓连接确认】是指当服务器端套接字监听到或者说接收到客户端套接字的连接请求,它就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发给客户端,一旦客户端确认了此描述,连接就建立好了。而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求。
Java socket 编程原理
1,什么是Socket
网络上的两个程序通过一个双向的通讯连接实现数据的交换,这个双向链路的一端称为一个Socket。Socket通常用来实现客户方和服务方的连接。Socket是TCP/IP协议的一个十分流行的编程界面,一个Socket由一个IP地址和一个端口号唯一确定。但是,Socket所支持的协议种类也不光TCP/IP一种,因此两者之间是没有必然联系的。在Java环境下,Socket编程主要是指基于TCP/IP协议的网络编程。
2,Socket通讯的过程
Server 端Listen(监听)某个端口是否有连接请求,Client端向Server端发出Connect(连接)请求,Server端向Client端发回 Accept(接受)消息。一个连接就建立起来了。Server端和Client端都可以通过Send,Write等方法与对方通信。
对于一个功能齐全的Socket,都要包含以下基本结构,其工作过程包含以下四个基本的步骤:
(1) 创建Socket;
(2) 打开连接到Socket的输入/出流;
(3) 按照一定的协议对Socket进行读/写操作;
(4) 关闭Socket.(在实际应用中,并未使用到显示的close,虽然很多文章都推荐如此,不过在我的程序中,可能因为程序本身比较简单,要求不高,所以并未造成什么影 3,怎么创建Socket 创建Socket
java在包java.net中提供了两个类Socket和ServerSocket,分别用来表示双向连接的客户端和服务端。这是两个封装得非常好的类,使用很方便。其构造方法如下:
Socket(InetAddress address, int port);
Socket(InetAddress address, int port, boolean stream); Socket(String host, int prot);
Socket(String host, int prot, boolean stream); Socket(SocketImpl impl)
Socket(String host, int port, InetAddress localAddr, int localPort)
Socket(InetAddress address, int port, InetAddress localAddr, int localPort) ServerSocket(int port);
ServerSocket(int port, int backlog);
ServerSocket(int port, int backlog, InetAddress bindAddr)
其中address、host和port分别是双向连接中另一方的IP地址、主机名和端口号,stream指明socket是流socket还是数据报 socket,localPort表示本地主机的端口号,localAddr和bindAddr是本地机器的地址(ServerSocket的主机地址),impl是socket的父类,既可以用来创建serverSocket又可以用来创建Socket。count则表示服务端所能支持的最大连接数。例如: Socket client = new Socket(\"127.0.01.\ ServerSocket server = new ServerSocket(80);
注意,在选择端口时,必须小心。每一个端口提供一种特定的服务,只有给出正确的端口,才能获得相应的服务。0~1023的端口号为系统所保留,例如 http服务的端口号为80,telnet服务的端口号为21,ftp服务的端口号为23, 所以我们
在选择端口号时,最好选择一个大于1023的数以防止发生冲突。 在创建socket时如果发生错误,将产生IOException,在程序中必须对之作出处理。所以在创建Socket或ServerSocket是必须捕获或抛出例外。 TCP/IP协议参考模型:
TCP/IP 实际上一个一起工作的通信家族,为网际数据通信提供通路。为讨论方便可将TCP/IP 协议组大体上分为三部分:
1.网络层:包括Internet 协议(IP)、网际控制报文协议(ICMP)和地址识别协议(ARP).
2. 传输层:传输控制协议(TCP)和用户数据报文协议(UDP)
3. 应用层:处于TCP 和UDP 之上的一组协议专门开发的应用程序。它们包括:TELNET,文件传送协议(FTP),域名服务(DNS)和简单的邮件传送程序(SMTP)等许多协议。
TCP/IP 使用了主干网络,能连接各种主机和LAN 的多级分层结构,局部用户能方便的联网,不致影响到整个网络系统。此外这种结构还有利于局部用户控制操作和管理。
TCP/IP 具有两个主要功能。第一是IP在网络之间(有时在个别网络内部)提供路由选择。第二是TCP将TP传递的数据传送到接收主机那的适当的处理部件。
四.功能模块描述:
(1) 用户登录:用户根据自己的注册信息进行登录,只需要输入用户账号和密码,如果输入正确,服务器会发出登录成功,如果失败,会根据不同情况进行处理。 (2) 用户私聊:用户之间通过共同的服务器进行通信私聊,实现了一对一聊天。 (3) 聊天室群聊功能:用户A可以在聊天室中对其余所有人说话
(4) 服务器开启与关闭:根据TCP SOCKET管理通信,服务器开启监听用户请求链接,若服务器关闭则用户不能登录。
(5) 保存/查看聊天记录:使用文件I/O操作,根据不同的客户ID,产生相应的文件(文件名为客户ID号)来保存记录。
(6) 好友状态实时显示:利用客户端守护线程实时监控好友状态,守护线程每间隔10秒向服务器端发送请求,服务端返回当前在线的用户状态信息给客户端,若
当前在线的用户状态信息有变,则客户端根据服务器端的返回信息更新好友状态。
(7) 服务器对客户终端实时监控:只监听在线用户
(8) 用户终端登录记录(ID/Time):利用xml文档解析技术,记录用户的ID和操作时间。
(9) 时间查看: 程序带有时钟查看功能,可看到聊天时的时间。
五.实验部分主要程序:
(服务器界面,可以实现用户信息管理及基本信息的显示功能。包含:服务器当前状态,在线人数,服务器IP等信息。用户信息,包含:用户成员,在线信息,消息记录等信息。)
1、服务器主要程序:(只包含原理主要部分)
import java.io.*; import java.net.*; import java.util.*;
* Title: HappyChat聊天系统服务器程序 * Description: 聊天服务器 * Copyright: Copyright (c) 2006 * Filename: AppServer.java * 创建服务器
* @author * @version 1.0
public class AppServer extends Thread { private ServerSocket serverSocket; private ServerFrame sFrame;
private static Vector userOnline = new Vector(1, 1); private static Vector v = new Vector(1, 1); 创建服务器 启动服务监听1001端口 public AppServer() { sFrame = new ServerFrame(); try { serverSocket = new ServerSocket(1001); // 获取服务器的主机名和IP地址 InetAddress address = InetAddress.getLocalHost(); sFrame.txtServerName.setText(address.getHostName()); sFrame.txtIP.setText(address.getHostAddress()); sFrame.txtPort.setText(\"1001\"); } catch (IOException e) {
}
fail(e, \"不能启动服务!\"); } sFrame.txtStatus.setText(\"已启动...\"); this.start(); // 启动线程 }
/* 退出服务器 * @param e * @param str * 退出信息
public static void fail(Exception e, String str) { System.out.println(str + \" 。\" + e);
}/* 监听客户的请求,当有用户请求时创建 Connection线程 public void run() { try { while (true) { // 监听并接受客户的请求 Socket client = serverSocket.accept(); new Connection(sFrame, client, userOnline, v); // 支持多线程 // System.out.println(\"userLength:\"+userLength); } } catch (IOException e) { fail(e, \"不能监听!\"); }
}/** 启动服务器
public static void main(String args[]) { new AppServer(); }
2、客户端主要程序:(只包含原理主要部分) import java.io.*; import java.net.*;
public class TalkClient {
public static void main(String args[]) { try{
Socket socket=new Socket(\"127.0.0.1\ //向本机的1001端口发出客户请求
BufferedReader sin=new BufferedReader(new InputStreamReader(System.in));
//由系统标准输入设备构造BufferedReader对象
PrintWriter os=new PrintWriter(socket.getOutputStream()); //由Socket对象得到输出流,并构造PrintWriter对象
BufferedReader is=new BufferedReader(new
InputStreamReader(socket.getInputStream()));
//由Socket对象得到输入流,并构造相应的BufferedReader对象
String readline;
readline=sin.readLine(); //从系统标准输入读入一字符串 while(!readline.equals(\"bye\")){
//若从标准输入读入的字符串为 \"bye\"则停止循环 os.println(readline);
//将从系统标准输入读入的字符串输出到Server os.flush();
//刷新输出流,使Server马上收到该字符串 System.out.println(\"Client:\"+readline); //在系统标准输出上打印读入的字符串
System.out.println(\"Server:\"+is.readLine()); //从Server读入一字符串,并打印到标准输出上
readline=sin.readLine(); //从系统标准输入读入一字符串 } //继续循环
os.close(); //关闭Socket输出流 is.close(); //关闭Socket输入流 socket.close(); //关闭Socket }catch(Exception e) {
System.out.println(\"Error\"+e); //出错,则打印出错信息 } } }
客户端功能说明:1,可以实现一对一聊天,也可以实现一对多聊天。 2,可以发送系统消息,和保存消息记录。 3,可以实现字体的调整和表情的发送。 4,可以实现观看人数等基本信息。
3、用户注册功能:(只提供实验结果集功能说明,源码在另一个文件夹中)
功能说明:1 实现用户基本信息的录制和注册功能 2 可以判别信息是否录制正确。 3 将信息保存至服务器中。
4、时钟显示及登陆(只提供实验结果集功能说明,源码在另一个文件夹中) 功能说明:实现时钟显示的功能及登陆功能
六.实验程序截图:
1.用户注册界面(用户首次运行程序需要先注册)
2.开启服务器(监听)
3.用户登录程序(用户名+口令)
此处实验调试需登录两个用户(wh和wh2) 4.登录成功后聊天室主界面
如图所示,wh用户对wh2用户发言聊天中的程序运行显示。 Wh对wh2说你好,
wh中显示你对wh2说‘******’,wh2中显示wh对你说‘******’。
此时服务器更新并显示了用户的登录信息 以示管理所用。
5.改变字体颜色大小等属性后采用私聊方式聊天(悄悄话)
此处,wh对wh2采用私聊方式对话,信息只在wh和wh2间传递,wh3(其他所有用户)均在界面中看不到私聊的信息。
时钟信息截图
七.关键部分源码:
服务器程序: import java.io.*; import java.net.*; import java.util.*; *
* Title: HappyChat聊天系统服务器程序 *
** Description: 聊天服务器 *
** Copyright: Copyright (c) 2006 *
** Filename: AppServer.java *
* 创建服务器
*
* @author * @version 1.0 * */
public class AppServer extends Thread { private ServerSocket serverSocket; private ServerFrame sFrame; private static Vector userOnline = new Vector(1, 1); private static Vector v = new Vector(1, 1); /** * 创建服务器 启动服务监听1001端口
* */
public AppServer() { sFrame = new ServerFrame(); try { serverSocket = new ServerSocket(1001); // 获取服务器的主机名和IP地址 InetAddress address = InetAddress.getLocalHost(); sFrame.txtServerName.setText(address.getHostName()); sFrame.txtIP.setText(address.getHostAddress()); sFrame.txtPort.setText(\"1001\"); } catch (IOException e) { fail(e, \"不能启动服务!\"); } sFrame.txtStatus.setText(\"已启动...\"); this.start(); // 启动线程 } /**
* 退出服务器 *
* @param e
* 异常 * @param str
* 退出信息 */
public static void fail(Exception e, String str) { System.out.println(str + \" 。\" + e); } /**
* 监听客户的请求,当有用户请求时创建 Connection线程 */
public void run() { try { while (true) { // 监听并接受客户的请求 Socket client = serverSocket.accept(); }
new Connection(sFrame, client, userOnline, v); // 支持多线程 // System.out.println(\"userLength:\"+userLength); }
} catch (IOException e) { fail(e, \"不能监听!\"); }
public static void main(String args[]) { new AppServer(); } }
登陆源码:
import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.net.*; import java.io.*; /** *
* Title: HappyChat聊天系统登录程序 *
** Description: 根据指定的服务器地址、用户名和密码登录聊天服务器 *
** Copyright: Copyright (c) 2006 *
** Filename: Login.java *
* * @author * @version 1.0 */public class Login extends JFrame implements ActionListener {
private static final long serialVersionUID = -8965773902056088264L;
lblServer = new JLabel(\"SERVER(S):\"); lblUserName = new JLabel(\"NAME(U):\"); lblPassword = new JLabel(\"PASSWORD(P):\"); /**
* 构造登陆窗体 */
public Login() {
super(\"登录[justtalk]聊天室\"); pnlLogin = new JPanel();
this.getContentPane().add(pnlLogin);
private Toolkit toolkit = Toolkit.getDefaultToolkit(); // 用于将窗口定位
private Dimension scrnsize; private String strServerIp; private JPasswordField pwdPassword; private JTextField txtUserName, txtServer;
private JLabel lblServer, lblUserName, lblPassword, lblLogo; private JButton btnLogin, btnRegister, btnExit; private JPanel pnlLogin;
txtServer = new JTextField(20); txtServer.setText(\"127.0.0.1\"); txtUserName = new JTextField(20); pwdPassword = new JPasswordField(20); btnLogin = new JButton(\"login(L)\"); btnLogin.setToolTipText(\"login sever\"); btnLogin.setMnemonic('L');
btnRegister = new JButton(\"register(R)\"); btnRegister.setToolTipText(\"register new user\"); btnRegister.setMnemonic('R'); btnExit = new JButton(\"EXIT(X)\"); btnExit.setToolTipText(\"EXIT\"); btnExit.setMnemonic('X');
/***********************************************************************
* 该布局采用手动布局 setBounds设置组件位置 * setFont设置字体、字型、字号 * setForeground设置文字的颜色 *
lblServer.setBounds(50, 100, 100, 30); txtServer.setBounds(150, 100, 120, 25); lblUserName.setBounds(50, 130, 100, 30); txtUserName.setBounds(150, 130, 120, 25); lblPassword.setBounds(50, 160, 100, 30); pwdPassword.setBounds(150, 160, 120, 25); btnLogin.setBounds(30, 200, 100, 25); btnRegister.setBounds(130, 200, 100, 25); btnExit.setBounds(230, 200, 100, 25);
* setBackground设置背景色 * setOpaque将背景设置为透明 */
pnlLogin.setLayout(null); // 组件用手动布局 pnlLogin.setBackground(new Color(220, 130, 203));
// 设置背景图片
Icon logo1 = new ImageIcon(\"images\\\\loginlogo.jpg\");
pnlLogin.add(lblServer); pnlLogin.add(txtServer); pnlLogin.add(lblUserName); pnlLogin.add(txtUserName); pnlLogin.add(lblPassword); pnlLogin.add(pwdPassword); pnlLogin.add(btnLogin); pnlLogin.add(btnRegister); pnlLogin.add(btnExit);
lblUserName.setForeground(Color.BLACK); lblPassword.setForeground(Color.BLACK); btnLogin.setBackground(Color.GREEN); btnRegister.setBackground(Color.GREEN); btnExit.setBackground(Color.GREEN);
Font fontstr = new Font(\"宋体\lblServer.setFont(fontstr); txtServer.setFont(fontstr); lblUserName.setFont(fontstr); txtUserName.setFont(fontstr); lblPassword.setFont(fontstr); pwdPassword.setFont(fontstr); btnLogin.setFont(fontstr); btnRegister.setFont(fontstr); btnExit.setFont(fontstr);
lblLogo = new JLabel(logo1); lblLogo.setBounds(0, 0, 340, 66); pnlLogin.add(lblLogo); // 设置登录窗口 setResizable(false); setSize(340, 260); setVisible(true);
scrnsize = toolkit.getScreenSize();
setLocation(scrnsize.width / 2 - this.getWidth() / 2, scrnsize.height
/ 2 - this.getHeight() / 2);
Image img = toolkit.getImage(\"images\\\\appico.jpg\"); setIconImage(img);
// 三个按钮注册监听
btnLogin.addActionListener(this); btnRegister.addActionListener(this); btnExit.addActionListener(this);
} // 构造方法结束
/**
* 按钮监听响应 */
@SuppressWarnings({ \"deprecation\public void actionPerformed(ActionEvent ae) {
Object source = ae.getSource(); if (source.equals(btnLogin)) {
// 判断用户名和密码是否为空
if (txtUserName.getText().equals(\"\")
|| pwdPassword.getText().equals(\"\")) {
}
JOptionPane op1 = new JOptionPane();
op1.showMessageDialog(null, \"用户名或密码不能为空\");
} else { }
strServerIp = txtServer.getText(); login();
if (source.equals(btnRegister)) { }
if (source == btnExit) { }
System.exit(0);
strServerIp = txtServer.getText(); this.dispose();
new Register(strServerIp);
} // actionPerformed()结束
/**
* 登录事件响应方法 */
@SuppressWarnings(\"deprecation\") public void login() {
// 接受客户的详细资料
Customer data = new Customer(); data.custName = txtUserName.getText(); data.custPassword = pwdPassword.getText(); try {
// 连接到服务器 Socket toServer;
toServer = new Socket(strServerIp, 1001);
ObjectOutputStream streamToServer = new ObjectOutputStream(toServer
.getOutputStream());
// 写客户详细资料到服务器socket
streamToServer.writeObject((Customer) data); // 读来自服务器socket的登录状态
BufferedReader fromServer = new BufferedReader(
new InputStreamReader(toServer.getInputStream()));
String status = fromServer.readLine(); if (status.equals(\"登录成功\")) {
new ChatRoom((String) data.custName, strServerIp); this.dispose(); // 关闭流对象
streamToServer.close(); fromServer.close(); toServer.close();
} else { }
JOptionPane.showMessageDialog(null, status); // 关闭流对象
streamToServer.close(); fromServer.close(); toServer.close();
} catch (ConnectException e1) {
JOptionPane.showMessageDialog(null, \"未能建立到指定服务器的连接!\");
} catch (InvalidClassException e2) {
JOptionPane.showMessageDialog(null, \"类错误!\");
} catch (NotSerializableException e3) {
JOptionPane.showMessageDialog(null, \"对象未序列化!\");
} catch (IOException e4) {
JOptionPane.showMessageDialog(null, \"不能写入到指定服务器!\");
}
} // login()结束 /**
* 启动登陆窗体 * @param args */
public static void main(String args[]) { }
new Login();
} // Class Login结束
八.问题及解决方案:
该课程设计对于进一步培养面向对象程序设计的思想有很大的帮助,同时加深了对面向对象程序设计四大特性的理解,由于能力有限,还存在着许多不足之处。如:
◆ 写代码切勿急躁。这个程序是对我掌握知识熟练度的巨大挑战。写代码过程中经常出现到处报错的情况,常常要花好长时间寻找错误。有时只是单纯的语法错误就会困惑好久。
◆ 考虑问题要全面,要有整体意识。因为之前写的程序都是单纯的具有一项功能,各部分几件的配合不是很关键,而之一程序包含聊天涂鸦等花样,如果协调不好,就会顾此失彼。
◆ 同学们之间的互相的帮助也对软件的完成起了很大作用。编写这些代码需要很多方面的知识,一个人要想全面掌握有些困难。当我山穷水尽得时候常找同学过来帮忙调试。
◆ 实践要比理论难得多。有一个好想法,如果不去实践,永远不知道它正不正确。而这个过程中会遇到各种各样的问题,我也在不断提出与解决问题过程中得到提高。
九.心得体会与总结:
通过此项目实训——JAVA聊天室的设计,实践、总结、回顾了网络编程思想以及socket通信的编程方法,并通过编写程序来掌握Java语言编程技巧,将学习到的知识融会贯通,同时提高调试程序的能力,养成良好的编程习惯,并增强对程序设计整体思路的把握,理论与时间相结合。
因篇幅问题不能全部显示,请点此查看更多更全内容