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 sockets = new ArrayList();
@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: