最近在学习Socket网络连接,在制作聊天服务器时采用了多线程的方式。下面是代码:
服务器
package socketConnect;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Iterator;
public class SimpleChatServer {
ArrayList<PrintWriter> clientOutputStreams;
public class ClientHandler implements Runnable {
BufferedReader reader;
Socket sock;
public ClientHandler(Socket clientSocket) {
// TODO Auto-generated constructor stub
sock = clientSocket;
try {
InputStreamReader isReader = new InputStreamReader(clientSocket.getInputStream());
reader = new BufferedReader(isReader);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void run() {
// TODO Auto-generated method stub
String message;
System.out.println("服务端线程在跑");
try {
while ((message = reader.readLine()) != null) {
System.out.println("Sread: " + message);
tellEveryone(message);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public void go() {
clientOutputStreams = new ArrayList<>();
try {
ServerSocket serverSock = new ServerSocket(8080);
while (true) {
Socket clientSocket = serverSock.accept();
PrintWriter writer = new PrintWriter(clientSocket.getOutputStream());
clientOutputStreams.add(writer);
Thread t = new Thread(new ClientHandler(clientSocket));
t.start();
System.out.println("got a connection");
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void tellEveryone(String message) {
Iterator<PrintWriter> it = clientOutputStreams.iterator();
while (it.hasNext()) {
PrintWriter writer = (PrintWriter)it.next();
writer.println(message);
writer.flush();
}
}
public static void main(String[] args) {
new SimpleChatServer().go();
}
}
客户端
package socketConnect;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
public class ClientA {
JTextField outgoing;
JTextArea incoming = new JTextArea(15, 50);
PrintWriter pw;
BufferedReader br;
Socket socket;
public void go() {
JFrame frame = new JFrame("Chat Client");
JPanel panel = new JPanel();
incoming.setLineWrap(true);
incoming.setWrapStyleWord(true);
incoming.setEditable(false);
JScrollPane scrollPane = new JScrollPane(incoming);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
outgoing = new JTextField(20);
JButton button = new JButton("Send");
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
String text = getClass().getEnclosingClass().getSimpleName() + ": " + outgoing.getText();
try {
pw.println(text);
pw.flush();
} catch (Exception e2) {
// TODO: handle exception
}
outgoing.setText("");
outgoing.requestFocus();
}
});
panel.add(outgoing);
panel.add(button);
setUpNetWorking();
Thread readerThread = new Thread(new IncomingReader());
readerThread.start();
frame.getContentPane().add(BorderLayout.NORTH, scrollPane);
frame.getContentPane().add(BorderLayout.CENTER, panel);
frame.setSize(400, 500);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);;
}
private void setUpNetWorking() {
try {
socket = new Socket("127.0.0.1", 8080);
pw = new PrintWriter(socket.getOutputStream());
InputStreamReader isReader = new InputStreamReader(socket.getInputStream());
br = new BufferedReader(isReader);
System.out.println("networking established");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void main(String[] args) {
System.out.println("程序运行开始");
new ClientA().go();
}
public class IncomingReader implements Runnable {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("线程开始跑了");
String message;
try {
while ((message = br.readLine()) != null) {
System.out.println("Aread: " + message);
incoming.append(message + "\n");
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("线程跑完了");
}
}
}
以上是代码,运行不报错,可以正常实现聊天。
但是想知道一些细节的原理:
1. 先运行服务端,跑起来后控制台无输出。然后运行客户端,控制台首先会闪现一下以下的输出(是闪现的,特别快,所以我截不了图,手打的):
程序运行开始
networking established
线程开始跑了。
这应该是是客户端运行时的输出,然后是这样的输出:
所以当服务端建立接口之后等待客户端的请求,客户端请求该端口之后,是整个客户端程序运行了一遍,然后服务器的代码才会接着往下运行吗?但是如果整个客户端程序运行了一遍,为什么控制台不输出“线程跑完了”?还是说客户端监听服务器信息的那个线程一直在独立空间中运行着?
请大神帮我解答一下服务器和客户端运行时代码的运行过程以及客户端子线程为什么跑不完?
2. 当我向聊天框中输入信息时:
点击send,会出现以下结果:
所以为什么整个客户端又重新运行了一次????我只是点击了send触发了监听器监听的事件,事件中flush了输出流的内容,为啥整个客户端好像重新run了一次??另外,这个子线程是一直监听服务器的信息吗?服务器没有新的消息它也不结束,就一直等待吗?
请大神解答一下,我对多线程也是刚刚接触,而且有点强迫症,想搞清楚一切不理解的东西。。