实现原理:
1、构建好窗体,在窗体中创建好相应的布局和控件;
2、为相应的控件(按钮:监听、连接、发送)添加事件;
3、使用到两个线程:
a、监听线程:获取服务器端输入的端口号,构建socket,监听相应端口,等待客户端来连接;
b、坚守网络阵地的线程:从网路中获取信息,并将其写出到屏幕;
源码如下:
JFrameTcp.java
/**
* @author lmb
* 实现带界面的一对一聊天
*/
public class JFrameTcp extends JFrame implements ActionListener{
private JTextField ipText;
private JTextField portText;
private JButton listen;
private JButton connect;
private TextArea msg;
private JButton send;
private JTextField c;
private Socket skt;
public JFrameTcp(){//构造方法,初始化窗体中的组件
this.setBounds(100,100,500,400);//设置窗体大小
Container cc=this.getContentPane();//构建容器
cc.setLayout(new BorderLayout());//设置容器cc的布局
//上
Panel p1=new Panel();
JLable ip=new JLable("IP:");//构建IP标签
p1.add(ip);//将其加入到p1容器中
ipText=new JTextField(13);//构建ip后的输入框
p1.add(ipText);//将其加入到p1容器中
JLable port=new JLable("Port:");//构建port标签
p1.add(port);//将其加入到p1容器中
portText=new JTextField(6);//构建port后的输入框
p1.add(portText);//将其加入到p1容器中
listen=new JButton("监听");//构建监听按钮
p1.add(listen);//将其加入到p1容器中
connect=new JButton("连接");//构建连接按钮
p1.add(connect);//将其加入到p1容器中
cc.add(p1,BorderLayout.NORTH);//将容器p1加入到容器cc的上边
//中
Panel p2=new Panel();
p2.setLayout(new BorderLayout());//设置p2的布局方式
msg=new TextArea();//构建消息显示框
p2.add(msg,BorderLayout.CENTER);//将消息显示框添加到p2的中间
cc.add(p2,BorderLayout.CENTER);//将容器p1加入到容器cc的中间
//下
Panel p3=new Panel();
p3.setLayout(new BorderLayout());//为容器p3设置一个布局方式
sendText=new JTextField(20);//构建send前的输入框
p3.add(sendText,BorderLayout.CENTER);//将其加入到p3容器的中间
send=new JButton("发送");//构建发送按钮
p3.add(send,BorderLayout.EAST);//将其加入到p3容器的右边
cc.add(p3,BorderLayout.SOUTH);//将容器p1加入到容器cc的下边
//给按钮添加事件
listen.addActionListener(this);
connect.addActionListener(this);
send.addActionListener(this);
}
//事件方法
public void actionPerformed(ActionEvent e){
if (e.getSource() == listen) {
doListen();//服务器端调用该方法,监听端口,等待客户端来连接
}
if (e.getSource() == connect) {
doConnect();//客户端调用该方法,向服务器端发起连接
}
if (e.getSource() == send) {
doSend();//发送消息
}
}
//发送消息
private void doSend() {
String s=sendText.getText();//获取发送框中的信息
try {
//构建输出流,将从发送框获取的信息写入到网络
PrintWriter pw=new PrintWriter(skt.getOutputStream());
pw.println(s);
pw.flush();
sendText.setText("");//每次点击发送之后将文本框置空
} catch (Exception e) {
e.printStackTrace();
}
}
//连接
private void doConnect() {
try {
skt=new Socket(ipText.getText(),
Integer.parseInt(portText.getText()));
new Net().start();//启动一个线程来坚守网络
} catch (Exception e) {
e.printStackTrace();
}
}
//监听
private void doListen() {
//为什么要使用线程:监听的时候会发生阻塞,使用线程来避免阻塞
new ListenThread().start();//在线程中实施监听
new Net().start();//启动一个线程来坚守网络
}
/**
* 监听线程(内部类)
* 使用内部类的原因:在该线程中要使用portText、appendMsg两个变量,我们可以在开启线程的时候使用构造方法
* 将这两个变量传进来,但是如果我们在线程中要用到的变量比较多,这时候再用构造方法来传变量就相当麻烦了,
* 于是,我们可以使用内部类来达到直接使用外部类成员变量的目的。
*
*/
class ListenThread extends Thread{
public void run(){
String s=portText.getText();//获取portText中输入的端口号
ServerSocket sskt=null;
try {
sskt=new ServerSocket(Integer.parseInt(s));
//当开始监听时给出提示信息
appendMsg.append("监听中……\n");
skt=sskt.accept();//等待其他人来连接
appendMsg.append(skt.getInetAddress().getHostAddress() + "与我建立连接\n");
} catch (Exception e) {
e.printStackTrace();
}
}
}
//用来坚守网络阵营,然后将从网络中获取的信息写出到屏幕
class Net extends Thread{
public void run(){
try {
BufferedReader br=new BufferedReader(
new InputStreamReader(skt.getInputStream()));
for (;;) {
appendMsg.append(skt.getInetAddress().getHostAddress() +
":" + br.readLine() + "\n");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
new JFrameTcp().setVisible(true);//设置窗体可见
}
}
运行效果:
该例只是简单实现了一下一对一聊天的主要功能,其中还有好多问题没有解决,不如:在没有输入端口号和IP地址的情况下直接发送消息,也是可以发送的,等等,边学边改进,努力做到更好,后续会有更好的实现!