java之网络编程

张开发
2026/4/21 17:14:19 15 分钟阅读
java之网络编程
由于我们之前已经写多了socket对udq的实现所以我们这节主要将重心放在Tcp之上socket对tcpServerSocketServerSocket 是创建TCP服务端Socket的API。ServerSocket 构造方法ServerSocket 方法因为tcp是有连接的所以在一个客户端给服务器传输信息前我们需要先建立连接这样才能传输信息这时候就需要用到servesocket了。当创建好servesocket后如果有客户端想和服务器建立连接,发送了连接请求这个时候服务器的应用程序是不需要做出任何操作由服务器的系统内核直接就完成了连接建立的流程这样客户端和服务器之间建立了逻辑上的连接这个连接在内核中以连接对象的形式存在。我们就将该连接对象放入已完成连接队列中accept队列。创建好的连接对象就会在该内核的队列中(这个队列是每个 serverSocket 都有一个这样的队列) 排队.而该服务器要想和已经连接好的客户端进行通讯的话,还需要通过 accept函数从accept队列中取出连接对象通过该对象就创建socket用于通信。当我们accept时 如果连接对象在队列中不存在则会堵塞有的话则会接受且创建一个socket对象用于通讯。我们可以认为上述的模型是一个生产者消费者模型。socketSocket 可以是客户端Socket也可以是服务器中通过连接对象建立的 Socket。 不管是客户端还是服务端的Socket都是双方建立连接以后用来双方进行通讯的工具这个不仅是客户端的socket的构造方法还是客户端的连接建立请求以下是它的普通方法这里的输入流和输出流就是我们发送消息的工具它们是普通的字节流我们之前文件io里学到的所有方法在这里也全能用通过该输入流读取数据就能读取到另一个主机发送的数据同理输出流就往里面写数据就能发送到另一个主机的输入流里。实现回显服务器和客户端服务器端的逻辑如下1. 服务器启动1. 创建一个 ServerSocket 对象绑定到指定的端口10002并监听客户端连接。2. 打印 服务器启动 表示服务器已启动并正在等待客户端连接。2. 接受客户端连接1. 调用 serverSocket.accept() 方法等待并接受客户端的连接请求。2. 创建一个新的 Socket 对象表示与客户端的连接。3. 处理客户端任务1. 调用 startTask 方法处理客户端的请求。2. 在 startTask 方法中获取客户端的输入流和输出流。4. 读取客户端请求并发送响应1. 使用 Scanner 从输入流读取客户端发送的数据。2. 使用 PrintWriter 向输出流发送响应数据。3. 在循环中不断读取客户端发送的数据直到 scanner.hasNext() 返回 false相当于客户端断开连接之后会特殊说明这里的细节。4. 对于每次读取的数据调用 process 方法进行处理在这个例子中process 方法只是简单地返回传入的字符串。5. 将处理后的字符串发送回客户端。5. 关闭连接1. 在 finally 块中关闭客户端套接字确保资源被正确释放。package network;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;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 TcpServe {public static void main(String[] args) throws IOException {System.out.println(服务器启动);ServerSocket serverSocket new ServerSocket(10002);Socket socket serverSocket.accept();startTask(socket);}public static void startTask(Socket socket) throws IOException {try (InputStream inputStream socket.getInputStream();OutputStream outputStream socket.getOutputStream()) {Scanner scanner new Scanner(inputStream);PrintWriter printWriter new PrintWriter(outputStream);while (true) {if (!scanner.hasNext())break;String string scanner.next();String string1 process(string);printWriter.println(string1);printWriter.flush();}} catch (Exception e) {e.printStackTrace();} finally {socket.close();}}public static String process(String string) {return string;}}客户端的逻辑如下客户端启动1.创建一个 Socket 对象连接到指定的服务器地址192.168.50.173和端口10002。打印 客户端上线 表示客户端已启动并正在连接服务器。2.获取输入输出流获取 Socket 对象的输入流和输出流用于接收服务器的响应和发送客户端的请求。读取用户输入并发送请求使用 Scanner 从控制台读取用户输入的字符串。使用 PrintWriter 将用户输入的字符串发送到服务器。3.接收服务器响应使用 Scanner 从输入流读取服务器发送的响应。将服务器的响应打印到控制台。4.循环处理在 while (true) 循环中不断读取用户输入发送请求接收响应直到程序被手动终止或出现异常。5.关闭连接在 finally 块中关闭客户端套接字确保资源被正确释放。public class TcpClient {public static void main(String[] args) throws IOException {System.out.println(客户端上线);Socket socket new Socket(10.90.113.182, 10002);try (InputStream inputStream socket.getInputStream();OutputStream outputStream socket.getOutputStream()) {Scanner scanner new Scanner(System.in);Scanner scanner1 new Scanner(inputStream);PrintWriter printWriter new PrintWriter(outputStream);while (true) {String string scanner.next();printWriter.println(string);printWriter.flush();System.out.println(scanner1.next());}} catch (Exception e) {e.printStackTrace();} finally {socket.close();}}}这里服务器的ip地址可以用root 的ip地址也可以用你自己主机的ip地址。但这里还是要说几个特殊的点1.这里由于我们采用的是printwrite所以要用flush冲刷数据否则可能有bug这里是输入流部分的知识点不清楚的可以看前面io文件操作那节博客2.在 TCP 网络编程中,Scanner.hasHext() 方法的行为与它在处理本地输入(如控制台输入或文件输入)时的行为有所不同。这里由于scanner是跟另一个主机的输出流联系在一块所以没有明确的消息边界。因此在执行Scanner.hasnext()时如果该输入流没数据那么该方法就会阻塞直到输入流中有数据可用则返回出true。如果客户端断开了连接连接的输出流则结束了断开了连接此时就有明确的消息边界Scanmer.hasNext()方法就因为没数据会返回 false所以退出。有消息边界时没数据则返回false有数据则返回true没消息边界时没数据则堵塞有数据则返回true不可能返回false3.对于该程序只支持单个客户端不支持多个客户端如果想要达到多个则要用多线程。public class TcpServe {public static void main(String[] args) throws IOException {System.out.println(服务器启动);ServerSocket serverSocket new ServerSocket(10010);while (true) {Socket socket serverSocket.accept();Thread thread new Thread(() - {try {TcpServe.startTask(socket);} catch (Exception e) {e.printStackTrace();}});thread.start();}}public static void startTask(Socket socket) throws IOException {try (InputStream inputStream socket.getInputStream();OutputStream outputStream socket.getOutputStream()) {Scanner scanner new Scanner(inputStream);PrintWriter printWriter new PrintWriter(outputStream);printWriter.flush();while (true) {if (!scanner.hasNext())break;String string scanner.next();String string1 process(string);printWriter.println(string1);printWriter.flush();}} catch (Exception e) {e.printStackTrace();} finally {socket.close();}}public static String process(String string) {return string;}}4.对于socket以及它带的两个字节流文件我们都需要手动close因为它们并不是全程跟随整个程序有可能中途这些文件就不用了所以就需要close对应的socket文件和字节流文件否则一直不关而我们一直使用该程序文件就会持续累积导致文件泄露问题。而servesocket就不需要因为它跟之前的Datagramsocket是全程跟随的所以没必要多此一举。5.对于这双方通信我们现在还是只限于一个主机内部如果要两个主机进行交流就需要部署一个云服务器或者在一个局域网内部才能进行交流。大概讲一下思路先把你写的IDEA文件打包为一个jar包然后在Xshell上打开需要有云服务器开始运行服务器然后将你的客户端中的服务器ip地址改为云服务器的ip地址这样就可以远程的实现俩个主机的交互了6.如果我们程序线程创建销毁太频繁了还可以用线程池这样能提高效率public class TcpServe {public static void main(String[] args) throws IOException {System.out.println(服务器启动);ServerSocket serverSocket new ServerSocket(10017);ExecutorService executorService Executors.newCachedThreadPool();while (true) {Socket socket serverSocket.accept();executorService.submit(() - {try {TcpServe.startTask(socket);} catch (Exception e) {e.printStackTrace();}});}}public static void startTask(Socket socket) throws IOException {try (InputStream inputStream socket.getInputStream();OutputStream outputStream socket.getOutputStream()) {Scanner scanner new Scanner(inputStream);PrintWriter printWriter new PrintWriter(outputStream);printWriter.flush();while (true) {if (!scanner.hasNext())break;String string scanner.next();String string1 process(string);printWriter.println(string1);printWriter.flush();}} catch (Exception e) {e.printStackTrace();} finally {socket.close();}}public static String process(String string) {return string;}}

更多文章