网站快速过备案,湖南企业竞价优化公司,美食网站的建设论文,云南省城乡建设厅网站在Java的开发中#xff0c;有一个很重要#xff01;很重要#xff01;很重要#xff01;的东西#xff0c;叫做网络套接字#xff0c;它被广泛的用来二次开发服务#xff0c;比如大数据中台的服务链路调用等。
它的实现原理是依靠三次握手来完成通信的建立#xff0c;…在Java的开发中有一个很重要很重要很重要的东西叫做网络套接字它被广泛的用来二次开发服务比如大数据中台的服务链路调用等。
它的实现原理是依靠三次握手来完成通信的建立也是TCP传输控制协议连接建立过程中的一个重要机制。TCP是一种可靠的、面向连接的传输层协议它在正式传输数据之前需要通过三次握手来建立一个可靠的连接。这个过程涉及到客户端和服务器之间的交互具体步骤如下、 第一次握手客户端向服务器发送一个SYN同步序列编号报文段请求建立连接。这个报文段中SYN标志位被设置为1同时客户端会随机生成一个初始序列号seq作为自己的起始序号并将其发送给服务器。此时客户端进入SYN_SEND状态等待服务器的确认。
第二次握手服务器收到客户端的SYN报文段后会回复一个SYN-ACK同步-确认报文段给客户端。在这个报文段中SYN和ACK标志位都被设置为1表示服务器同意建立连接并同步自己的序列号。同时服务器会确认客户端的序列号即在ACK字段中填写客户端的序列号加1作为确认号ack。此时服务器进入SYN_RCVD状态。
第三次握手客户端收到服务器的SYN-ACK报文段后会发送一个ACK确认序列编号报文段给服务器以确认连接建立。在这个报文段中ACK标志位被设置为1同时确认号ack为服务器的序列号加1。此时客户端和服务器都进入ESTABLISHED状态表示连接已经成功建立可以开始传输数据了。
三次握手的目的和意义在于 确保双方通信能力正常通过三次握手客户端和服务器可以相互确认对方的发送和接收能力是否正常从而确保连接的可靠性。 同步双方初始序列号在三次握手过程中双方会交换并确认各自的初始序列号这是为了后续数据传输时能够正确地进行序号匹配和确认。 防止已失效的连接请求三次握手还可以防止因网络延迟或丢包等原因导致的已失效的连接请求被错误地接受。
总的来说三次握手是TCP连接建立过程中的一个关键步骤它确保了连接的可靠性和双方通信能力的正常性。在开发Java网络套接字时理解并掌握三次握手的原理和实现方式是非常重要的。 涉及到的类都在java.net包下如果你是刚接触套接字你可以用下面这个最简单的案例来感受一下套接字的能力
首先准备一个服务端
package one;import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;/*** 作者: wangyang br/* 创建时间: 2025/1/2 br/* 描述: 网络套接字的服务端用来处理所有客户端的链接请求 br/* nbsp;nbsp;nbsp;nbsp;ServerSoc*/
public class ServerSoc {public static void main(String[] args) {try {ServerSocket ss new ServerSocket(9000);System.out.println(等待连接。。。。。);//accept方法会阻塞进程检测到有客户端连接则放行Socket s1 ss.accept();System.out.println(连接成功);//向客户端输出一句话因此这里获取对客户端的输出流OutputStream os s1.getOutputStream(); //获得输出流byte[] b 客户端客户端我是服务器收到请回答.getBytes();os.write(b);os.flush();//必须刷一下不然写不进去//关闭资源os.close();s1.close(); ss.close();//关闭自己} catch (IOException e) {e.printStackTrace();}}}然后是客户端
package one;import java.io.InputStream;
import java.net.Socket;/*** 作者: wangyang br/* 创建时间: 2025/1/2 br/* 描述: 网络套接字的客户端用来链接服务端面向用户操作 br/* nbsp;nbsp;nbsp;nbsp;ClientSoc*/
public class ClientSoc {public static void main(String[] args) {try {Socket s new Socket(127.0.0.1, 9000);//客户端要读取服务端的信息所以这里获取连接中的输入流InputStream is s.getInputStream();byte[] b new byte[100];//读取int num is.read(b);String msg new String(b, 0, num);System.out.println(msg);//关资源is.close();s.close();} catch (Exception e) {e.printStackTrace();}}}启动的时候要先启动服务端然后在启动客户端你就可以看到如下的输出 同样的客户端也可以在接收完信息后给服务端也写一句话你只需要在上面的代码中加上各自的输入和输出流即可
服务端
package one;import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;/*** 作者: wangyang br/* 创建时间: 2025/1/2 br/* 描述: 网络套接字的服务端用来处理所有客户端的链接请求 br/* nbsp;nbsp;nbsp;nbsp;ServerSoc*/
public class ServerSoc {public static void main(String[] args) {try {ServerSocket ss new ServerSocket(9000);System.out.println(等待连接。。。。。);//accept方法会阻塞进程检测到有客户端连接则放行Socket s1 ss.accept();System.out.println(连接成功);//向客户端输出一句话因此这里获取对客户端的输出流OutputStream os s1.getOutputStream(); //获得输出流byte[] b 客户端客户端我是服务器收到请回答.getBytes();os.write(b);os.flush();//必须刷一下不然写不进去/*服务端这里多了从客户端读消息的输入流也就是上面的代码向客户端写一句话之后服务端从连接的客户端中获取输入流从而等待客户端给服务端发送的信息*/InputStream in s1.getInputStream();byte[] b1 new byte[100];int read in.read(b1);String msg new String(b1, 0, read);System.out.println(msg);//关闭资源in.close();os.close();s1.close();ss.close();} catch (IOException e) {e.printStackTrace();}}}客户端
package one;import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;/*** 作者: wangyang br/* 创建时间: 2025/1/2 br/* 描述: 网络套接字的客户端用来链接服务端面向用户操作 br/* nbsp;nbsp;nbsp;nbsp;ClientSoc*/
public class ClientSoc {public static void main(String[] args) {try {Socket s new Socket(127.0.0.1, 9000);//客户端要读取服务端的信息所以这里获取连接中的输入流InputStream is s.getInputStream();byte[] b new byte[100];//读取int num is.read(b);String msg new String(b, 0, num);System.out.println(msg);/*客户端这里在接收完服务端的信息后向服务端写入一条信息*/OutputStream out s.getOutputStream();byte[] b1 服务器服务器我是客户端收到请回答.getBytes();out.write(b1);out.flush();//必须刷一下不然写不进去//关资源out.close();is.close();s.close();} catch (Exception e) {e.printStackTrace();}}}同样的先启动服务端然后启动客户端连接就会得到如下输出
上面这两个案例可以让你很方便的了解到Java提供的网络套接字能力当然这不是全部你还可以获取客户端的ip等等这些都可以办到。但是上面的案例中有着写入顺序的限制也就是无论那边都不能同时先发信息或接受不然流就拥堵了 那么怎么能够实现基本的双向通道来实现一对一正常对话的效果可以用如下的代码实现
服务端
package one;import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;/*** 作者: wangyang br/* 创建时间: 2025/1/2 br/* 描述: 网络套接字的服务端用来处理所有客户端的链接请求 br/* nbsp;nbsp;nbsp;nbsp;ServerSoc*/
public class ServerSoc {public static void main(String[] args) {try {ServerSocket ss new ServerSocket(9000);System.out.println(等待连接。。。。。);//accept方法会阻塞进程检测到有客户端连接则放行Socket s1 ss.accept();System.out.println(连接成功);//获取来自客户端的输入和输出流向客户端发送与接收信息BufferedReader in new BufferedReader(new InputStreamReader(s1.getInputStream()));PrintWriter out new PrintWriter(s1.getOutputStream());Scanner input new Scanner(System.in);//服务端获取控制台输入的扫描器while(true){//服务端先开始写信息发给客户端String serverMsg input.nextLine();out.println(serverMsg);out.flush();//发送完消息后进入等待发消息的状态String clientMsg in.readLine();System.out.println(clientMsg);}//关闭资源
// in.close();
// out.close();
// s1.close();
// ss.close();} catch (IOException e) {e.printStackTrace();}}}客户端
package one;import java.io.*;
import java.net.Socket;
import java.util.Scanner;/*** 作者: wangyang br/* 创建时间: 2025/1/2 br/* 描述: 网络套接字的客户端用来链接服务端面向用户操作 br/* nbsp;nbsp;nbsp;nbsp;ClientSoc*/
public class ClientSoc {public static void main(String[] args) {try {Socket s new Socket(127.0.0.1, 9000);//客户端一样获取服务端的输入和输出流向服务端端发送与接收信息BufferedReader in new BufferedReader(new InputStreamReader(s.getInputStream()));PrintWriter out new PrintWriter(s.getOutputStream());Scanner input new Scanner(System.in);//服务端获取控制台输入的扫描器while(true){//等待服务端发来的消息String serverMsg in.readLine();System.out.println(serverMsg);//接收完消息进入向服务端写数据的状态String clientMsg input.nextLine();out.println(clientMsg);out.flush();}//关资源
// out.close();
// in.close();
// s.close();} catch (Exception e) {e.printStackTrace();}}}启动还是一样的顺序先器服务端并先由服务端发送消息 上面的案例虽然初步的让服务端和客户端都能发送和接收消息但是很明显仍然没有摆脱需要有一方先写不能一起先写的限制而且还是只能一句一句的来因为用的是死循环当然至于那一方先写是你自己决定的。那么怎么能够彻底摆脱这个问题这就要用到多线程了你可以把接收信息的流程放在子线程里面如下
服务端
package one;import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;/*** 作者: wangyang br/* 创建时间: 2025/1/2 br/* 描述: 网络套接字的服务端用来处理所有客户端的链接请求 br/* nbsp;nbsp;nbsp;nbsp;ServerSoc*/
public class ServerSoc {public static void main(String[] args) {try {ServerSocket ss new ServerSocket(9000);System.out.println(等待连接。。。。。);//accept方法会阻塞进程检测到有客户端连接则放行Socket s1 ss.accept();System.out.println(连接成功);//获取客户端的输入和输出流向客户端发送与接收信息BufferedReader in new BufferedReader(new InputStreamReader(s1.getInputStream()));PrintWriter out new PrintWriter(s1.getOutputStream());Scanner input new Scanner(System.in);//服务端获取控制台输入的扫描器/*将双方的接收放在子线程中*/new Thread(new Runnable() {Overridepublic void run() {String clientMsg null;try {while (true){clientMsg in.readLine();System.out.println(clientMsg);}} catch (IOException e) {throw new RuntimeException(e);}}}).start();//主线程正常进入死循环写信息while(true){String serverMsg input.nextLine();out.println(serverMsg);out.flush();}//关闭资源
// in.close();
// out.close();
// s1.close();
// ss.close();} catch (IOException e) {e.printStackTrace();}}}客户端
package one;import java.io.*;
import java.net.Socket;
import java.util.Scanner;/*** 作者: wangyang br/* 创建时间: 2025/1/2 br/* 描述: 网络套接字的客户端用来链接服务端面向用户操作 br/* nbsp;nbsp;nbsp;nbsp;ClientSoc*/
public class ClientSoc {public static void main(String[] args) {try {Socket s new Socket(127.0.0.1, 9000);//客户端一样获取服务端的输入和输出流向服务端端发送与接收信息BufferedReader in new BufferedReader(new InputStreamReader(s.getInputStream()));PrintWriter out new PrintWriter(s.getOutputStream());Scanner input new Scanner(System.in);//服务端获取控制台输入的扫描器/*将双方的接收放在子线程中*/new Thread(new Runnable() {Overridepublic void run() {String clientMsg null;try {while (true){clientMsg in.readLine();System.out.println(clientMsg);}} catch (IOException e) {throw new RuntimeException(e);}}}).start();//主线程正常进入死循环写信息while(true){String clientMsg input.nextLine();out.println(clientMsg);out.flush();}//关资源
// out.close();
// in.close();
// s.close();} catch (Exception e) {e.printStackTrace();}}}这次启动服务端和客户端后就可以实现畅通无阻的对话了 上面的案例始终是但对单的那么怎么实现群聊呢其实现在就很简单了不需要动客户端让它保持现状只需要改造一下服务端即可。上面的所有案例中服务端其实有点不务正业了只不过为了简化代码强行给它写了手动输入并发送的代码兼职一个客户端的身份相互直接对话但其实它只需要负责接收客户端的消息并按照要求发给所有客户端连接即可要完成这样的流程需要对服务端做出如下更改
首先抽离服务端中所有的客户端流程并编写所有服务端业务
package one;import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;/*** 作者: wangyang br/* 创建时间: 2025/1/2 br/* 描述: 网络套接字的服务端用来处理所有客户端的链接请求 br/* nbsp;nbsp;nbsp;nbsp;ServerSoc*/
public class ServerSoc {//服务端持有一个所有链接的容器集合public static ArrayListSocket connections new ArrayListSocket();//服务端群发信息方法参数是要发送的消息public static void sendAll(String s) throws IOException {if (connections ! null) {for (int i 0;iconnections.size();i) { //遍历Socket集合//获取每个socket的输出流PrintWriter pw new PrintWriter( connections.get(i).getOutputStream() );//使用每个输出流发送同一条消息pw.println(s);pw.flush();//这里有个重点不能关流因为要不停的监听消息并发送所以不能再这里关流}}}public static void main(String[] args) {try {ServerSocket ss new ServerSocket(9000);//服务端等待所有的连接while (true){System.out.println(等待新连接。。。。。);//accept检测到链接之后将这个套接字对象放入容器并且为每个连接启动一个监听读取的线程Socket s1 ss.accept();connections.add(s1);new ServerThreadForClient(s1).start();System.out.println(连接成功,当前所以连接如下--》);System.out.println(connections);}} catch (IOException e) {e.printStackTrace();}}}为每一个连接启动一个监听消息的线程类
package one;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;/*** 作者: wangyang br/* 创建时间: 2025/1/2 br/* 描述: br/* nbsp;nbsp;nbsp;nbsp;ServerThreadForClient 用来承载服务端对每个连接的发送消息处理*/
public class ServerThreadForClient extends Thread{private Socket s null;public ServerThreadForClient(Socket adds){this.s adds;}/*** 描述: 为一个连接启动发出消息的监听 br/* 作者: wangyang br/* 创建时间: 2025/1/2 br/* 参数: br/* 返回值: br/*/Overridepublic void run() {try {BufferedReader in new BufferedReader(new InputStreamReader(s.getInputStream()));String clientMsg null;while (true){clientMsg in.readLine();//调用服务端的方法向所有的客户端发送消息ServerSoc.sendAll(clientMsg);}} catch (IOException e) {throw new RuntimeException(e);}}
}客户端保持不变
package one;import java.io.*;
import java.net.Socket;
import java.util.Scanner;/*** 作者: wangyang br/* 创建时间: 2025/1/2 br/* 描述: 网络套接字的客户端用来链接服务端面向用户操作 br/* nbsp;nbsp;nbsp;nbsp;ClientSoc*/
public class ClientSoc {public static void main(String[] args) {try {Socket s new Socket(127.0.0.1, 9000);System.out.println(客户端连接服务器成功);//客户端一样获取服务端的输入和输出流向服务端端发送与接收信息BufferedReader in new BufferedReader(new InputStreamReader(s.getInputStream()));PrintWriter out new PrintWriter(s.getOutputStream());Scanner input new Scanner(System.in);//服务端获取控制台输入的扫描器/*将双方的接收放在子线程中*/new Thread(new Runnable() {Overridepublic void run() {String clientMsg null;try {while (true){clientMsg in.readLine();System.out.println(clientMsg);}} catch (IOException e) {throw new RuntimeException(e);}}}).start();//主线程正常进入死循环写信息while(true){String clientMsg input.nextLine();out.println(clientMsg);out.flush();}} catch (Exception e) {e.printStackTrace();}}}启动服务端后启动多个客户端就可以看到如下效果 到此Java套接字的基本应用就介绍完了可以自己以此为基础衍生很多有意思的小程序市面上也有很多现成的类库和架构来完成不同的能力比如上面的socket其实是一种二进制接口想要完成丰富的能力需要写很复杂的代码而Springboot提供了websocket用来在web的基础上完成套接字的实现从而实现在线客服等能力的支持