这两天写了一个小应用,实现基本的聊天功能。该应用既是客户端也是服务器,不要求某一方先发起连接请求。但同一时间仅能两个ip互相传输数据。用上了最近学的多线程和网络编程相关知识。先贴上初稿,还有问题,以后有空再琢磨琢磨。
两个类:LittleTom.class, TalkThread.class
1. LittleTom.java
import java.io.*;
import java.net.*;
public class LittleTom {
private Wait waitThread = null;
private Connect connectThread = null;
private ServerSocket waitSocket;
private Socket socket;
private BufferedReader br = null;
private int waitPort = 10001;
private int connectPort = 10002;
private boolean isConnecting = false;
public LittleTom() {
br = new BufferedReader(new InputStreamReader(System.in));
waitThread = new Wait();
connectThread = new Connect();
waitThread.start();
connectThread.start();
}
class Wait extends Thread {
public void run(){
waitConnect();
}
synchronized private void waitConnect() {
try {
waitSocket = new ServerSocket(waitPort);
while(true) {
if(!isConnecting) {
Socket memberSocket = waitSocket.accept();
if(!isConnecting) {
isConnecting = true;
System.out.println(memberSocket.getInetAddress().getHostName() + " want to talk with you.");
new TalkThread(LittleTom.this, memberSocket);
} else {
System.out.println("Wait::You can only speak with one preson at the same time.");
continue;
}
} else {
wait();
}
}
} catch(Exception e) {
e.printStackTrace();
} finally {
try {
if(waitSocket != null){
waitSocket.close();
}
if(socket != null) {
socket.close();
}
br.close();
} catch(Exception e) {
e.printStackTrace();
}
}
}
}
class Connect extends Thread {
public void run(){
tryToConnect();
}
synchronized private void tryToConnect() {
try{
while(true) {
if(!isConnecting) {
System.out.println("Please input the IP that you want to talk with: ");
br = new BufferedReader(new InputStreamReader(System.in));
String input = br.readLine();
// String IP = "109.123.102.117";
if(!isConnecting) {
isConnecting = true;
System.out.println("Try to connecting...");
socket = new Socket(input, connectPort);
new TalkThread(LittleTom.this, socket);
} else {
System.out.println("Connect::You can only speak with one preson at the same time.");
continue;
}
} else {
wait();
}
}
} catch(Exception e) {
System.out.println("connect fail.");
e.printStackTrace();
} finally {
try {
if(waitSocket != null){
waitSocket.close();
}
if(socket != null) {
socket.close();
}
br.close();
} catch(Exception e) {
e.printStackTrace();
}
}
}
}
public void resetConnection() {
isConnecting = false;
synchronized(waitThread) {
waitThread.notify();
}
synchronized(connectThread) {
connectThread.notify();
}
}
public BufferedReader getSystemBuffer() {
return br;
}
public static void main(String[] args) {
LittleTom lt = new LittleTom();
}
}
2. TalkThread.java
import java.io.*;
import java.net.*;
public class TalkThread {
private LittleTom littleTom;
private Socket memberSocket;
private String peer;
private InputStream is;
private OutputStream os;
private Thread receiveThrd = null;
private Thread sendThrd = null;
public TalkThread(LittleTom lt, Socket socket){
try {
this.littleTom = lt;
this.memberSocket = socket;
peer = memberSocket.getInetAddress().getHostName();
is = memberSocket.getInputStream();
os = memberSocket.getOutputStream();
receiveThrd = new Thread() {
public void run(){
receive();
sendThrd.stop();
close();
littleTom.resetConnection();
}
};
sendThrd = new Thread() {
public void run(){
send();
receiveThrd.stop();
close();
littleTom.resetConnection();
}
};
receiveThrd.start();
sendThrd.start();
} catch(Exception e) {
e.printStackTrace();
}
}
private void receive() {
byte[] b = new byte[1024];
String receiveMsg = null;
try {
do {
int n = is.read(b);
if(n<=0) {
continue;
}
receiveMsg = new String(b, 0, n);
System.out.println(peer + " say >>>> " + receiveMsg);
} while(receiveMsg == null || !receiveMsg.equalsIgnoreCase("exit"));
} catch(Exception e) {
if(memberSocket.isClosed()){
System.out.println("receive::socket is closed.");
} else {
e.printStackTrace();
}
}
}
private void send() {
// byte[] b = new byte[1024];
String input = null;
try {
do {
input = littleTom.getSystemBuffer().readLine();
if(input == null) {
continue;
}
System.out.println("I say >>>> " + input);
os.write(input.getBytes());
} while((input == null) || (!input.equalsIgnoreCase("exit")));
} catch(Exception e) {
if(memberSocket.isClosed()){
System.out.println("send::socket is closed.");
} else {
e.printStackTrace();
}
}
}
public void close() {
try {
is.close();
os.close();
memberSocket.close();
} catch(Exception e) {
e.printStackTrace();
}
}
}
程序运行起来还有问题:
1. 被动接受连接的一方,第一次用户输入的数据不能发送出去。原因是数据被tryToConnect读到,并被认为是用户输入的IP地址,所以尝试去连接。连接失败,打印"Connect::You can only speak with one preson at the same time.", 再次进入循环的时候,由于isConnecting==true,直接进入等待。然后后面用户的输入就能发送出去了。但是怎么修复这个bug啊?
2. 连接只能进行一次。一方exit,然后再尝试去连就连不上了。但是程序并没有退出。求高手解答。