摘要:本文基于传输层(TCP协议)和网络层(IP协议)利用Java Socket(套接字)来实现双机通信,它采用客户/服务器通信机制,使客户端和服务器端通过Socket接口在网络上实现连接和数据交换。通过本文对Socket通信的实现简单的分析和讲解,让大家对Socket的原理有一个较清晰的认识。
关键词:JAVA通信、套接字、SocketServer类、客户机/服务器模式
2程序功能的实现
2.1程序的结构
本程序共有四个类:LoginWindow登录窗口类、MainActivity主类、chatServer服务器类、chatClinet客户端类,均放在一个LANChatSoftware文件中,如图所示:
2.3主界面的开发
主界面主要有1个label标签和2个button按钮,实现打开服务器端,和客户端窗体。
2.4服务器端的开发
(1)服务器端实现对主机某个端口不断监听,并不断接受客户端的连接请求,当受到连接后打印客户的的信息并向客户端完成一次服务。
(2)使用readUTF方法的DataInputStream处理流提供的方法,功能为读取满足UTF格式的字符串。并使用writeUTF方法为DataOutputStream处理流提供的方法,功能为写出满足UTF 格式的字符。一般在网络中发生消息都在发送端用writeUTF方法写,在接收端使用readUTF方法读取,这样程序的兼容性强,不容易受到乱码。
2.5客户端的开发
(1)客户端用于对某个固定IP的服务器进行连接,接着向服务器发送一条消息,最后接受服务器的返回消息并打印。
(2)为了与服务器对应,发送消息还要使用DataInputStream的writeUTF方法。同时应该注意的两边的首发顺序是互逆的,服务器现售后发,客户端先发后收。
3程序的具体代码
3.1登陆界面
/*
* 登录界面
*/
public class LoginWindow extends JDialog implements ActionListener{
//自定义5个面板
JPanel p1=new JPanel();
JPanel p2=new JPanel();
JPanel p3=new JPanel();
JPanel p4=new JPanel();
JPanel p5=new JPanel();
int i=0;//记录密码输错的次数
JTextField txtUserName=new JTextField(15);// 用户名文本框
JPasswordField txtPassword=new JPasswordField(15);// 密码域文本框
JButton ok=new JButton("确定");// 确定按钮
JButton cancel=new JButton("取消");//取消按钮
// LoginWindow方法
public LoginWindow(){
Container contentPane=this.getContentPane();//获取内容面板
contentPane.setLayout(new GridLayout(5,1));//设置5行1列的网格布局
p2.add(new JLabel("用户名:"));
p2.add(txtUserName);
p3.add(new JLabel("密码:"));
p3.add(txtPassword);
p4.add(ok);
p4.add(cancel);
// 添加按钮监听
ok.addActionListener(this);
cancel.addActionListener(this);
contentPane.add(p1);
contentPane.add(p2);
contentPane.add(p3);
contentPane.add(p4);
contentPane.add(p5);
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
setSize(350, 250);
Dimension screen=Toolkit.getDefaultToolkit().getScreenSize();
setLocation((screen.width-300)/2,(screen.height-220)/2);
setTitle("聊天软件(by:春天的故事原创)-登陆窗口");
setResizable(false);
setVisible(true);
}
@Override
public void actionPerformed(ActionEvent e) {
String password=new String(txtPassword.getPassword());
//String
if(e.getSource()==ok||e.getSource()==txtPassword){
if (txtUserName.getText().trim().equals("gyc")&&password.equals("1")) {
this.dispose();//关闭窗口
new MainActivity();//调出MainActivity聊天主窗口
}
else {
if(++i>=3){//三次密码输入错误关闭窗口退出程序
this.dispose();
System.exit(0);
}
JOptionPane.showMessageDialog(null, "用户名或密码错误!联系作者获得用户密码");
txtUserName.requestFocus();//设置焦点
txtUserName.setSelectionStart(0);//设置选中文本开始位置
txtUserName.setSelectionEnd(txtUserName.getText().length());
}
}
else if(e.getSource()==cancel){//单机取消按钮
this.dispose();
System.exit(0);
}
else if (e.getSource()==txtPassword) {
txtPassword.requestFocus();//密文
}
}
public static void main(String [] args){
new LoginWindow();//实例化LoginWindow
}
}
3.2主窗口
/*
* 程序的主窗口
* 打开客户端,服务器
*/
public class MainActivity extends JFrame implements ActionListener{
private static final long serialVersionUID = 1L;
JPanel p1=new JPanel();
JPanel p2=new JPanel();
JPanel p3=new JPanel();
Button btn1=new Button("打开服务器");
Button btn2=new Button("打开客户端");
Button ok=new Button("确定");
Label lab1=new Label("先开启服务器再开启客户端");
Dialog dia;
public MainActivity(){
super("聊天软件(by:春天的故事原创)-主界面");
Container con=getContentPane();//获得内容窗格
con.setLayout(new GridLayout(4,1));//网格布局
p1.add(lab1);
p2.add(btn1);
p3.add(btn2);
con.add(p1);
con.add(p2);
con.add(p3);
btn1.addActionListener(this);
btn2.addActionListener(this);
setSize(350, 250);
Dimension screen=Toolkit.getDefaultToolkit().getScreenSize();
setLocation((screen.width-300)/2,(screen.height-220)/2);
setTitle("聊天软件(by:春天的故事原创)-登陆窗口");
setResizable(false);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
if (e.getSource()==btn1) {
new chatServer();
}else if (e.getSource()==btn2) {
this.dispose();//关闭窗口
new chatClient();
}
}
public static void main(String[] args) {
MainActivity a=new MainActivity();
a.setSize(280,180);
a.setLocation(350,300);
}
}
3.3服务器端的实现
服务器端用一个类chatServer来实现,创建一个ServerSocket类的对象,实现对端口6500的不断监听,直到受到客户端的信息。
服务器端
/*
* 该类主方法将接收数据的部分放在线程中,他始终在后台运行,一旦对方发来数据,就立即显示在界面上
*/
JTextArea showArea;
JTextField msgText;
JFrame mainJframe;
JButton sentBtn;
JScrollPane JSPane;
JPanel pane;
DataInputStream inFromClient;
DataOutputStream outTOClient;
public chatServer() {
// TODO Auto-generated constructor stub
// 设置界面
mainJframe=new JFrame("聊天软件(by:春天的故事原创)-服务器端");
Container con = mainJframe.getContentPane();
showArea=new JTextArea();//创建文本域
showArea.setEditable(false);
showArea.setLineWrap(true);
JSPane=new JScrollPane(showArea);//创建滚动面板
msgText=new JTextField();//创建文本框
msgText.setColumns(30);
msgText.addActionListener(this);
sentBtn=new JButton("发送");//发送按钮
sentBtn.addActionListener(this);
pane=new JPanel();
pane.setLayout(new FlowLayout());//组件布局
pane.add(msgText);
pane.add(sentBtn);
con.add(JSPane,BorderLayout.CENTER);
con.add(pane,BorderLayout.SOUTH);
mainJframe.setSize(500,400);
mainJframe.setVisible(true);
mainJframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// 通信
try {
InetAddress client=InetAddress.getLocalHost();
ServerSocket serverSocket = new ServerSocket(5500);//创建服务套接字
showArea.append("您的连接IP地址是:"+client.getHostAddress()+"\n\n正在等待连接请求...\n");
Socket connectTOClient = serverSocket.accept();//监听客户端连接
inFromClient = new DataInputStream(connectTOClient.getInputStream());
outTOClient = new DataOutputStream(connectTOClient.getOutputStream());
Thread thread=new Thread((Runnable) this);//启动线程在后台来接收对方的消息
thread.setPriority(Thread.MIN_PRIORITY);
thread.start();
} catch (Exception e) {
// TODO: handle exception
showArea.append("抱歉,服务器创建不成功!请联系作者!\n");
msgText.setEditable(false);
sentBtn.setEnabled(false);
}
}
@Override
public void actionPerformed(ActionEvent e) {//响应按钮事件,发送消息给对方
// TODO Auto-generated method stub
String s=msgText.getText();
if (s.length()>0) {
try {
outTOClient.writeUTF(s);
outTOClient.flush();
showArea.append(" 我说:"+msgText.getText()+"\n");
msgText.setText(null);
} catch (Exception e1) {
// TODO: handle exception
showArea.append("你的消息是:“"+msgText.getText()+"”未能发出去\n");
}
}
}
public void run() {//本线程负责将客户端传来的消息显示在对话区域
// TODO Auto-generated method stub
try {
while (true) {
showArea.append("对方说:"+inFromClient.readUTF()+"\n");
Thread.sleep(1000);
}
} catch (IOException e1) {}
catch(InterruptedException e){}
} }
3.4客户端的实现
客户端
/*
* 我们还要创建一个和服务器 对等的客户端页面来实现和服务器端相似的功能
*/
public class chatClient implements ActionListener,Runnable{
JTextArea showArea;
JTextField msgText;
JFrame mainJframe;
JButton sentBtn;
JScrollPane JSPane;
JPanel pane;
Container con;
Thread thread=null;
Socket connectTOServer;
DataInputStream inFromServer;
DataOutputStream outTOServer;
public chatClient() {
// TODO Auto-generated constructor stub
mainJframe=new JFrame("聊天软件(by:春天的故事原创)-客户端");
con=mainJframe.getContentPane();
showArea=new JTextArea();
showArea.setEditable(false);
showArea.setLineWrap(true);
JSPane=new JScrollPane(showArea);
msgText=new JTextField();
msgText.setColumns(30);
msgText.addActionListener(this);
sentBtn=new JButton("发送");
sentBtn.addActionListener(this);
pane=new JPanel();
pane.setLayout(new FlowLayout());
pane.add(msgText);
pane.add(sentBtn);
con.add(JSPane,BorderLayout.CENTER);
con.add(pane,BorderLayout.SOUTH);
mainJframe.setSize(500,400);
mainJframe.setVisible(true);
mainJframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Socket client=new Socket();
// 通信
try {
InetAddress client1=InetAddress.getLocalHost();
// 创建套接字连接服务器
connectTOServer = new Socket("localhost",5500);
inFromServer = new DataInputStream(connectTOServer.getInputStream());
outTOServer = new DataOutputStream(connectTOServer.getOutputStream());
showArea.append("服务器"+client1.getHostAddress() +"\n\n连接成功,开始通信\n");
thread=new Thread(this);//创建线程在后台处理对方的消息
thread.setPriority(Thread.MIN_PRIORITY);
thread.start();
} catch (Exception e) {
// TODO: handle exception
showArea.append("对不起,没能找到服务器\n");
//文本框,发送按钮不可用
msgText.setEditable(false);
sentBtn.setEnabled(false);
}
}
@Override
public void actionPerformed(ActionEvent e) {//按钮响应事件,发送消息给对方
// TODO Auto-generated method stub
String s=msgText.getText();
if (s.length()>0) {
try {
outTOServer.writeUTF(s);
outTOServer.flush();
showArea.append(" 我说:"+msgText.getText()+"\n");
msgText.setText(null);
} catch (Exception e1) {
// TODO: handle exception
showArea.append("你的消息是:“"+msgText.getText()+"”未能发出去\n");
}
}
}
public void run() {//本线程负责将服务器传来的信息显示在对话区域
// TODO Auto-generated method stub
try {
while (true) {
showArea.append("对方说:"+inFromServer.readUTF()+"\n");
Thread.sleep(1000);
}
} catch (IOException e1) {
// TODO: handle exception
}catch(InterruptedException e){}
}
}