Java语言实验报告
计科1203 杜杨浩 2012310200707
实验环境:普通PC机、 Windows XP 系统
实验目的:设计简单的QQ双人聊天界面
(1)客户端和服务端有发送文本框和接收文本域
(2)客户端和服务端有发送按钮和退出按钮
实验内容:
由老师提供的简单QQ双人聊天程序,设计相应的聊天界面。下面分别介绍一下双人聊天程序和聊天界面。首先该程序是一个非常基础和简单的C/S模式双人聊天程序,是基于Socket套接字的低层次java网络编程(基于TCP/IP协议),依然存在许多debug,而且只能实现客户端和服务端“一句话通信“。所谓”一句话通信“指的是:客户从键盘键入要发送的信息,按回车键后,服务端接受客户端发来的信息,并同样键入要发送的信息,按回车键后,客户端接收信息。不断重复上述步骤,即客户端和服务端都只能发送一句话的信息,不能连续发送多句话信息(以回车键来标志是否为一句话)。并且当客户端(服务端)发送“Bye.”时,服务端(客户端)只能发送信息,而不能接受信息,而客户端(服务端)只能接受信息,不能发送信息了。直到服务端(客户端)发送”Bye.”时,服务端和客户端均结束通信。
下面简单讲述一下QQ聊天界面代码的设计。
QQ聊天代码是在上述双人聊天代码设计的基础上增加了一个简单的界面,该界面主要采用Swing和 AWT设计,利用向容器中添加组件的方法设计界面。这里容器主要指框架(Frame),而组件则相对较多,有文本框、文本域、按钮等等。并由事件触发原理在组件上注册某些事件的监听者,使组件能够对相应事件做出响应。
实验步骤:
首先让SingleTalkClient扩展ActionListener接口,作为点击按钮时的监听者。在SingleTalkClient中覆盖actionPerformed()函数,用于处理点击按钮时的相关响应。将原来主函数代码中的局部变量都作为SingleTalkClient的成员变量。这样可以直接在各成员函数中对成员变量进行相应处理。上面已经说明了用户端和服务端的QQ聊天界面容器都是一个框架(Frame),组件都是由一个发送文本框,一个接收文本域,和一个发送按钮以及接收按钮组成。这些组件和容器都作为SingleTalkClient和SingleTalkServer的成员变量。这里,为容器Frame设置了一个匿名对象监听者,用于处理关闭容器Frame的相关响应。在成员变量handle()中,建立各容器及相关组件对象。直接在handle()中写接收内容,在actionPerformed()函数中写发送内容,这样整个QQ聊天界面框架就打好了。接着主要讲述一下如何在成员函数handle()中实现接收,以及如何在actionPerformed()函数中实现发送。这是整个QQ聊天界面的核心。首先给出核心代码(SingleTalkClient类的handle()函数代码):
public void handle() throws IOException {
Socket client = null;
out = null;
in = null;
try {
client = new Socket("127.0.0.1", 4444);
out = new PrintWriter(client.getOutputStream(), true); //auto flush
in = new BufferedReader(new InputStreamReader(client.getInputStream()));
} catch (UnknownHostException e) {
System.err.println("Don't know about host: 127.0.0.1.");
System.exit(1);
} catch (IOException e) {
System.err.println("Couldn't get I/O for the connection to: 127.0.0.1.");
System.exit(1);
}
//创建图形界面
f=new Frame("qq clinet boss Test");
f.setSize(280,250);
f.setLayout(new FlowLayout(FlowLayout.CENTER));
send=new TextField(80);
receive=new TextArea(30,80);
f.add(send);
f.add(receive);
sending = new Button("发送");
receiveing= new Button("关闭");
f.add(sending);
f.add(receiveing);
sending.addActionListener(this);
receiveing.addActionListener(this);
f.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e){
System.exit(0);
}
});
f.setBackground(Color.lightGray);
f.setVisible(true);
record="";
send.setText("Client input:");
sbye = false;
ubye = false;
while( true )
{
if( sbye == false )
{
fromServer = in.readLine();
record=record+"from Server: "+fromServer+"\n";
receive.setText(record);
if (fromServer.equals("Bye."))
sbye = true;
}
if( ubye == false )
{
if(sbye){
out.println("Bye.");
out.flush();
ubye=true;
}
else
send.setText("Client input:");
}
if( ubye == true && sbye == true )
break;
}
out.close();
in.close();
client.close();
}
从上面的代码可以看出:首先是创建了Frame容器和文本框和文本域等各组件,接着为Frame设置了一个匿名监听者处理关闭响应。由于SingTalkClient类扩展了ActionListener接口,故可以将SingleTalkClient类的对象作为点击按钮的监视者。
代码:sending.addActionListener(this);
receiveing.addActionListener(this);
正是这个作用。主要流程是首先在客户端的发送文本框中输入“Client Input:“信息。提示客户输入信息。当客户输入要传达的信息并点击发送按钮后,在actionPerformed()中处理发送响应,下面是actionPerformed()的内容:
public void actionPerformed(ActionEvent e){
if(e.getSource()==sending){
if(ubye==false){
fromUser=send.getText();
fromUser=fromUser.replace("Client input:","");
out.println(fromUser);
out.flush();
if (fromUser.equals("Bye."))
ubye = true;
}
}
else if(e.getSource()==receiveing){
System.exit(0);
}
}
简单介绍一下该函数的功能:首先判断点击的是发送按钮还是关闭按钮。如果是关闭按钮则直接退出。如果是发送按钮,首先获取文本框中的内容,进行相应的转换后变成客户要输入的信息,然后将输入信息传送到输出流out上,通过套接字socket传递给服务端的输入流in ,最后判断客户输入的信息是否为对话结束标志“Bye.”,若是,则置标记ubye为true,这样发送处理结束。当服务端发送回信息时,客户端的处理程序如下:
if( sbye == false )
{
fromServer = in.readLine();
record=record+"from Server: "+fromServer+"\n";
receive.setText(record);
if (fromServer.equals("Bye."))
sbye = true;
}
直接通过输入流in来获取服务端发送过来的信息。然后经过转换后输出到接收文本域中(保留对话记录),最后判断服务端输入过来的信息是不是对话结束标志“Bye.“,如果是,则置标记sbye为true。这里我对未加界面的源程序中的”Bye.“问题作了相应的处理。处理策略是:无论是客户端还是服务端只要哪一方先发送”Bye.“,则对方自动也发送”Bye.“,同时双方程序均结束。实现这一操作的处理程序如下:
if( ubye == false )
{
if(sbye){
out.println("Bye.");
out.flush();
ubye=true;
}
else
send.setText("Client input:");
}
假设当服务端先发送过来“Bye.“信息时,客户端接收信息并做判断置sbye为true。进入if判断,会自动发送过去信息”Bye.” ,同时置ubye为true,这样客户端的程序就会结束。同样服务端在发送信息“Bye.”时,会置sinbye为true,而当其接收到信息“Bye.”时,会置inbye为true,这样服务端也会结束。而如果是客户端先发送“Bye.”,处理过程类似。正是通过这样的方式解决了“Bye.”问题。当然,当双方都没有发送“Bye.”时,则进入分支语句的else子句,直接在发送文本框中输入“Client Input:”信息,然后由客户输入要发送的信息,点击发送按钮,服务端接收到信息,在服务端的接收文本域中显示接收到的信息,并在服务端的发送文本框中输入“Server Input:”,服务端输入要发送的信息,点击发送按钮,客户端接收,接下来的过程就是上述过程的循环了。服务端的界面程序和客户端基本相同,其中有一点可能不相同,不过这也是为了将“Bye.“问题处理一致,下面是代码:
inputLine = in.readLine();
if(inputLine.equals("Bye."))
inbye=true;
record=record+"from Client: "+inputLine+"\n";
receive.setText(record);
if(inbye){
sinbye=true;
out.println("Bye.");
out.flush();
}
else
send.setText("Server input:");
开始时服务端接收客户端的信息,若为“Bye.“信息,则置inbye标志为true。接下来的分支语句中进入if,服务端自动传送过去”Bye.“信息,并置sinbye标记为true。若客户端发送过来的信息不是”Bye.“则会进入else分支,在服务端的发送文本框中输入”Server Input:“信息,提示服务端输入要发送的信息。接下来的处理过程就和上面讲述的一致了,这里不再叙述。
为了更全面系统地了解整个QQ聊天界面的设计,下面还是分别给出客户端和服务端的全部代码。
首先是客户端:
import java.io.*;
import java.net.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
public class SingleTalkClient implements ActionListener
{
private Frame f;
private Button sending,receiveing;
private TextField send;
private TextArea receive;
private PrintWriter out;
private BufferedReader in;
private BufferedReader stdIn;
private String fromServer, fromUser;
private String record;
boolean sbye,ubye;
public void actionPerformed(ActionEvent e){
if(e.getSource()==sending){
if(ubye==false){
fromUser=send.getText();
fromUser=fromUser.replace("Client input:","");
out.println(fromUser);
out.flush();
if (fromUser.equals("Bye."))
ubye = true;
}
}
else if(e.getSource()==receiveing){
System.exit(0);
}
}
public void handle() throws IOException {
Socket client = null;
out = null;
in = null;
try {
client = new Socket("127.0.0.1", 4444);
out = new PrintWriter(client.getOutputStream(), true); //auto flush
in = new BufferedReader(new InputStreamReader(client.getInputStream()));
} catch (UnknownHostException e) {
System.err.println("Don't know about host: 127.0.0.1.");
System.exit(1);
} catch (IOException e) {
System.err.println("Couldn't get I/O for the connection to: 127.0.0.1.");
System.exit(1);
}
//创建图形界面
f=new Frame("qq clinet boss Test");
f.setSize(280,250);
f.setLayout(new FlowLayout(FlowLayout.CENTER));
send=new TextField(80);
receive=new TextArea(30,80);
f.add(send);
f.add(receive);
sending = new Button("发送");
receiveing= new Button("关闭");
f.add(sending);
f.add(receiveing);
sending.addActionListener(this);
receiveing.addActionListener(this);
f.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e){
System.exit(0);
}
});
f.setBackground(Color.lightGray);
f.setVisible(true);
record="";
send.setText("Client input:");
sbye = false;
ubye = false;
while( true )
{
if( sbye == false )
{
fromServer = in.readLine();
record=record+"from Server: "+fromServer+"\n";
receive.setText(record);
if (fromServer.equals("Bye."))
sbye = true;
}
if( ubye == false )
{
if(sbye){
out.println("Bye.");
out.flush();
ubye=true;
}
else
send.setText("Client input:");
}
if( ubye == true && sbye == true )
break;
}
out.close();
in.close();
client.close();
}
public static void main(String[] args) throws IOException {
new SingleTalkClient().handle();
}
}
接下来是服务端:
import java.io.*;
import java.net.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
public class SingleTalkServer implements ActionListener{
private Frame f;
private Button sending,receiveing;
private TextField send;
private TextArea receive;
private PrintWriter out;
private BufferedReader in;
private BufferedReader sin;
private String sinputLine, inputLine;
private String record;
boolean sinbye,inbye;
public void actionPerformed(ActionEvent e){
if(e.getSource()==sending){
if( sinbye == false )
{
sinputLine=send.getText();
sinputLine=sinputLine.replace("Server input:","");
out.println(sinputLine);
out.flush();
if (sinputLine.equals("Bye."))
sinbye = true;
}
}
else if(e.getSource()==receiveing){
System.exit(0);
}
}
public void handle() throws IOException{
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(4444);
} catch (IOException e) {
System.err.println("Could not listen on port: 4444.");
System.exit(1);
}
Socket clientSocket = null;
try {
clientSocket = serverSocket.accept(); //程序将在此等候客户端的连接
} catch (IOException e) {
System.err.println("Accept failed.");
System.exit(1);
}
System.out.println("Accept OK!");
out = new PrintWriter(clientSocket.getOutputStream(), true); //auto flush
in = new BufferedReader(
new InputStreamReader(
clientSocket.getInputStream()));
sinbye = false;
inbye = false;
f=new Frame("qq server boss Test");
f.setSize(280,250);
f.setLayout(new FlowLayout(FlowLayout.CENTER));
send=new TextField(80);
receive=new TextArea(30,80);
f.add(send);
f.add(receive);
sending = new Button("发送");
receiveing= new Button("关闭");
f.add(sending);
f.add(receiveing);
sending.addActionListener(this);
receiveing.addActionListener(this);
f.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e){
System.exit(0);
}
});
f.setBackground(Color.lightGray);
f.setVisible(true);
record="";
inputLine = in.readLine();
if(inputLine.equals("Bye."))
inbye=true;
record=record+"from Client: "+inputLine+"\n";
receive.setText(record);
if(inbye){
sinbye=true;
out.println("Bye.");
out.flush();
}
else
send.setText("Server input:");
while( true )
{
if( inbye == false )
{
inputLine = in.readLine();
record=record+"from Client: "+inputLine+"\n";
receive.setText(record);
if (inputLine.equals("Bye."))
inbye = true;
}
if( sinbye == false )
{
if(inbye){
out.println("Bye.");
out.flush();
sinbye=true;
}
else
send.setText("Server input:");
}
if( sinbye == true && inbye == true )
break;
}
out.close();
in.close();
clientSocket.close();
serverSocket.close();
}
public static void main(String[] args )throws IOException{
new SingleTalkServer().handle();
}
}