java socket BIO (ServerSocket,Socket,多线程)

Java Socket


今天给同事写了一个socket bio的demo让他来测试用,顺便分享给大家,希望可以帮助正在学习和了解java bio的新人们
java.net.socket 是java中最基本的socket bio实现方式。
本例实现了多人互动广播

Server端
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.BindException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 
 * @author Allen 2017年7月19日
 *
 */
public class SocketServer {
	/**
	 * class constant
	 */
	static int POST = 44554;
	static String NOTE_FORMAT_INFO = "[%s:%s]<ID:%s> %s %s \n";
	static ExecutorService threads;
	static PrintWriter pw = null;
	static Map<Integer, Socket> alls = new HashMap<Integer, Socket>();//保存ClientSocket的容器,可以进行统计,定向广播等

	public static void main(String[] args) throws IOException {
                //初始化指定大小的线程池
		threads = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() << 3);
		SocketServer socketServer = new SocketServer();
		try {
			socketServer.openServer();
		} catch (BindException e) {
			System.out.printf("%s [%s] %s", "端口", POST, "被占用");
		}
	}

	public void openServer() throws BindException, IOException {
		ServerSocket server = null;
		int uid = 0;
		try {
			server = new ServerSocket(POST);
			System.out.println("服务器启动成功");
			while (true) {//为什么while(true)因为要不断的保证进入下面的阻塞来接收新的客户端
                               //SocketServer通过阻塞来获取一个new Socket
                               //java如何实现的accept阻塞?通过源码最终指向下面方法
                               //当看到native我们就不需要在去考虑了
                               //native是什么?native是调用本地方法
                               //static native int accept0(int fd, InetSocketAddress[] isaa) throws IOException;
				Socket socket = server.accept();
                               //当用户连接成功时我们给他发送一条欢迎消息
				sendHelp("连接成功 --  From SocketServer", null, socket); 
                               //通过线程池启动线程并把我们uid+1,当然这里的uid没有原子性
                               //原子性怎么做?synchronized/CAS/AtomicInteger皆可
				threads.execute(new ClientSocekt(socket, ++uid));
			}
		} finally {
			if (server != null && !server.isClosed())
				server.close();

		}
	}

	private void sendHelp(String msg, Socket nowSocket, Socket... sockets) throws IOException {
		if (sockets == null || sockets.length == 0)
                        //把alls的值通过toArray(T[] t)转成Socket[]
			sockets = alls.values().toArray(new Socket[0]);
		for (Socket s : sockets) {
                        //把消息广播到sockets[]
			pw = new PrintWriter(s.getOutputStream());
                        //如果不是println就一定要在后面加上"\n"
			pw.println(nowSocket != null && s.hashCode() == nowSocket.hashCode() ? "send success" : msg);
			pw.flush();
		}

	}

	class ClientSocekt extends Thread {
		Socket socket;
		Integer uid;
		BufferedReader br;

		public ClientSocekt(Socket socket, Integer uid) {
			this.socket = socket;
			alls.put(uid, socket);
			this.uid = uid;
		}

		@Override
		public void run() {
			try {
                                //通过bufferedReader从缓冲区读取数据
				br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
				String line = null;
                                //readline也是阻塞的
                                //本demo没有把粘包等问题考虑
                                //如果想解决粘包最简单的就是用read,创建一个Byte
                                //用byte去接,byte设定一个大小
                                //传输的文本改用 {Length|Context|Type|End} 这种方式
                                //当得到length > byte.length的时候继续去缓冲区获取数据,知道此数据获取完毕
				while ((line = br.readLine()) != null) {
					System.out.printf(NOTE_FORMAT_INFO, socket.getInetAddress().toString(), socket.getPort(), uid,
							"收到: ", line);
					sendHelp("来自: "+socket.getInetAddress().toString()+":"+socket.getPort()+"的消息: "+line, socket);
				}
			} catch (SocketException e) {
				System.out.printf(NOTE_FORMAT_INFO, socket.getInetAddress().toString(), socket.getPort(), uid, "状态: ",
						"离开了");
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				alls.remove(uid);
				clear();
			}

		}

		private void clear() {
			if (br != null)
				try {
					br.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			if (socket != null)
				try {
					socket.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
                        //销毁客户端线程
			this.interrupt();
		}
	}
}
Client端
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.SocketException;
import java.util.Scanner;

public class SocketClient { // 搭建客户端
	/**
	 * class constant
	 */
	static int POST = 44554;
	static String IP = "127.0.0.1";

	public static void main(String[] args) throws IOException {
		SocketClient socketClient = new SocketClient();
		try {
			socketClient.openClient();
		} catch (Exception e) {
			e.printStackTrace();
			System.out.println("连接失败");
		}

	}

	@SuppressWarnings("resource")
	public void openClient() throws IOException {
		Socket socket = new Socket(IP, POST);
                //连接成功后我们就建一个阻塞的IO来获取server广播的消息
		new ReadMsg(socket).start();
		System.out.println("客户端启动成功");
		PrintWriter pw = null;
		while (true) {
                        //通过Scanner阻塞的接收键盘输入的信息发送到server
			pw = new PrintWriter(socket.getOutputStream());
			pw.println(new Scanner(System.in).next());
			pw.flush();
		}

	}

	/**
	 * 监听收到的消息
	 * 
	 * @author Allen 2017年7月19日
	 *
	 */
	class ReadMsg extends Thread {
		Socket socket;
		BufferedReader br;

		public ReadMsg(Socket socket) {
			this.socket = socket;
		}

		@Override
		public void run() {
			try {
				br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
				String line = null;
				while (true) {
					while ((line = br.readLine()) != null)
						System.out.printf("%s\n",line);
				}

			} catch (SocketException e) {
				System.out.printf("%s\n","服务器断开了你的连接");
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				clear();
			}
		}
		private void clear() {
			if (br != null)
				try {
					br.close();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			if (socket != null)
				try {
					socket.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			this.interrupt();
		}
	}
}
输出
服务器启动成功
[/172.26.106.38:53305]<ID:1> 收到:  我是Allen 
[/172.26.106.38:53307]<ID:2> 收到:  我是赵钱孙 
[/172.26.106.47:50359]<ID:3> 收到:  RRRR 
客户端启动成功
连接成功 --  From SocketServer
我是Allen
send success
来自: /172.26.106.38:53307的消息: 我是赵钱孙
来自: /172.26.106.47:50359的消息: RRRR
客户端启动成功
连接成功 --  From SocketServer
来自: /172.26.106.38:53305的消息: 我是Allen
我是赵钱孙
send success
来自: /172.26.106.47:50359的消息: RRRR





是不是很简单,没什么复杂的,把线程和容器搞明白了,阻塞IO还有数据传输搞懂了,基本就可以了

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章