本文主要是复习一个我以前上课时写的小作业—java聊天室,当时刚接触Java不久,所以代码写的可能比较啰嗦。一步一步增加,一共写了13个版本。
如果你要看最终版本,点击Java 聊天室的实现(具体)2
第一个版本:这个版本的主要作用是利用socket是实现基本的客户端与服务器的连接。
// server.java
package Chat1;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
ServerSocket server;//做成全局变量
//构造方法
public Server() {
System.out.println("正在启动服务器.....");
try {
server = new ServerSocket(8088);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}//参数:端口号
System.out.println("服务器已启动!");
}
//开始监听
public void start() {
System.out.println("正在等待客户端连接....");
try {
Socket socket = server.accept();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("客户端已连接!");
}
public static void main(String[] args) {
Server server = new Server();
server.start();
}
}
//client.java
package Chat1;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
public class Client {
//构造方法
public Client() {
System.out.println("正在连接服务器........");
try {
Socket socket = new Socket("localhost",8088);//两个参数,一个是服务器的IP地址,一个是服务器的端口号;写完之后对异常进行处理(try..catch)
} catch (UnknownHostException e) {//UnknownHostException e:未知服务器异常
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {//IOException e:未知IO流异常,因为Socket是通过信息流来传递的,所以得捕获异常
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("服务器已连接!");
}
public static void main(String[] args) {
Client client = new Client();
}
}
运行程序时先启动服务端,再启动客户端。
当服务端启动成功时,服务端会显示:“正在启动服务器…服务器已启动!正在等待客户端连接…”,当客户端已经连接上时,服务端会显示“客户端已连接”
第二个版本:这个版本的主要作用是利用Frame类实现图形界面的编写。
//client.c
package Chat2;
import java.awt.*;//Frame
public class Client extends Frame{//继承框架
public static void main(String[] args) {
new Client().launchFrame();
}
//窗口框架
public void launchFrame() {
setLocation(800,300);//控制窗口位置
this.setSize(400,400);//控制窗口大小
setVisible(true);
}
}
第三个版本:这个版本的主要作用是完善图形界面,加入输入框与显示框。
package Chat3;
import java.awt.*;//Frame
//加上输入输出创口
public class Client extends Frame{//继承框架
TextField tfText = new TextField();//全局变量,方便后面使用
TextArea taContext = new TextArea();
public static void main(String[] args) {
new Client().launchFrame();
}
public void launchFrame() {
setLocation(800,300);//位置
this.setSize(400,400);//大小
add(tfText,BorderLayout.SOUTH);//显示框加到南边
add(taContext,BorderLayout.NORTH);//输入框加到北边
pack();//处理中间大部分空白
setVisible(true);
}
}
第四个版本:这个版本的主要作用是完善图形界面,定义窗口关闭按钮。细心的朋友可能会发现上一个版本的窗口不能点关闭按钮关闭,我们这一版本就是解决这个问题。
package Chat4;
import java.awt.*;//Frame
import java.awt.event.*;//WindowAdapter,WindowEvent
//定义关掉窗口
public class Client extends Frame {// 继承框架
TextField tfText = new TextField();// 全局变量,方便后面使用
TextArea taContext = new TextArea();
public static void main(String[] args) {
new Client().launchFrame();
}
public void launchFrame() {
setLocation(800, 300);// 位置
this.setSize(400, 400);// 大小
add(tfText, BorderLayout.SOUTH);// 显示框加到南边
add(taContext, BorderLayout.NORTH);// 输入框加到北边
pack();// 处理中间大部分空白
this.addWindowListener(new WindowAdapter() {//匿名类WindowAdapter是个抽象类,里面对接口WindowListener的方法都进行了空实现。 这样编程时不需要自己再去实现全部接口WindowListener里的方法。只需覆写自己需要的方法,其他的方法 WindowAdapter都实现了
@Override
//重写windowsClosing
public void windowClosing(WindowEvent arg0) {
System.exit(0);//正常退出就行
}
});
setVisible(true);
}
}
第5个版本:这个版本的主要作用是完善图形界面,把输入框里面的内容通过Enter显示到输出框中
package Chat5;
import java.awt.*;//Frame
import java.awt.event.*;//WindowAdapter,WindowEvent
//把tfText里面的内容通过Enter进入到taContext
public class Client extends Frame {// 继承框架
TextField tfText = new TextField();// 全局变量,方便后面使用
TextArea taContext = new TextArea();
public static void main(String[] args) {
new Client().launchFrame();
}
//窗口
public void launchFrame() {
setLocation(800, 300);// 位置
this.setSize(400, 400);// 大小
add(tfText, BorderLayout.SOUTH);// 显示框加到南边
add(taContext, BorderLayout.NORTH);// 输入框加到北边
pack();// 处理中间大部分空白
this.addWindowListener(new WindowAdapter() {//WindowAdapter是个抽象类,里面对接口WindowListener的方法都进行了空实现。 这样编程时不需要自己再去实现全部接口WindowListener里的方法。只需覆写自己需要的方法,其他的方法 WindowAdapter都实现了
@Override
//重写windowsClosing
public void windowClosing(WindowEvent arg0) {
System.exit(0);//正常退出就行
}
});
tfText.addActionListener(new TFListener());
setVisible(true);
}
//把tfText里面的内容通过Enter进入到taContext
private class TFListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
String s = tfText.getText().trim();//trim去掉空格
taContext.setText(s);
tfText.setText("");//处理enter后的tfText,使其为空。
}
}
}
第6个版本:这个版本的主要作用重新写了一下服务器,主要是加入了一个死循环来进行与客户端的连接,这么做的原因有二:1.为了方面进行一个服务端与多个客户端连接,2.为了解决一次连接失败的问题。
package Chat6;
import java.io.IOException;
import java.net.*;
public class Server {
public static void main(String[] args) {
try {
ServerSocket sc = new ServerSocket(8088);
//接受客户端连接,不一定是一个连接,就用死循环
while(true) {
Socket s = sc.accept();
System.out.println("a client connected!");
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
第7个版本:这个版本的主要作用实现已经有窗口的客户端与服务器的连接。
//client.java
package Chat7;
import java.net.*;
import java.awt.*;//Frame
import java.awt.event.*;//WindowAdapter,WindowEvent
import java.io.IOException;
//与Server连接
public class Client extends Frame {// 继承框架
TextField tfText = new TextField();// 全局变量,方便后面使用
TextArea taContext = new TextArea();
public static void main(String[] args) {
new Client().launchFrame();
}
//Connet,与客户端进行连接
public void connect() {
try {
Socket s = new Socket("localhost",8088);//127.0.0.1
System.out.println("connected!");
} catch (UnknownHostException e) {//找不到主机
e.printStackTrace();
} catch (IOException e) {//网卡上有错
e.printStackTrace();
}
}
//窗口
public void launchFrame() {
setLocation(800, 300);// 位置
this.setSize(400, 400);// 大小
add(tfText, BorderLayout.SOUTH);// 显示框加到南边
add(taContext, BorderLayout.NORTH);// 输入框加到北边
pack();// 处理中间大部分空白
this.addWindowListener(new WindowAdapter() {//WindowAdapter是个抽象类,里面对接口WindowListener的方法都进行了空实现。 这样编程时不需要自己再去实现全部接口WindowListener里的方法。只需覆写自己需要的方法,其他的方法 WindowAdapter都实现了
@Override
//重写windowsClosing
public void windowClosing(WindowEvent arg0) {
System.exit(0);//正常退出就行
}
});
tfText.addActionListener(new TFListener());
setVisible(true);
connect();//发完信息后直接连接
}
//把tfText里面的内容通过Enter进入到taContext
private class TFListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
String s = tfText.getText().trim();//trim去掉空格
taContext.setText(s);
tfText.setText("");//处理enter后的tfText,使其为空。
}
}
}
第8个版本:这个版本的主要作用实现将客户端输入的内容发送到服务器。主要改变有两处:1.是在服务端要加入输入函数InputStream 2.是在客户端加入输出函数OutputStream
//server.java
package Chat8;
import java.io.*;
import java.io.IOException;
import java.net.*;
public class Server {
public static void main(String[] args) {
try {
ServerSocket sc = new ServerSocket(8088);
//接受客户端连接,不一定是一个连接,就用死循环
while(true) {
Socket s = sc.accept();
System.out.println("a client connected!");
DataInputStream dis = new DataInputStream(s.getInputStream());
String str = dis.readUTF();//readUTF是转化成utf-8编码格式的
System.out.println(str);
dis.close();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
//client.java
package Chat8;
import java.io.*;
import java.net.*;
import java.awt.*;//Frame
import java.awt.event.*;//WindowAdapter,WindowEvent
import java.io.IOException;
//把输入的内容发送过去
public class Client extends Frame {// 继承框架
Socket s = null;
TextField tfText = new TextField();// 全局变量,方便后面使用
TextArea taContext = new TextArea();
public static void main(String[] args) {
new Client().launchFrame();
}
//Connet
public void connect() {
try {
s = new Socket("localhost",8088);//127.0.0.1
System.out.println("connected!");
} catch (UnknownHostException e) {//找不到主机
e.printStackTrace();
} catch (IOException e) {//网卡上有错
e.printStackTrace();
}
}
//窗口
public void launchFrame() {
setLocation(800, 300);// 位置
this.setSize(400, 400);// 大小
add(tfText, BorderLayout.SOUTH);// 显示框加到南边
add(taContext, BorderLayout.NORTH);// 输入框加到北边
pack();// 处理中间大部分空白
this.addWindowListener(new WindowAdapter() {//WindowAdapter是个抽象类,里面对接口WindowListener的方法都进行了空实现。 这样编程时不需要自己再去实现全部接口WindowListener里的方法。只需覆写自己需要的方法,其他的方法 WindowAdapter都实现了
@Override
//重写windowsClosing
public void windowClosing(WindowEvent arg0) {
System.exit(0);//正常退出就行
}
});
tfText.addActionListener(new TFListener());
setVisible(true);
connect();//发完信息后直接连接
}
//把tfText里面的内容通过Enter进入到taContext
private class TFListener implements ActionListener{
//处理enter以后的动作,我们现在想enter后把输入的东西发过去,得在这里进行处理。
public void actionPerformed(ActionEvent e) {
String str = tfText.getText().trim();//trim去掉空格
//把输入的展示到展示框中。
taContext.setText(str);
tfText.setText("");//处理enter后的tfText,使其为空。
//把输入的内容发送到Server中
SendMessage(str);
}
}
//把输入的内容发送到Server中
public void SendMessage(String str) {
try {
DataOutputStream dos = new DataOutputStream(s.getOutputStream());//DataOutputStream:数据输出流允许应用程序以适当方式将基本 Java 数据类型写入输出流中。然后应用程序可以使用数据输入流将数据
dos.writeUTF(str);//发送消息
dos.flush();//你向输出流写入东西之后,执行flush(),目的是把缓冲区里的东西强行写入输出流.因为有些带缓冲区的输出流要缓冲区满的时候才输出.
dos.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
写到这里,文档开始变得卡起来来了,所以我决定分为两篇写完吧。