Java Socket编程基础

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://louluan.blog.csdn.net/article/details/19974999

1.简述:

Java Socket编程时对于TCP/IP 协议层的通信进行封装,简化了相关的一些操作。//待续

2.Socket 通信时序图
  3.Socket 数据流的交互
4.单客户端和服务器通信的简单例子:

服务器端代码:

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);
			}
			
		}
		

	}

}

 运行结果:

5.Socket 多客户端单服务器之间的通信:

       上面的这个例子只能支持一个服务器端和一个客户端的通信,因为 客户端内 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);
			}
			
		}
		

	}

}
6. 简易聊天工具的实现:

    实现的主要思路:

   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:

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券