本文分享自华为云社区《Java Socket 如何实现服务器和客户端数据交互》,作者:jackwangcumt 。GoodMai
1 Socket概述
依据百度百科的定义,Socket 译为套接字,它是对网络中不同主机上的利用过程之间进行双向通信的端点的形象。一个Socket实例就是网络上过程通信的一端,提供了应用层过程利用网络协议替换数据的机制。Socket向上连贯各种利用过程,向下连贯各种网络协议,是应用程序通过网络协议进行通信的接口。其示意图如下下图所示:
(来自《Java TCP/IP Socket编程》)
从上图可知,套接字Socket在OSI七层模型中处于应用层和传输层之间,是一个重要的接口。一般来说,传输层协定有TCP协定和UDP协定。这两个协定底层都基于IP网络层协定。
2 Java Socket 实现
在Java SDK中,对于Socket原生提供了反对,它分为ServerSocket和Socket,其中ServerSocket发动一个服务端的Socket,其中须要提供一个端口号,如果给定0,则主动申请可用的端口。当然了,也能够指定具体的端口号(有肯定的范畴,不超过 65535 )。不过这里须要留神,传入的端口不能被其余利用占用,否则启动服务失败。上面给出一个简略的ServerSocket示例,代码如下:
package com.example.demo.network;import java.io.IOException;import java.io.PrintWriter;import java.net.ServerSocket;import java.net.Socket;import java.util.Scanner;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class MyServer { //<= 65535 private static final int PORT = 65535; public static void main(String[] args) throws Exception { try (ServerSocket listener = new ServerSocket(PORT)) { System.out.println("Server Started"); //线程池大小,它依据客户端数来决定,当大于10时,则之前的10个线程依然能够工作, // 而超过的线程则进入队列中,期待。 //当之前的客户端开释量后,则在队列中的线程依然能够工作。 ExecutorService pool = Executors.newFixedThreadPool(10); while (true) { //多线程 pool.execute(new MyServerDemo01(listener.accept())); System.out.println(pool); } } } //Runnable接口的实现对象能够被线程Thread调用 private static class MyServerDemo01 implements Runnable { private Socket socket; MyServerDemo01(Socket socket) { this.socket = socket; } @Override public void run() { System.out.println("Client [" + socket.getRemoteSocketAddress().toString()+" ] Connected"); try { //输出 Scanner in = new Scanner(socket.getInputStream()); //输入 PrintWriter out = new PrintWriter(socket.getOutputStream(), true); while (in.hasNextLine()) { String msg = in.nextLine(); System.out.println("Client [" + socket.getRemoteSocketAddress().toString()+" ] : " + msg); out.println(msg.toUpperCase()); } } catch (Exception e) { System.out.println("Error:" + socket+ e.getMessage()); } finally { try { //敞开socket socket.close(); } catch (IOException e) { e.printStackTrace(); } System.out.println("Closed: " + socket); } } }}
启动该Server端,并开始监听客户端的连贯。当客户端没有连贯时,服务器线程池pool并未启动独自的线程。上面给出客户端的Java Socket实现,具体的示例代码如下:
package com.example.demo.network;import java.io.PrintWriter;import java.net.Socket;import java.util.Scanner;public class MyClient { //<= 65535 private static final int PORT = 65535; //服务器地址 private static final String IP = "127.0.0.1"; public static void main(String[] args) throws Exception { try (Socket socket = new Socket(IP, PORT)) { System.out.println("Client ["+socket.getRemoteSocketAddress().toString()+" ] Started"); Scanner scanner = new Scanner(System.in); Scanner in = new Scanner(socket.getInputStream()); PrintWriter out = new PrintWriter(socket.getOutputStream(), true); while (scanner.hasNextLine()) { out.println(scanner.nextLine()); System.out.println("Server Response:"+ in.nextLine()); } }catch (Exception ex){ System.out.println("Error : "+ ex.getMessage()); } }}
从代码可知,try (Socket socket = new Socket(IP, PORT)) 是一种蕴含资源开释的try-with-resource机制,它会主动进行资源开释,而不须要手动进行开释。Socket对象要想和服务器通信,必须要明确服务器的IP地址和端口,否则不能正确通信,Socket启动时,也会在主机上占用本人的端口。咱们首先启动Server端,而后能够同时启动10个以上的Client端,比方13个,那么超过Executors.newFixedThreadPool(10)限定的数量10后,将进入queued tasks队列中进行排队期待。通过打印pool对象,能够看出以后的状态,比方[Running, pool size = 4, active threads = 4, queued tasks = 0, completed tasks = 0]阐明以后在运行状态,线程池大小为10,激活的线程为10,期待的工作线程queued tasks为0。
上面给出Server端相干输入示例:
Server Startedjava.util.concurrent.ThreadPoolExecutor@256216b3[Running, pool size = 1, active threads = 1, queued tasks = 0, completed tasks = 0]Client [/127.0.0.1:64590 ] Connectedjava.util.concurrent.ThreadPoolExecutor@256216b3[Running, pool size = 2, active threads = 2, queued tasks = 0, completed tasks = 0]Client [/127.0.0.1:64597 ] Connectedjava.util.concurrent.ThreadPoolExecutor@256216b3[Running, pool size = 3, active threads = 3, queued tasks = 0, completed tasks = 0]Client [/127.0.0.1:64603 ] ConnectedClient [/127.0.0.1:64590 ] : helloClient [/127.0.0.1:64590 ] : helloClient [/127.0.0.1:64597 ] : worldClient [/127.0.0.1:64597 ] : worldClient [/127.0.0.1:64603 ] : pythonClient [/127.0.0.1:64597 ] : python02java.util.concurrent.ThreadPoolExecutor@256216b3[Running, pool size = 4, active threads = 4, queued tasks = 0, completed tasks = 0]Client [/127.0.0.1:57806 ] Connectedjava.util.concurrent.ThreadPoolExecutor@256216b3[Running, pool size = 5, active threads = 5, queued tasks = 0, completed tasks = 0]Client [/127.0.0.1:57814 ] Connectedjava.util.concurrent.ThreadPoolExecutor@256216b3[Running, pool size = 6, active threads = 6, queued tasks = 0, completed tasks = 0]Client [/127.0.0.1:57820 ] Connectedjava.util.concurrent.ThreadPoolExecutor@256216b3[Running, pool size = 7, active threads = 7, queued tasks = 0, completed tasks = 0]Client [/127.0.0.1:57827 ] Connectedjava.util.concurrent.ThreadPoolExecutor@256216b3[Running, pool size = 8, active threads = 8, queued tasks = 0, completed tasks = 0]Client [/127.0.0.1:57833 ] Connectedjava.util.concurrent.ThreadPoolExecutor@256216b3[Running, pool size = 9, active threads = 9, queued tasks = 0, completed tasks = 0]Client [/127.0.0.1:57839 ] Connectedjava.util.concurrent.ThreadPoolExecutor@256216b3[Running, pool size = 10, active threads = 10, queued tasks = 0, completed tasks = 0]Client [/127.0.0.1:57845 ] Connectedjava.util.concurrent.ThreadPoolExecutor@256216b3[Running, pool size = 10, active threads = 10, queued tasks = 1, completed tasks = 0]java.util.concurrent.ThreadPoolExecutor@256216b3[Running, pool size = 10, active threads = 10, queued tasks = 2, completed tasks = 0]java.util.concurrent.ThreadPoolExecutor@256216b3[Running, pool size = 10, active threads = 10, queued tasks = 3, completed tasks = 0]Closed: Socket[addr=/127.0.0.1,port=64590,localport=65535]Client [/127.0.0.1:57854 ] ConnectedClient [/127.0.0.1:57814 ] : t2Client [/127.0.0.1:57814 ] : tw2
客户端相干输入输出界面如下:
Client [/127.0.0.1:65535 ] StartedworldServer Response:WORLDworldServer Response:WORLDpython02Server Response:PYTHON02
好买网 IT技术交易网站