好吧,这例子其实没那么傻瓜(主要是我解释得没那么详细)。有不懂的不说明你笨哦~欢迎留言交流。
实现的功能:
1,一个客户端,一个服务端。
2,客户端和服务端实现一来一回的收发信息。
后续还会加入GUI和多线程,教程戳这里:java socket 聊天小程序(socket入门小例子进阶版)
客户端:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
public class TalkToAlicebot {
BufferedReader bufferedReader;
ObjectOutputStream out;
ObjectInputStream in;
public TalkToAlicebot() throws IOException {
Socket client;
try {
client = new Socket("127.0.0.1", 7777);
// construct stream 'out' BEFORE 'in'
out = new ObjectOutputStream(client.getOutputStream());
out.flush();
in = new ObjectInputStream(client.getInputStream());
try {
do {
// DO NOT construct stream here, would cause StreamCorruptedException.
// out = new ObjectOutputStream(client.getOutputStream());
// out.flush();
// in = new ObjectInputStream(client.getInputStream());
System.out.println("Waiting for input...");
bufferedReader = new BufferedReader(new InputStreamReader(System.in));
out.writeObject(bufferedReader.readLine());
out.flush();
String messageFromServer;
System.out.println("Waiting for response...");
messageFromServer = (String) in.readObject();
System.out.println("Server>" + messageFromServer);
} while (true);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (bufferedReader != null) {
bufferedReader.close();
}
if (out != null) {
out.close();
}
if (in != null) {
in.close();
}
}
}
public static void main(String[] args) {
try {
new TalkToAlicebot();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
服务器端:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Alicebot {
ServerSocket ss;
Socket socket;
ObjectOutputStream out;
String messageReceived;
ObjectInputStream in;
BufferedReader send;
public Alicebot() throws IOException {
ss = new ServerSocket(7777);
System.out.println("Waiting for connection...");
socket = ss.accept();// blocks until a connection is made
System.out.println("Connection Established");
// send
out = new ObjectOutputStream(socket.getOutputStream());
out.flush();
// receive
System.out.println("Waiting for in stream...");
in = new ObjectInputStream(socket.getInputStream());
System.out.println("In stream received! Sending out stream...");
try {
while (true) {
messageReceived = (String) in.readObject();
System.out.println("From client: " + messageReceived);
send = new BufferedReader(new InputStreamReader(System.in));
System.out.println("Waiting for input response...");
sendMessage(send.readLine());
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
closeConnection();
}
}
private void closeConnection() {
try {
in.close();
out.close();
socket.close();
send.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private void sendMessage(String messageReceived2) {
try {
out.writeObject(messageReceived2);
out.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
try {
new Alicebot();
} catch (IOException e) {
e.printStackTrace();
}
}
}
开始写这个小例子的时候,借鉴了网上的类似的东西,即使是这么短的代码,自己搞的时候还是遇到不少问题。确实对socket和输入输出不了解。相关问题总结如下:
1- Connection reset
java.net.SocketException: Connection reset
at java.net.SocketInputStream.read(SocketInputStream.java:168)
at java.net.SocketInputStream.read(SocketInputStream.java:182)
at java.io.ObjectInputStream$PeekInputStream.peek(ObjectInputStream.java:2248)
at java.io.ObjectInputStream$BlockDataInputStream.peek(ObjectInputStream.java:2541)
at java.io.ObjectInputStream$BlockDataInputStream.peekByte(ObjectInputStream.java:2551)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1296)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:350)
原因:
1,如果一端的Socket被关闭(或主动关闭,或因为异常退出而 引起的关闭),另一端仍发送数据,发送的第一个数据包引发该异常(Connect reset by peer)。2,一端退出,但退出时并未关闭该连接,另一端如果在从连接中读数据则抛出该异常(Connection reset)。简单的说就是在连接断开后的读和写操作引起的。
2-StreamCorruptedException
我当时出现这个问题,现象是客户端第一次向服务器端发送数据没有问题,第二次就会出现这个异常。
在网上查了一下,是因为在while循环中,每次服务端都new ObjectInputStream(),而客户端没有。而new ObjectInputStream对象时,会去readStreamHeader();将客户端发送过来的数据当做的消息头了,比较出消息头不对所以报错。
public ObjectOutputStream(OutputStream out) throws IOException {
verifySubclass();
bout = new BlockDataOutputStream(out);
handles = new HandleTable(10, (float) 3.00);
subs = new ReplaceTable(10, (float) 3.00);
enableOverride = false;
writeStreamHeader();
bout.setBlockDataMode(true);
if (extendedDebugInfo) {
debugInfoStack = new DebugTraceInfoStack();
} else {
debugInfoStack = null;
}
}
另外,还有从stackoverflow上查到一些socket stream相关的注意事项,挺有用的(对于初学者来说哈):
1-You are using multiple streams over the same socket. Don't do that, they will only confuse each other. As you need object I/O, just use an ObjectInputStream and ObjectOutputStream for everything.--每个socket只有一个ObjectInputStream和一个ObjectOutputStream。
2-Incorrect construction order. You must construct the ObjectOutputStream before the ObjectInputStream, at both ends.--先初始化ObjectOutputStream,再初始化ObjectInputStream。这个问题我也遇到过,所以写到代码的注释中了。不过我试验在服务端不用按照顺序,先初始化哪个都可以。客户端必须按照顺序。
3-You are using streams with different lifetimes. You haven't hit the problem yet but eventually this will lead to a StreamCorruptedException. Use the same ObjectInputStream and ObjectOutputStream for the life of the Socket, at both ends.--socket的生命周期和输入输出流的生命周期应该相同。不要在同一个socket中使用多个输入输出流。实际上又在强调第一点。
今天调这个小例子的感想就是:
1-Slow down。Don't panic。虽然是个简单的小例子。但是之前没用过socket,还有一些输入输出流的类,所以慢点儿是正常的。
2-不要忽略任何一个错误或者异常。
然后我还写了这个例子的进阶版,增加了gui和多线程的应用,具体戳这里:http://blog.csdn.net/alalay168/article/details/9046793