多线程最终服务器和客户端集成一体【swing程序】
我们通过窗体形式来实验这个socket通信。
窗体形式,其实把服务器和客户端集成在一起。因此每个窗体程序既是客户端又是服务器端。
窗体应用程序思路:运行窗体后,此服务器在后台默默监听,而前端展示给客户的是我们所谓的客户端。若服务器接受到相应的消息,则反应在客户端窗体消息窗口中。
首先脑海中窗体形式如下:
![](https://img-my.csdn.net/uploads/201301/22/1358829639_3674.png)
为CS新手补习点图形化知识:
Java中使用Awt和Swing类来完成图形化界面。其中Awt是abstract window tookit(抽象窗口工具库),他是最早的gui库。
那为什么会出现swing类呢?他们两者的关联是什么呢?
1.Awt在所有平台上展示的界面很丑陋,功能也有限。
2.Awt是非常笨拙、非面向对象的编程方式。
3.Awt是调用底层平台的GUI实现,因此限制了Awt支持的GUI的实现。
而swing则:
1.开发的图形界面要不awt优秀。
2.采用mvc架构设计,使显示数据与数据来源隔离。
3.是纯java开发,所以在任何平台都展示一样的效果,而不是依赖平台。
他们之间的关联是:swing是在awt的基础上开发出来,现在很少使用awt组件,大部分使用swing组件。
在AWT组件结构:
![](https://img-my.csdn.net/uploads/201301/22/1358829684_9728.png)
容器的继承关系如下:
![](https://img-my.csdn.net/uploads/201301/22/1358829702_8133.png)
事件关系如下:
![](https://img-my.csdn.net/uploads/201301/22/1358829715_6716.png)
还有就是布局管理器组件了。代码中会有相应的解释。
而swing组件关系:
![](https://img-my.csdn.net/uploads/201301/22/1358829736_2223.png)
分析我们脑海中的窗体,差不多我们都知道布局。
因为是Tcp协议测试,所以是基于面向链接发送数据。所以在发送之前,首先要连接服务器,若连接上,然后再发送数据。
客户端与服务器消息交流都是在中间的消息框中展示。
在这个窗体中,到底如何布局呢?
这就需要awt的布局管理器。在代码中,我们用的是BorderLayout组件,布局方式是分为东南西北中,若不指定方向,则默认是中间。
BoderLayout效果如下:
![](https://img-my.csdn.net/uploads/201301/22/1358829776_4774.png)
在构造器启动服务,服务一直在监听客户端。
若监听到,则开启接受信息的线程,其中线程类作为此主线程类的内部类。
代码如下:并且其中有详细的注释:
package com.test;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
public class SocketTcpDemo1 extends JFrame implements ActionListener {
//输入的IP标签
private JLabel ipLable;
//输入对方的ip地址
private JTextField sendIp;
//连接按钮
private JButton linkBtn;
//发送的数据框
private JTextField sendText;
//显示数据显示框
private JTextArea showMessage;
//创建滚动条 主要是创建垂直滚动条
private JScrollBar verscrollbar;
//默认端口号
private int port=30000;
//发送按钮
private JButton sendBtn;
//默认发送的ip
public String defaultSendIp="127.0.0.1";
//连接socket
private Socket socket;
//构造方法
public SocketTcpDemo1()
{
this.setTitle("测试tcp通信的窗体");
this.setBounds(200, 200, 500, 500);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLayout(new BorderLayout());
//定义消息显示数据框
showMessage=new JTextArea();
//设置不可以编辑,消息显示的数据框
showMessage.setEditable(false);
//消息显示数据框不够,需要添加滚动条
JScrollPane messageScrollPane=new JScrollPane(showMessage);
verscrollbar=messageScrollPane.createVerticalScrollBar();
//把滚动面板放在JFrame容器中
this.add(messageScrollPane, BorderLayout.CENTER);
//设置连接ip的数据框
JPanel panel=new JPanel(new BorderLayout(5,2));
//设置发送默认的ip
sendIp=new JTextField(defaultSendIp);
panel.add(sendIp,BorderLayout.CENTER);
//连接按钮
linkBtn=new JButton("连接");
panel.add(linkBtn,BorderLayout.EAST);
//ip标签显示
ipLable =new JLabel("对方IP:");
panel.add(ipLable,BorderLayout.WEST);
//然后把这个panel放在Frame的上方
this.add(panel,BorderLayout.NORTH);
//设置发送数据框的位置
JPanel sendDataPanel=new JPanel(new BorderLayout(5,2));
//发送数据文本框
sendText=new JTextField();
sendDataPanel.add(sendText,BorderLayout.CENTER);
//发送按钮
sendBtn=new JButton("发送");
sendDataPanel.add(sendBtn,BorderLayout.EAST);
//然后把这个panel放在Frame的下方
this.add(sendDataPanel,BorderLayout.SOUTH);
//可视化
this.setVisible(true);
//添加事件源
linkBtn.addActionListener(this);
sendBtn.addActionListener(this);
//启动监听服务方法
TcpServer();
}
public void actionPerformed(ActionEvent e) {
if(linkBtn==e.getSource())
{
//判断是否能连接上
try {
socket=new Socket(sendIp.getText().trim(),port);
JOptionPane.showMessageDialog(this, "连接成功");
} catch (Exception e1) {
try {
JOptionPane.showMessageDialog(this, "连接失败");
socket.close();
} catch (IOException e2) {
// TODO Auto-generated catch block
e2.printStackTrace();
}
}
}
else if(sendBtn==e.getSource())
{
try {
PrintStream ps=new PrintStream(socket.getOutputStream());
ps.println(sendText.getText());
//显示框中显示
showMessage.append(InetAddress.getLocalHost().getHostAddress()+"说:"+sendText.getText()+"\n");
sendText.setText("");
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
public static void main(String[] args) {
SocketTcpDemo1 tcpSocket=new SocketTcpDemo1();
}
//服务监听
private void TcpServer()
{
try {
ServerSocket ss = new ServerSocket(port);
while(true)
{
//此行代码会阻塞,将一直等待别人的连接
Socket s=ss.accept();
new Thread(new ServerThread(s)).start();
}
}
catch (IOException e)
{
e.printStackTrace();
}
}
class ServerThread implements Runnable {
//定义当前线程所处理的socket
Socket s=null;
//该线程所处理的socket所对应的输入流
BufferedReader br=null;
public ServerThread(Socket s)
{
try
{
this.s=s;
//初始化socket对应的输入流
br=new BufferedReader(new InputStreamReader(s.getInputStream()));
}
catch(IOException e)
{
e.printStackTrace();
}
}
@Override
public void run()
{
try {
String content = null;
while((content = br.readLine()) != null) {
//服务器发送的消息
showMessage.append(sendIp.getText().trim()+"说:" + content + "\n");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
效果如下:
测试连接窗体:
![](https://img-my.csdn.net/uploads/201301/22/1358829797_8003.png)
![](https://img-my.csdn.net/uploads/201301/22/1358829831_5233.png)
互相通信窗口:
![](https://img-my.csdn.net/uploads/201301/22/1358829873_3469.png)
至此,目录中的总结都已经整理完毕。
整理这部分内容的原因:第一:socket通信,当时元旦两天测试出来,可当时遇到点问题,所以索性把练习之旅记录下来。第二:练习基础中的多线程,多线程这部分,基本上理论都可以讲出来,但是项目中一直没有用到。第三:初次接触图形化界面,因为这方面内容没有涉及到,大部分是在研究web方向,但是就像揭开他的神秘面纱。
呵呵,基于以上原因,才有了此次总结的想法。最近一直在研究其他的东西,所以博客没有及时贴出来。(*^__^*)嘻嘻
编译打包成jar包,这部分内容暂时不更新博客了。其实,在打包的过程中应用了ant打包。对于ant的应用,我们有时间再更新博客。