Java Socket编程时对于TCP/IP 协议层的通信进行封装,简化了相关的一些操作。//待续
服务器端代码:
package com.lou.socket; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; public class TestServer { public static void main(String[] args) throws IOException { //1.创建一个Server Socket ServerSocket server = new ServerSocket(); // 2.绑定监听指定的端口 InetSocketAddress address = new InetSocketAddress("localhost",18824); server.bind(address); // 3.接受此端口的通信请求 Socket socket = server.accept(); // 在没有客户端对其进行相应前,下面的代码不会执行,将一直阻塞 //服务器端的输出流和输入流获取 BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); PrintWriter writer = new PrintWriter(socket.getOutputStream(),true); //来自键盘的输入数据 BufferedReader keyword=new BufferedReader(new InputStreamReader(System.in)); while(true) { if(reader.ready()) { // 捕捉来自客户端发来的消息 客户端没有发消息过来时,reader.ready() 为false, 循环检测是否有数据,有测打印出来 String info = reader.readLine(); System.out.println("Client:"+info); } if(keyword.ready()) { //捕获来自服务器端另外一个输入流 : 键盘的数据。如果有数据,则发送给客户端 String test = keyword.readLine(); writer.println(test); System.out.println("Server:"+test); } } } }
客户端代码:
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.InetSocketAddress; import java.net.Socket; import java.net.UnknownHostException; public class ClientSocket { public static void main(String[] args) throws UnknownHostException, IOException { //1.创建一个Server Socket Socket socket = new Socket(); // 2.连接到指定的 server socket,指定IP 和端口号 InetSocketAddress address = new InetSocketAddress("localhost",18824); socket.connect(address); // 3.连接成功后,获取相应的输入输出流,进行数据交互 BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())); PrintWriter pw = new PrintWriter(socket.getOutputStream(),true); BufferedReader keyword=new BufferedReader(new InputStreamReader(System.in)); while(true) { // 捕捉来自服务器端发来的消息 服务器端没有发消息过来时,br.ready() 为false, 循环检测是否有数据,有测打印出来 if(br.ready()) { String info = br.readLine(); System.out.println("Server:"+info); } //捕获来自服务器端另外一个输入流 : 键盘的数据。如果有数据,则发送给服务器端 if(keyword.ready()) { String test = keyword.readLine(); pw.println(test); System.out.println("Client:"+test); } } } }
运行结果:
上面的这个例子只能支持一个服务器端和一个客户端的通信,因为 客户端内 server.accept() 方法只执行了一次,只能返回一个Socket 连接。可以在服务端接受多个Socket,这时候的Socket应当放在一个线程里,让它有生命周期,来使用客户端和服务端的自由通信。
package com.lou.socket; import java.io.IOException; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; public class TestServer { public static void main(String[] args) throws IOException { //1.创建一个Server Socket ServerSocket server = new ServerSocket(); // 2.绑定监听指定的端口 InetSocketAddress address = new InetSocketAddress("localhost",18824); server.bind(address); // 3.接受此端口的通信请求 while(true) { //循环调用accept方法,返回相应的Socket Socket socket = server.accept(); //使用线程,将每一个Socket都封装到线程内,这个每个接受的Socket可以自由的跟服务器通信了 new Thread(new SocketHandler(socket)).start(); } } }
package com.lou.socket; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; public class SocketHandler implements Runnable { private Socket socket; public SocketHandler(Socket socket) { this.socket =socket; } @Override public void run() { try { BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); PrintWriter writer = new PrintWriter(socket.getOutputStream(),true); //来自键盘的输入数据 BufferedReader keyword=new BufferedReader(new InputStreamReader(System.in)); boolean flag = true; while(flag) { if(reader.ready()) { // 捕捉来自客户端发来的消息 客户端没有发消息过来时,reader.ready() 为false, 循环检测是否有数据,有测打印出来 String info = reader.readLine(); System.out.println("Client "+socket.getPort() +":"+info); //如果对方输入BYE,关闭回话 if("BYE" == info) { socket.close(); flag = false; } } if(keyword.ready()) { //捕获来自服务器端另外一个输入流 : 键盘的数据。如果有数据,则发送给客户端 String test = keyword.readLine(); writer.println(test); System.out.println("Server:"+test); } } } catch (IOException e) { e.printStackTrace(); } } }
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.InetSocketAddress; import java.net.Socket; import java.net.UnknownHostException; public class ClientSocket { public static void main(String[] args) throws UnknownHostException, IOException { //1.创建一个Server Socket Socket socket = new Socket(); // 2.连接到指定的 server socket,指定IP 和端口号 InetSocketAddress address = new InetSocketAddress("localhost",18824); socket.connect(address); // 3.连接成功后,获取相应的输入输出流,进行数据交互 BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())); PrintWriter pw = new PrintWriter(socket.getOutputStream(),true); BufferedReader keyword=new BufferedReader(new InputStreamReader(System.in)); while(true) { // 捕捉来自服务器端发来的消息 服务器端没有发消息过来时,br.ready() 为false, 循环检测是否有数据,有测打印出来 if(br.ready()) { String info = br.readLine(); System.out.println("Server:"+info); } //捕获来自服务器端另外一个输入流 : 键盘的数据。如果有数据,则发送给服务器端 if(keyword.ready()) { String test = keyword.readLine(); if("BYE" == test) { br.close(); pw.close(); socket.close(); } pw.println(test); System.out.println("Client:"+test); } } } }
实现的主要思路:
a.在服务器端设置一个主线程,监听特定的一个接口,为每一个socket请求创建一个对话框和相应的处理。
主线程MainThread代码:
package com.lou.socket; import java.io.IOException; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; import java.util.List; /* * server的主线程,监听socket端口,为每一个Socket 创建一个对话框 */ public class MainThread extends Thread{ public List<Socket> sockets = new ArrayList<Socket>(); @Override public void run() { //1.创建一个Server Socket ServerSocket server =null; try { server = new ServerSocket(); InetSocketAddress address = new InetSocketAddress("localhost",18824); server.bind(address); while(true) { //循环调用accept方法,返回相应的Socket Socket socket = server.accept(); sockets.add(socket); // 为每一个请求的Socket提供 界面 "会话" ServerGUI serverGUI = new ServerGUI(socket); //创建监听socket 数据流输入信息,有数据输入,则更新到GUI new Thread(new SocketInfoUpdater(serverGUI)).start(); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public static void main(String[] args) { new MainThread().start(); } }
b.定义一个监听线程,用来处理 socket发送过来的数据信息,及时更新到GUI上,和 server GUI 上的发送按钮的事件相应,来发送数据。
事件和输入流监听线程SocketInfoUpdater.java:
package com.lou.socket; 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 java.text.SimpleDateFormat; import java.util.Date; /* * 此线程监听serverGUI所拥有的socket是否有数据输入,如果有更新,则更新到GUI上 */ public class SocketInfoUpdater implements Runnable,ActionListener{ ServerGUI server; SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); public SocketInfoUpdater(ServerGUI server) { this.server = server; } @Override public void run() { Socket socket = server.socket; while(true) { try { BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); if(reader.ready()) { String info = reader.readLine(); server.outputArea.append("Client-" +socket.getPort()+" at " + f.format(new Date()) + "\n"); server.outputArea.append(" "+info +"\n"); } } catch (IOException e) { e.printStackTrace(); } } } //设置发送按钮监听,事件相应 @Override public void actionPerformed(ActionEvent e) { String temp = server.inputArea.getText(); SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); server.outputArea.append("Client "+server.socket.getLocalPort() +" at " + f.format(new Date()) + "\n"); server.outputArea.append(" " + temp + "\n"); this.sendInfo(temp); server.inputArea.setText(""); } private void sendInfo(String s) { try { PrintWriter pw = new PrintWriter(server.socket.getOutputStream(), true); pw.println(s); } catch (IOException e) { e.printStackTrace(); } } }
c. 服务器端界面的设计:
服务器端界面设计 ServerGUI.java:
package com.lou.socket; import java.awt.BorderLayout; import java.awt.Container; import java.awt.GridLayout; import java.io.IOException; import java.net.Socket; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; public class ServerGUI extends JFrame { public final Socket socket; //交互对话框中接收数据显示区 final JTextArea outputArea = new JTextArea(70, 70); //输入区域 final JTextArea inputArea = new JTextArea(70, 70); final JScrollPane outputScroll = new JScrollPane(outputArea); final JScrollPane inputScroll = new JScrollPane(inputArea); public ServerGUI(Socket socket1) throws IOException { //传入特定的socket this.socket = socket1; this.setTitle("Server"); Container container = getContentPane(); JPanel pane = new JPanel(); container.setLayout(new BorderLayout()); pane.setLayout(new GridLayout(2, 1, 5, 5)); inputScroll.setAutoscrolls(true); outputScroll.setAutoscrolls(true); pane.add(outputScroll); pane.add(inputScroll); setSize(300, 300); container.add(pane, BorderLayout.CENTER); JButton send = new JButton("Send"); //为发送按钮设置发送事件 SocketInfoUpdater updater = new SocketInfoUpdater(this); send.addActionListener(updater); container.add(send, BorderLayout.SOUTH); setDefaultCloseOperation(3); setVisible(true); } }
客户端的设计:
客户端的实现比较简单,创建一个界面,然后配一个监听输入流和处理事件的监听线程就可以了。
a.客户端GUI,ClientGUI代码:
import java.awt.BorderLayout; import java.awt.Container; import java.awt.GridLayout; import java.io.IOException; import java.net.InetSocketAddress; import java.net.Socket; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; public class ClientGUI extends JFrame { Socket socket = null; final JTextArea outputArea = new JTextArea(70, 70); final JTextArea inputArea = new JTextArea(70, 70); final JScrollPane outputScroll = new JScrollPane(outputArea); final JScrollPane inputScroll = new JScrollPane(inputArea); public ClientGUI() throws IOException { this.setTitle("Client"); this.initClientSocket(); SocketInfoUpdater updater = new SocketInfoUpdater(this); new Thread(updater).start(); Container container = getContentPane(); JPanel pane = new JPanel(); container.setLayout(new BorderLayout()); pane.setLayout(new GridLayout(2, 1, 5, 5)); inputScroll.setAutoscrolls(true); outputScroll.setAutoscrolls(true); pane.add(outputScroll); pane.add(inputScroll); setSize(300, 300); container.add(pane, BorderLayout.CENTER); JButton send = new JButton("Send"); send.addActionListener(updater); container.add(send, BorderLayout.SOUTH); setDefaultCloseOperation(3); setVisible(true); } /* * 初始化socket */ private void initClientSocket() { // 1.创建一个Server Socket socket = new Socket(); // 2.连接到指定的 server socket,指定IP 和端口号 InetSocketAddress address = new InetSocketAddress("localhost", 18824); try { socket.connect(address); } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) throws IOException { new ClientGUI(); } }
b. 输入流监听和发送数据的监听线程SocketInfoUpdater.java (这个类其实和服务器端上的基本上一样,之所以把它贴出来是考虑到在后续的开发设计中,Server 端和Client端的机制有所不同):
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 java.text.SimpleDateFormat; import java.util.Date; public class SocketInfoUpdater implements Runnable ,ActionListener{ ClientGUI client; public SocketInfoUpdater(ClientGUI client) { this.client = client; } @Override public void run() { Socket socket = client.socket; // 循环检测输入流有没有数据传入,有则显示出来 while(true) { try { BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); if(reader.ready()) { String info = reader.readLine(); SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); client.outputArea.append("Server "+socket.getPort() +" at " + f.format(new Date()) + "\n"); client.outputArea.append(" "+info +"\n"); } } catch (IOException e) { e.printStackTrace(); } } } //设置发送按钮监听,事件相应 @Override public void actionPerformed(ActionEvent e) { String temp = client.inputArea.getText(); SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); client.outputArea.append("Client "+client.socket.getLocalPort() +" at " + f.format(new Date()) + "\n"); client.outputArea.append(" " + temp + "\n"); this.sendInfo(temp); client.inputArea.setText(""); } private void sendInfo(String s) { try { PrintWriter pw = new PrintWriter(client.socket.getOutputStream(), true); pw.println(s); } catch (IOException e) { e.printStackTrace(); } } }
执行结果:
截图1:
截图2: