昨天同学问了一个关于socket通信编程的程序,花了我一晚时间去修改,才将它改好。这个程序主要是做一个服务端接收处理多个客户端请求的例程,具体如下代码所示,挺好的学习例子:
server.java
/*
* server.java
*
* Created on __DATE__, __TIME__
*/
package socket_;
import java.awt.event.KeyEvent;
import java.io.*;
import java.net.*;
//import socket_.SocketServer.Getinfo;
/**
*
* @author __USER__
*/
public class server extends javax.swing.JFrame implements Runnable{// 继承runnable之后,通过Runnable接口来实现多线程的功能
private ServerSocket serverSocket;
private Socket socket;
private boolean hasStart;//是否开启服务器的标志
/** Creates new form server */
public server() {
initComponents();
hasStart = false;
try {
serverSocket = new ServerSocket(10000);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
// GEN-BEGIN:initComponents
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents() {
jScrollPane1 = new javax.swing.JScrollPane();
jTextArea1 = new javax.swing.JTextArea();
jLabel1 = new javax.swing.JLabel();
jButton1 = new javax.swing.JButton();
jTextField1 = new javax.swing.JTextField();
jButton2 = new javax.swing.JButton();
jButton3 = new javax.swing.JButton();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
setTitle("\u670d\u52a1\u5668");
jTextArea1.setColumns(20);
jTextArea1.setRows(5);
jScrollPane1.setViewportView(jTextArea1);
jLabel1.setText("\u53d1\u9001");
jButton1.setText("\u542f\u52a8");
jButton1.setActionCommand("jButton1");
jButton1.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jButton1ActionPerformed(evt);
}
});
jButton2.setText("\u53d1\u9001");
jButton2.setActionCommand("jButton2");
jButton2.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jButton2ActionPerformed(evt);
}
});
jButton3.setText("\u9000\u51fa");
jButton3.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jButton3ActionPerformed(evt);
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(
getContentPane());
getContentPane().setLayout(layout);
layout
.setHorizontalGroup(layout
.createParallelGroup(
javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(
layout
.createSequentialGroup()
.addGap(18, 18, 18)
.addGroup(
layout
.createParallelGroup(
javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(
layout
.createSequentialGroup()
.addComponent(
jButton1)
.addGap(
139,
139,
139)
.addComponent(
jButton2)
.addPreferredGap(
javax.swing.LayoutStyle.ComponentPlacement.RELATED,
161,
Short.MAX_VALUE)
.addComponent(
jButton3))
.addGroup(
layout
.createSequentialGroup()
.addComponent(
jLabel1)
.addGap(
26,
26,
26)
.addComponent(
jTextField1,
javax.swing.GroupLayout.DEFAULT_SIZE,
421,
Short.MAX_VALUE))
.addComponent(
jScrollPane1,
javax.swing.GroupLayout.PREFERRED_SIZE,
471,
javax.swing.GroupLayout.PREFERRED_SIZE))
.addContainerGap(19, Short.MAX_VALUE)));
layout
.setVerticalGroup(layout
.createParallelGroup(
javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(
layout
.createSequentialGroup()
.addContainerGap()
.addComponent(
jScrollPane1,
javax.swing.GroupLayout.PREFERRED_SIZE,
239,
javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(29, 29, 29)
.addGroup(
layout
.createParallelGroup(
javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jLabel1)
.addComponent(
jTextField1,
javax.swing.GroupLayout.PREFERRED_SIZE,
javax.swing.GroupLayout.DEFAULT_SIZE,
javax.swing.GroupLayout.PREFERRED_SIZE))
.addGap(18, 18, 18)
.addGroup(
layout
.createParallelGroup(
javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jButton1)
.addComponent(jButton3)
.addComponent(jButton2))
.addContainerGap(28, Short.MAX_VALUE)));
pack();
}// </editor-fold>
// GEN-END:initComponents
private class CreateSocket extends Thread{
private Socket socket;
private BufferedReader bufferedReader;
private PrintWriter printWriter;
public CreateSocket(Socket socket){
this.socket = socket;
try {
bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
// printWriter.print(b)
String temp;
try {
temp = bufferedReader.readLine();
while(!"exit".equals(temp))
{
jTextArea1.append("服务器接收到客户" + socket.getLocalPort() + ":" + socket.getPort() + "的消息:" + temp + "\n");
// jTextArea1.setCaretPosition(jTextArea1.getText().length());//设置新加入的数据显示出来,滚动条向下滑
//休眠一下再试读下一条
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
temp = bufferedReader.readLine();
}
} catch (IOException e) {
e.printStackTrace();
jTextArea1.append(e.toString() + "\n");
}
try {
bufferedReader.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
jTextArea1.append(e.getStackTrace() + "\n");
}
}
}
private void jButton3ActionPerformed(java.awt.event.ActionEvent evt) {
/*try {
jTextArea1.append("服务器退出!\n");
printWriter.println("exit");
printWriter.flush();
} catch (Exception e) {
jTextArea1.append(e.toString() + "\n");
} finally {
System.exit(0);
}*/
System.exit(0);
}
private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {
/*
try {
// printWriter需要分配空间
printWriter.println(jTextField1.getText());
printWriter.flush();
jTextArea1.append("服务器端消息:" + jTextField1.getText() + "\n");// .append("服务器端消息:"
// +
// jTextField1.getText());
jTextField1.setText("");
} catch (Exception e) {
System.out.println("run error");
jTextArea1.append(e.toString() + "\n");
}*/
}
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
if(hasStart) return;
hasStart = true;
jTextArea1.append("服务器已启动!" + "\n");
jTextArea1.setCaretPosition(jTextArea1.getText().length());
//再开启一个线程,因为主线程要用来作界面的显示。如果这里没用线程而直接开启serverSocket的监听,那就会在界面上看不到效果,因为主线程一直在做监听工作
Thread thread = new Thread(this);
thread.start();
// for (int i = 0; i < 10; i++) {
// Thread thread = new Thread(this); // 关键性,启动一个任务
// thread.start();
// }
}
@Override
public void run() {
while(true)
{
try {
socket = serverSocket.accept();
} catch (IOException e) {
e.printStackTrace();
}
CreateSocket createSocket = new CreateSocket(socket);
createSocket.start();
}
}
/**
* @param args
* the command line arguments
*/
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new server().setVisible(true);
}
});
}
// Variables declaration - do not modify
private javax.swing.JButton jButton1;
private javax.swing.JButton jButton2;
private javax.swing.JButton jButton3;
private javax.swing.JLabel jLabel1;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JTextArea jTextArea1;
private javax.swing.JTextField jTextField1;
// End of variables declaration//GEN-END:variables
}
Client.java
/*
* Client.java
*
* Created on __DATE__, __TIME__
*/
package socket_;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
//import socket_.SocketClient.Getinfo;
/**
*
* @author __USER__
*/
public class Client extends javax.swing.JFrame {
Socket socket;
BufferedReader bufferedReader;
PrintWriter printWriter;//=new PrintWriter("dasasadasdasd");
/** Creates new form Client */
public Client() {
initComponents();
}
/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
*/
//GEN-BEGIN:initComponents
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents() {
jScrollPane1 = new javax.swing.JScrollPane();
jTextArea1 = new javax.swing.JTextArea();
jLabel1 = new javax.swing.JLabel();
jTextField1 = new javax.swing.JTextField();
jButton1 = new javax.swing.JButton();
jButton2 = new javax.swing.JButton();
jButton3 = new javax.swing.JButton();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
setTitle("\u5ba2\u6237\u7aef");
jTextArea1.setColumns(20);
jTextArea1.setRows(5);
jScrollPane1.setViewportView(jTextArea1);
jLabel1.setText("jLabel1");
jTextField1.setText("jTextField1");
jButton1.setText("\u8fde\u63a5");
jButton1.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jButton1ActionPerformed(evt);
}
});
jButton2.setText("\u53d1\u9001");
jButton2.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jButton2ActionPerformed(evt);
}
});
jButton3.setText("\u9000\u51fa");
jButton3.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jButton3ActionPerformed(evt);
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(
getContentPane());
getContentPane().setLayout(layout);
layout
.setHorizontalGroup(layout
.createParallelGroup(
javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(
layout
.createSequentialGroup()
.addGap(19, 19, 19)
.addGroup(
layout
.createParallelGroup(
javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(
jScrollPane1,
javax.swing.GroupLayout.DEFAULT_SIZE,
482,
Short.MAX_VALUE)
.addGroup(
javax.swing.GroupLayout.Alignment.TRAILING,
layout
.createSequentialGroup()
.addComponent(
jLabel1)
.addGap(
18,
18,
18)
.addComponent(
jTextField1,
javax.swing.GroupLayout.DEFAULT_SIZE,
422,
Short.MAX_VALUE))
.addGroup(
layout
.createSequentialGroup()
.addComponent(
jButton1)
.addGap(
149,
149,
149)
.addComponent(
jButton2)
.addPreferredGap(
javax.swing.LayoutStyle.ComponentPlacement.RELATED,
162,
Short.MAX_VALUE)
.addComponent(
jButton3)))
.addContainerGap()));
layout
.setVerticalGroup(layout
.createParallelGroup(
javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(
layout
.createSequentialGroup()
.addGap(21, 21, 21)
.addComponent(
jScrollPane1,
javax.swing.GroupLayout.PREFERRED_SIZE,
234,
javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(18, 18, 18)
.addGroup(
layout
.createParallelGroup(
javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jLabel1)
.addComponent(
jTextField1,
javax.swing.GroupLayout.PREFERRED_SIZE,
javax.swing.GroupLayout.DEFAULT_SIZE,
javax.swing.GroupLayout.PREFERRED_SIZE))
.addGap(41, 41, 41)
.addGroup(
layout
.createParallelGroup(
javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jButton1)
.addComponent(jButton3)
.addComponent(jButton2))
.addContainerGap(30, Short.MAX_VALUE)));
pack();
}
private void jButton3ActionPerformed(java.awt.event.ActionEvent evt) {
try {
jTextArea1.append("服务器退出!\n");
printWriter.println("exit");
printWriter.flush();
} catch (Exception e) {
jTextArea1.append(e.toString() + "\n");
} finally {
System.exit(0);
}
}
private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {
try {
printWriter.println(jTextField1.getText());
printWriter.flush();
jTextArea1.append("客户器端发送消息:" + jTextField1.getText() + "\n");// .append("服务器端消息:" + jTextField1.getText());
jTextField1.setText("");
jTextField1.grabFocus();//获取焦点
} catch (Exception e) {
System.out.println("send error");
jTextArea1.append(e.toString() + "\n");
}
}
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
try {
socket = new Socket(InetAddress.getLocalHost(), 10000);
jTextArea1.append("客户端已加入 \n");
bufferedReader = new BufferedReader(new InputStreamReader(socket
.getInputStream()));
printWriter = new PrintWriter(socket.getOutputStream());
jButton2.setEnabled(true);
} catch (Exception e) {
jTextArea1.append(e.toString() + "\n");
}
}
/**
* @param args the command line arguments
*/
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new Client().setVisible(true);
}
});
}
//GEN-BEGIN:variables
// Variables declaration - do not modify
private javax.swing.JButton jButton1;
private javax.swing.JButton jButton2;
private javax.swing.JButton jButton3;
private javax.swing.JLabel jLabel1;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JTextArea jTextArea1;
private javax.swing.JTextField jTextField1;
// End of variables declaration//GEN-END:variables
}
要注意的就是用多线程的时候一定要有fork-join模型在脑子里:也就是我们要控制一个主线程(界面一般要做主线程,不然无法保持显示最新信息,而是处于像缰死的状态),然后分出子线程去完成所要的工作,最后再归到主线程。对于多个客户端的请求,服务端要对每个客户的请求都开一个新的socket和新的bufferedReader及新的printWriter用于与每个客户交互,不能共用。我们可以在多次运行client.java类,模拟多个客户端,我们从服务器端打印出来的信息可以看到客户端的端口号是不同的,因为对同一台机子的应用的区分就是靠端口来区分的。