由于需要做个一个CS的系统,需要实现服务器和客户端的交互,那么这个通讯的基本类就很重要了 为了写这个基本类 真是费了一番周折 。要把这个做好 多线程需要理解的比较好。因为 服务器需要一直监听客户端的请求,而客户端也要一直监听 服务器的请求。
首先是 客户端:
package client;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
public class Client implements Runnable{
private Socket client;
private String ip = "127.0.0.1";
private int port = 8888;
private ObjectOutputStream out;
//连接
public void conn(){
try {
client = new Socket(ip,port);
if(client.getOutputStream()!=null) {
out = new ObjectOutputStream(client.getOutputStream());
}
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//启动线程
public void run() {
try {
[color=red]ObjectInputStream in = new ObjectInputStream(client.getInputStream());
[/color]
boolean flag = true;
while(flag){
Object obj = in.readObject();
if(obj!=null){
String s = (String)obj;
System.out.println("收到服务器消息:" + s);
if(s.equals("shutdown")){
flag = false;
}
}
}
System.out.println("ByeBye!");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//向服务器发送object
public void sendMessage(Object obj){
//ObjectOutputStream out=null;
try {
out.writeObject(obj);
out.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
服务器端有两个类:
主类:
package server;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
public class MainServer implements Runnable{
private int port = 8888;
List arr = new ArrayList();
//初始化方法
public void init(int port) {
this.port = port;
}
public void run() {
try {
ServerSocket server = new ServerSocket(port);
System.out.println("--------服务器启动---------");
while (true) {
Socket socket = server.accept();
System.out.println("--------客户端连接---------");
String ip = socket.getLocalSocketAddress()+"";
ip = ip.substring(1,10);
ServerAccept acc = new ServerAccept();
acc.init(socket,ip);
arr.add(acc);//将客户端放入数组中
acc.start();
}
} catch (Exception e) {
e.printStackTrace();
}
}
//遍历数组,对指定IP发送object
public void sendMessage(String ip,Object obj) {
for(int i=0;i
ServerAccept a = (ServerAccept)arr.get(i);
String temp = a.getIp();
if(temp.equals(ip)) {
try {
a.getOut().writeObject(obj);
a.getOut().flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
线程类:
package server;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
public class ServerAccept extends Thread {
//private ObjectInputStream in;
private ObjectOutputStream out;
private String ip;
private String s;
private Socket socket;
public void init(Socket socket,String ip) {
try {
this.socket=socket;
this.out = new ObjectOutputStream(socket.getOutputStream());
this.ip=ip;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//一直监听客户端发送到来的消息
public void run() {
boolean flag = true;
try {
ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
[color=blue] while(flag){
Object obj = in.readObject();
if(obj!=null){
String s = (String)obj;
if(s.equals("关闭")){
flag = false;
continue;
}
System.out.println("客户端消息:" + s);
}
[/color]
}
// System.out.println("ByeBye!");
} catch (IOException e) {
// TODO Auto-generated catch block
flag = false;
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
flag = false;
e.printStackTrace();
}
}
public String getIp() {
return ip;
}
public ObjectOutputStream getOut() {
return out;
}
public String getS() {
return s;
}
}
在这个过程中 主要遇到了 最令人 头疼的 java.io.StreamCorruptedException: invalid type code: AC 异常。
一直不明白怎么回事 后来才知道 原来是 ObjectOutputStream 流只可以对应,不可以生成多次,
当时我把 红色的那一段代码 放在了 while 里面 结果一直报错,只可以发送一次,第二次就不行,因为你又new 了一个 ObjectOutputStream 那是不可以的 同一个 socket。
还有一个异常就是 java.io.ObjectInputStream$BlockDataInputStream.peekByte 异常
这个意思我查阅了一下 原来是 到达了文件的末尾,程序却没有正常结束读取文件内容 也就是蓝色的这一段 会抛异常 因为无法终止while 。 后来我 要结束流的时候就发送消息“关闭” 然后就 关闭了。socket是面向连接的 所以终止任何一方都会异常吧 ,现在都是捕获到异常然后 关闭 呵呵呵 。不过应该有好的方法。 我毕竟写的很多缺陷了。
这个基本类 实现了监听,主要就是在于 线程中的 连个while 循环 在一直等待着对方的消息,也是这里不好处理。
希望大家提出意见 改进啊
测试类:
package client;
public class ClientTest {
public static void main(String []args) {
Client client = new Client();
client.conn();
new Thread(client).start();
client.sendMessage("我发送的消息!");
client.sendMessage("这也是我发送的消息!");
client.sendMessage("关闭");
}
服务器:
/*
* ServerForm.java
*
* Created on __DATE__, __TIME__
*/
package server;
import java.util.HashMap;
/**
*
* @author __USER__
*/
public class ServerForm extends javax.swing.JFrame {
/** Creates new form ServerForm */
public ServerForm() {
initComponents();
}
/*
* 创建server对象
*
*/
MainServer server = new MainServer();
//ServerTest ser = new ServerTest();
/** 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
//
private void initComponents() {
jButton1 = new javax.swing.JButton();
jTextField1 = new javax.swing.JTextField();
jButton2 = new javax.swing.JButton();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
jButton1.setText("\u542f\u52a8\u670d\u52a1\u5668");
jButton1.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jButton1ActionPerformed(evt);
}
});
jButton2.setText("\u53d1\u9001\u6d88\u606f");
jButton2.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jButton2ActionPerformed(evt);
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(
getContentPane());
getContentPane().setLayout(layout);
layout
.setHorizontalGroup(layout
.createParallelGroup(
javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(
javax.swing.GroupLayout.Alignment.TRAILING,
layout
.createSequentialGroup()
.addContainerGap(47, Short.MAX_VALUE)
.addGroup(
layout
.createParallelGroup(
javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jButton1)
.addGroup(
layout
.createSequentialGroup()
.addComponent(
jTextField1,
javax.swing.GroupLayout.PREFERRED_SIZE,
139,
javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(
18,
18,
18)
.addComponent(
jButton2)))
.addContainerGap()));
layout
.setVerticalGroup(layout
.createParallelGroup(
javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(
javax.swing.GroupLayout.Alignment.TRAILING,
layout
.createSequentialGroup()
.addContainerGap(76, Short.MAX_VALUE)
.addComponent(jButton1)
.addGap(70, 70, 70)
.addGroup(
layout
.createParallelGroup(
javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jButton2)
.addComponent(
jTextField1,
javax.swing.GroupLayout.PREFERRED_SIZE,
javax.swing.GroupLayout.DEFAULT_SIZE,
javax.swing.GroupLayout.PREFERRED_SIZE))
.addGap(104, 104, 104)));
pack();
}//
//GEN-END:initComponents
private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {
String s = jTextField1.getText();
server.sendMessage("127.0.0.1", s);
jTextField1.setText("");
}
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
new Thread(server).start();
}
/**
* @param args the command line arguments
*/
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new ServerForm().setVisible(true);
}
});
}
//GEN-BEGIN:variables
// Variables declaration - do not modify
private javax.swing.JButton jButton1;
private javax.swing.JButton jButton2;
private javax.swing.JTextField jTextField1;
// End of variables declaration//GEN-END:variables
}
说明:
1,运行serverForm,点击启动服务器。
2,运行clientTest,启动一个客户端。
3,在form界面,输入字符串,点击发送,会发送给服务器,在控制台输出。
由于我用的是 MyEclipse 8.0 这个界面是生成的 如果版本低的话 可能要导架包哈 呵呵