网站建设便宜不可信,html5 经典网站,免费网站推广软件哪个好,江门seo计费管理文章目录一、TCP流套接字编程ServerSocketSocketTCP长短连接二、TCP回显服务器客户端服务器客户端并发服务器UDP与TCP一、TCP流套接字编程
我们来一起学习一下TCP socket api的使用#xff0c;这个api与我们之前学习的IO流操作紧密相关#xff0c;如果对IO流还不太熟悉的这个api与我们之前学习的IO流操作紧密相关如果对IO流还不太熟悉的可以看看这篇IO流操作
ServerSocket
顾名思义ServerSocket是创建TCP服务器的Socket对象
构造方法作用ServerSocket(int port)创建一个服务器套接字Socket并指定端口号
方法作用Socket accept()开始监听指定端口有客户端连接时返回一个服务端Socket对象并基于该Socket对象与客户端建立连接否则阻塞等待void close()关闭此套接字
Socket
我们这里的Socket既是客户端的Socket也可能是服务器接收到客户端连接后返回的服务器Socket不论是那个Socket都是双方建立连接后保存对方信息进行收发数据的。
构造方法作用Socket(String host,int port)创建一个客户端Socket对象并与对应IP主机对应端口的进程进行连接
方法作用InetAddress getInetAddress()返回套接字所连接的地址InputStream getInputStream()返回套接字的输入流OutputStream getOutputStream()返回套接字的输出流
TCP长短连接
顾名思义我们的TCP的长短连接就表示我们TCP建立连接后什么时候关闭连接就决定了是长连接还是短链接。 短连接 在每次接收到数据并返回响应后关闭连接。也就是说短连接只能收发一次数据。 长连接 一直保持连接的状态不关闭连接双方可以不停的收发数据。
两者各有优缺短连接适用于客户端请求频率不高的场景浏览网页等。长连接适用于客户端与服务器频繁通信的场景视频通话等。
二、TCP回显服务器客户端
服务器
有了昨天UDP实现的基础今天我们的TCP实现已经有些部分就比较容易理解了。
public class EchoServer {private ServerSocket serverSocket null;public EchoServer(int port) throws IOException {serverSocket new ServerSocket(port);}
}先创建TCP服务器Socket对象并指定端口号。
Socket clientSocket serverSocket.accept();与客户端进行连接如果没有获取到连接则会发生阻塞等待每获取到一个客户端连接就会返回一个Socket对象该Socket对象是专门负责与连接的客户端进行通信的。 我们获取到的每一个Socket对象可能会进行多次通信所以我们获取到连接后将通信封装成一个方法。
private void processCoonection(Socket clientSocket) {System.out.printf([%s:%d] 客户端上线\n,clientSocket.getInetAddress().toString(),clientSocket.getPort());try(InputStream inputStream clientSocket.getInputStream();OutputStream outputStream clientSocket.getOutputStream()) {}catch (IOException e) {e.printStackTrace();}finally {try{clientSocket.close();}catch (IOException e) {e.printStackTrace();}}}我们先获取与输出输入流因为需要释放我们直接将它们放到try()中然后在finally里释放Socket对象资源。
Scanner scanner new Scanner(inputStream);if(!scanner.hasNext()) {//数据已经读完了System.out.printf([%s:%d] 客户端下线\n,clientSocket.getInetAddress().toString(),clientSocket.getPort());break;}String request scanner.next();我们将输入流封装到Scanner里从控制台输入然后判断控制台是否还有数据输入如果没有数据输入就退出然后获取客户端的请求。
/直接返回客户端的请求String response process(request);//将输出流封装成打印流PrintWriter printWriter new PrintWriter(outputStream);printWriter.println(response);printWriter.flush();System.out.printf([%s:%d] req: %s;resp: %s\n,clientSocket.getInetAddress().toString(), clientSocket.getPort(),request, response);public class EchoServer {private ServerSocket serverSocket null;public EchoServer(int port) throws IOException {serverSocket new ServerSocket(port);}public void start() throws IOException {System.out.println(服务器启动!);while (true) {Socket clientSocket serverSocket.accept();processCoonection(clientSocket);}}private void processCoonection(Socket clientSocket) {System.out.printf([%s:%d] 客户端上线\n,clientSocket.getInetAddress().toString(),clientSocket.getPort());try(InputStream inputStream clientSocket.getInputStream();OutputStream outputStream clientSocket.getOutputStream()) {while (true) {Scanner scanner new Scanner(inputStream);if(!scanner.hasNext()) {//数据已经读完了System.out.printf([%s:%d] 客户端下线\n,clientSocket.getInetAddress().toString(),clientSocket.getPort());break;}String request scanner.next();//直接返回客户端的请求String response process(request);//将输出流封装成打印流PrintWriter printWriter new PrintWriter(outputStream);printWriter.println(response);printWriter.flush();System.out.printf([%s:%d] req: %s;resp: %s\n,clientSocket.getInetAddress().toString(), clientSocket.getPort(),request, response);}}catch (IOException e) {e.printStackTrace();}finally {try{clientSocket.close();}catch (IOException e) {e.printStackTrace();}}}public String process(String request) {return request;}
}我们现在实现的这个TCP server有个致命的缺点一次只能处理一个客户端等我们写完客户端再来分析。
客户端
public class EchoClient {private Socket socket null;public EchoClient(String serverIp,int serverPort) throws IOException {//我们TCP的Socket对象能够识别点分十进制的IP//我们在创建对象的时候就会与服务器进行连接socket new Socket(serverIp,serverPort);}
}我们在new 这个对象的过程就会触发TCP建立连接的过程如果我们客户端没有这部分代码那么服务器就会在accept进行阻塞等待。
public void start() {System.out.println(客户端启动!);Scanner scanner new Scanner(System.in);try(InputStream inputStream socket.getInputStream();OutputStream outputStream socket.getOutputStream()) {while (true) {System.out.print( );String request scanner.next();if(request.equals(exit)) {System.out.println(客户端退出!);break;}PrintWriter printWriter new PrintWriter(outputStream);printWriter.flush();Scanner respScanner new Scanner(inputStream);String response respScanner.next();System.out.println(response);}}catch (IOException e) {e.printStackTrace();}}我们客户端的收发数据与服务器大差不差这里就不在一一解释了。 分别启动客户端服务器程序。 可以成功的收发数据但是我们当前代码有一个很严重的问题服务器同一时刻只能处理一个连接这样是很鸡肋的我们对服务器进行更新。
并发服务器
我们看观察一下我们服务器的代码。 当我们客户端连接上这个服务器的时候就执行到processConnection方法的while循环中只要该方法不结束我们的accpet就无法获取到第二个客户端socket对象。 那么我们如何解决这个问题呢? 使用多线程我们的主线程专门负责进行accept每收到一个连接创建新线程由新线程来负责处理这个新的客户端。
public void start() throws IOException {System.out.println(服务器启动!);while (true) {Socket clientSocket serverSocket.accept();Thread t new Thread(() - {processCoonection(clientSocket);});t.start();}}我们可以使用多线程来解决这个问题但是现在每获取到一个连接就会创建一个线程如果同一时刻连接过多我们创建了大量线程资源全部耗费在了线程切换上面了我们可以使用线程池来提升效率。
public void start() throws IOException {System.out.println(服务器启动!);ExecutorService threadPool Executors.newCachedThreadPool();while (true) {Socket clientSocket serverSocket.accept();threadPool.submit(() - {processCoonection(clientSocket);});}}尽管我们使用了线程池了但还是不够如果我们的客户端非常多而且都迟迟不断开就会导致我们会有很多线程对于我们来说是一个很大的负担。 能否有办法解决单机支持更大量客户端的问题呢也是经典的C10M(单机处理1KW个客户端)问题 这里并不是说单机真正能处理1KW个客户端只是表达说客户端的量非常大针对我们上述多线程的版本我们的机器是承受不了这么多线程的开销的那么是否有办法一个线程处理很多客户端连接呢? 这就是IO多路复用IO多路转接技术 给线程安排一个集合这个集合放了一堆连接我们线程负责监听集合那个连接有数据来了就处理那个连接。虽然我们的连接有很多但是我们这里的连接并不是严格意义上的同时也是有先后的我们的操作系统里提供了一些API比如selectpollepoll我们的java里也提供了一组NIO这样的类封装了上述技术。
UDP与TCP
我们学习了TCP与UDP的网络编程后来进行一个对比。 TCP UDP