Java实现UDP与TCP应用程序
三、Java实现UDP应用程序
3.1 InetAddress类
java.net.InteAddress
类是用于描述IP地址和域名的一个Java类;
常用方法如下:
public static InetAddress getByName(String host)
:根据主机名获取InetAddress对象public String getHostName()
:获取该对象对应的主机名public String getHostAddress()
获取该对象对应的IP地址
示例代码:
package com.dfbz.demo01_实现UDP;import java.net.InetAddress;
import java.net.UnknownHostException;/*** @author lscl* @version 1.0* @intro:*/
public class Demo01_InetAddress {public static void main(String[] args) throws UnknownHostException {// 根据域名获取InetAddress对象InetAddress localhost = InetAddress.getByName("localhost");// 获取主机名String hostName = localhost.getHostName();// 获取ip地址String hostAddress = localhost.getHostAddress();System.out.println(hostName); // localhostSystem.out.println(hostAddress); // 127.0.0.1System.out.println("-----------");InetAddress baidu = InetAddress.getByName("www.baidu.com");System.out.println(baidu.getHostName()); // www.baidu.comSystem.out.println(baidu.getHostAddress()); // 14.119.104.254}
}
3.2 DatagramPacket类
java.net.DatagramPacket
是一种UDP协议的数据包结构,它包含源地址、目标地址和要传输的数据,用于封装一个UDP数据报文。
使用DatagramPacket,可以在应用程序之间发送和接收UDP数据包。
3.2.1 构造方法
public DatagramPacket(byte[] buf, int length, InetAddress address, int port)
:创建一个数据包对象buf
:要发送的内容length
:要发送的内容⻓度,单位字节address
:接收端的ip地址port
:接收端⼝号
public DatagramPacket(byte buf[], int length)
:创建一个数据包对象
示例代码:
package com.dfbz.demo01_实现UDP;import java.net.DatagramPacket;
import java.net.InetAddress;/*** @author lscl* @version 1.0* @intro:*/
public class Demo02_DatagramPacket_构造方法 {public static void main(String[] args) throws Exception {// 创建一个UDP报文用于发送DatagramPacket packet = new DatagramPacket("abc".getBytes(), // 报文封装的数据"abc".getBytes().length, // 报文的数据长度InetAddress.getByName("localhost"), // 接收端的地址8989 // 端口);// 创建一个UDP报文用于接收byte[] data = new byte[1024];DatagramPacket packet2 = new DatagramPacket(data,data.length);}
}
3.2.2 常用方法
public synchronized int getLength()
:获取此UDP数据包载荷的数据长度(单位字节)public synchronized int getPort()
:获取此UDP数据包的目的端口号public synchronized byte[] getData()
:获取此UDP数据包的载荷部分(数据)
示例代码:
package com.dfbz.demo01_实现UDP;import java.net.DatagramPacket;
import java.net.InetAddress;/*** @author lscl* @version 1.0* @intro:*/
public class Demo03_DatagramPacket_成员方法 {public static void main(String[] args) throws Exception {// 创建一个UDP报文用于发送UDP报文DatagramPacket packet = new DatagramPacket("abc".getBytes(),"abc".getBytes().length,InetAddress.getByName("localhost"),8989);byte[] data = packet.getData(); // 获取UDP报文的数据int length = packet.getLength(); // 获取UDP报文的数据长度InetAddress address = packet.getAddress(); // 获取该UDP报文要发送的地址int port = packet.getPort(); // 获取该UDP报文要发送的端口System.out.println("getData(): " + new String(data)); // abcSystem.out.println("getLength(): " + length); // 3System.out.println("getAddress(): " + address.getHostAddress()); // 127.0.0.1System.out.println("getPort(): " + port); // 8989}
}
3.2 DatagramSocket类
java.net.DatagramSocket
它提供了发送和接收UDP数据包的功能,用于描述一个UDP发送端或接收端;
在 Java 中,可以通过 DatagramSocket 类来创建一个套接字,并且可以通过 socket 的 receive 和 send 方法来发送和接收 UDP 数据包。这些方法提供了更好的灵活性,可以让您轻松地从多个地址和端口接收 UDP 数据包,并且可以选择性地发送数据包到特定地址和端口。 总的来说,DatagramSocket 是 UDP 协议的一种高级抽象,可以让您方便地在 Java 应用程序中发送和接收 UDP 数据包。
3.2.1 构造方法
public DatagramSocket(int port)
:通过端口构建一个发送端/接收端
示例代码:
DatagramSocket socket = new DatagramSocket(6969);
3.2.2 常用方法
public void send(DatagramPacket p)
:发送一个UDP数据包public synchronized void receive(DatagramPacket p)
:接收一个UDP数据包public void close()
:释放该Socket占用的资源
3.3 设计UDP应用程序
- 发送端:
package com.dfbz.demo01_实现UDP;import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;/*** @author lscl* @version 1.0* @intro:*/
public class Demo04_发送端 {public static void main(String[] args) throws Exception {// 创建一个UDP报文的发送/接收器DatagramSocket socket = new DatagramSocket();// 封装一个UDP报文byte[] data = new byte[8192];DatagramPacket packet = new DatagramPacket(data, data.length, InetAddress.getByName("localhost"), 9999);// 设置UDP报文的数据packet.setData("你好".getBytes());packet.setLength("你好".getBytes().length);// 发送UDP报文socket.send(packet);// 关闭资源socket.close();}
}
- 接收端:
package com.dfbz.demo01_实现UDP;import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;/*** @author lscl* @version 1.0* @intro:*/
public class Demo05_接收端 {public static void main(String[] args) throws IOException {// 创建一个UDP报文的发送/接收器DatagramSocket socket = new DatagramSocket(9999);// 创建一个UDP报文用于接收数据byte[] data = new byte[8192];DatagramPacket packet = new DatagramPacket(data, data.length);/*将socket接收到的UDP报文赋值给packet注意: 如果没有收到发送端的UDP报文,这一行代码将会阻塞当前线程的执行*/socket.receive(packet);socket.close();System.out.println("接收到了数据: " + new String(data, 0, packet.getLength()));}
}
四、Java实现TCP程序
在TCP通信中,分为数据的发送端(客户端)和接收端(服务器),当建立连接成功后(三次握手),才可以进行数据的发送;
在Java中,提供了两个类用于实现TCP通信程序:
- 1)客户端:
java.net.Socket
类表示;用于与服务器端建立连接,向服务器端发送数据报文等; - 2)服务端:
java.net.ServerSocket
类表示;用于与客户端的交互;
4.1 Socket
java.net.Sokcet
用于封装一个TCP应用程序的客户端;
4.1.1 构造方法
public Socket(String host, int port)
:创建套接字对象并将其连接到指定主机上的指定端口号。如果指定的host是null ,则相当于指定地址为本机地址。
示例代码:
Socket client = new Socket("127.0.0.1", 6868);
4.1.2 成员方法
public InputStream getInputStream()
: 返回此套接字的输入流。关闭生成的InputStream也将关闭相关的Socket。public OutputStream getOutputStream()
: 返回此套接字的输出流。关闭生成的OutputStream也将关闭相关的Socket。public void close()
:关闭此套接字。关闭此socket也将关闭相关的InputStream和OutputStream 。public void shutdownOutput()
: 禁用此套接字的输出流。任何先前写出的数据将被发送,随后终止输出流。
2.2 ServerSocket
ServerSocket
类:这个类实现了服务器套接字,该对象等待通过网络的请求。
4.2.1 构造方法
public ServerSocket(int port)
:使用该构造方法在创建ServerSocket对象时,就可以将其绑定到一个指定的端口号上,参数port就是端口号。
构造举例,代码如下:
ServerSocket server = new ServerSocket(6666);
4.2.2 成员方法
public Socket accept()
:监听并接受连接,返回一个新的Socket对象,用于和客户端实现通信。该方法会一直阻塞直到建立连接。
4.3 设计TCP应用程序
- 客户端代码:
package com.dfbz.demo01_实现TCP;import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;/*** @author lscl* @version 1.0* @intro:*/
public class Demo01_客户端 {public static void main(String[] args) throws Exception {// 创建一个客户端Socket socket = new Socket("localhost", 6666);// 获取与服务器的输入流(用于读取服务器的数据)InputStream is = socket.getInputStream();// 获取与服务器的输出流(用于向服务器写出数据)OutputStream os = socket.getOutputStream();// 发送数据到服务器os.write("你好呀~!在吗?".getBytes());byte[] data = new byte[1024];/*读取服务器的数据注意: 如果没有接收到服务器的数据,这一行代码将会阻塞当前线程的执行*/int len = is.read(data);System.out.println("接收到来自服务器的信息【" + new String(data, 0, len) + "】");os.write("在干嘛?".getBytes());/*读取服务器的数据注意: 如果没有接收到服务器的数据,这一行代码将会阻塞当前线程的执行*/len = is.read(data);System.out.println("接收到来自服务器的信息【" + new String(data, 0, len) + "】");socket.close();}
}
- 服务端代码:
package com.dfbz.demo01_实现TCP;import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;/*** @author lscl* @version 1.0* @intro:*/
public class Demo02_服务端 {public static void main(String[] args) throws Exception {// 创建了一个服务器ServerSocket serverSocket = new ServerSocket(6666);/*接收一个客户端注意: 如果没有客户端来连接服务器,这一行代码将会阻塞当前线程的执行*/Socket client = serverSocket.accept();// 获取与客户端的输入流(用于读取客户端的数据)InputStream is = client.getInputStream();// 获取与客户端的输出流(用于向客户端写出数据)OutputStream os = client.getOutputStream();byte[] data = new byte[1024];/*从客户端读取数据注意: 如果没有接收到客户端的数据,这一行代码将会阻塞当前线程的执行*/int len = is.read(data);System.out.println("接收到来自客户端的信息【" + new String(data, 0, len) + "】");os.write("在哦!".getBytes());/*从客户端读取数据注意: 如果没有接收到客户端的数据,这一行代码将会阻塞当前线程的执行*/len = is.read(data);System.out.println("接收到来自客户端的信息【" + new String(data, 0, len) + "】");os.write("在吃饭呢!".getBytes());// 关闭资源client.close();serverSocket.close();}
}
上述程序中,发送内容都是写死在代码中,我们使用Scanner来接受键盘录入的数据进行发送;
- 改造客户端程序:
package com.dfbz.demo02_使用Scanner改造;import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;/*** @author lscl* @version 1.0* @intro:*/
public class Demo01_客户端 {public static void main(String[] args) throws Exception {// 创建一个客户端,并且去连接服务器Socket socket = new Socket("localhost", 7777);// 获取与服务器的输入/输出流(用于读取服务器的数据/向服务器写出数据)InputStream is = socket.getInputStream();OutputStream out = socket.getOutputStream();Scanner scanner = new Scanner(System.in);byte[] data = new byte[1024];while (true) {String str = scanner.nextLine();// 向服务器写出数据out.write(str.getBytes());// 读取服务器的信息int len = is.read(data); // 这一句代码会造成阻塞,如果服务器没有向客户端写出数据,那么这一句代码阻塞System.out.println("来自服务器的信息【" + new String(data, 0, len) + "】");}}
}
- 改造服务端程序:
package com.dfbz.demo02_使用Scanner改造;import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;/*** @author lscl* @version 1.0* @intro:*/
public class Demo02_服务端 {public static void main(String[] args) throws Exception {// 创建一个服务器ServerSocket serverSocket = new ServerSocket(7777);// 接收到一个客户端(这一段代码会造成阻塞,如果没有客户端来连接服务器,那么代码会一直阻塞在这里)Socket client = serverSocket.accept();// 获取这个客户端的ip地址String hostAddress = client.getInetAddress().getHostAddress();// 获取这个客户端的端口int port = client.getPort();// 获取与这个客户端的输入/输出流(用于与这个客户端的读写操作)InputStream is = client.getInputStream();OutputStream os = client.getOutputStream();Scanner scanner = new Scanner(System.in);byte[] data = new byte[1024];while (true) {// 读取这个客户端的信息(如果客户端没有发送数据过来,这句代码会造成阻塞)int len = is.read(data);System.out.println("" + "来自客户端【" + hostAddress + "】" + "端口为【" + port + "】" + "的应用程序发送的消息【" + new String(data, 0, len) + "】");// 接收控制台的数据. 同样的,如果控制台没有输入数据,那么这一行代码也会阻塞当前线程的执行String str = scanner.nextLine();os.write(str.getBytes());}}
}
五、综合案例
5.1 图片上传案例
5.2.1 UDP实现图片上传
UDP图片上传流程:
需要注意的是:由于UDP的特点(面向无连接、不安全等),因此采用UDP协议上传的图片会造成数据丢失,图片失真、丢失像素等问题;
【发送端】
package com.dfbz.demo01_图片上传;import java.io.FileInputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;/*** @author lscl* @version 1.0* @intro:*/
public class Demo01_UDP_发送端 {public static void main(String[] args) throws Exception {// 创建一个套接字DatagramSocket socket = new DatagramSocket();// 从磁盘中读取文件FileInputStream fis = new FileInputStream("100.png");byte[] data = new byte[8192];// 创建一个UDP数据包DatagramPacket packet = new DatagramPacket(data, data.length, InetAddress.getByName("localhost"), 9999);int len;while ((len = fis.read(data)) != -1) {// 将从磁盘中读取到的数据设置到UDP报文中packet.setData(data);packet.setLength(len);// 发送UDP报文给接收端socket.send(packet);}// 发送一个空报文,作为结束标识packet.setLength(0);socket.send(packet);// 释放资源fis.close();socket.close();}
}
【接收端】
package com.dfbz.demo01_图片上传;import java.io.FileOutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;/*** @author lscl* @version 1.0* @intro:*/
public class Demo02_UDP_接收端 {public static void main(String[] args) throws Exception {// 创建一个套接字(用于接收UDP报文)DatagramSocket socket = new DatagramSocket(9999);byte[] data = new byte[8192];// 创建一个UDP数据包DatagramPacket packet = new DatagramPacket(data, data.length);// 创建一个文件输出流,将接收到的数据写入到这个文件中FileOutputStream fos = new FileOutputStream("udp.png");while (true) {// 接收UDP报文socket.receive(packet);// 获取报文数据长度int length = packet.getLength();if (length == 0) {// 说明读取到了末尾break;}fos.write(packet.getData(),0,length);}// 释放资源fos.close();socket.close();}
}
查看上传之前的原文件大小和上传之后的文件大小:
5.2.2 TCP实现图片上传
TCP图片上传流程:
1)客户端首先通过输入流将自己磁盘中的图片读取到内存中
2)客户端通过TCP连接的输出流,向服务器写出刚刚读取到的图片数据
3)服务器通过TCP连接的输入流,将客户端刚刚发送过来的图片数据读取到内存中
4)服务器通过输出流将内存中的数据写入到服务器的磁盘中
Tips:我们学习过程中,将服务器和客户端放在同一台机器。但实际开发中服务器和客户端不是在同一台机器,
【客户端代码实现】
package com.dfbz.demo02;import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.UUID;/*** @author lscl* @version 1.0* @intro:*/
public class Demo01_Server {public static void main(String[] args) throws Exception {// 声明服务器ServerSocket serverSocket = new ServerSocket(8888);// 接收到一个客户端Socket client = serverSocket.accept();System.out.println(client.getInetAddress().getHostAddress() + "连接成功");// 读取客户端传递过来的数据InputStream is = client.getInputStream();// 随机生成一个文件名写出到磁盘FileOutputStream fos = new FileOutputStream(UUID.randomUUID().toString() + ".png");byte[] data = new byte[1024];int len;while ((len = is.read(data)) != -1) {// 写出到磁盘fos.write(data, 0, len);}// 释放资源fos.close();client.close();}
}
【服务端代码实现】
package com.dfbz.demo01_图片上传;import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;/*** @author lscl* @version 1.0* @intro:*/
public class Demo04_TCP_服务端 {public static void main(String[] args) throws Exception {// 声明服务器ServerSocket serverSocket = new ServerSocket(8888);// 接收到一个客户端Socket client = serverSocket.accept();System.out.println(client.getInetAddress().getHostAddress() + "连接成功");// 读取客户端传递过来的数据InputStream is = client.getInputStream();// 随机生成一个文件名写出到磁盘FileOutputStream fos = new FileOutputStream("tcp.png");byte[] data = new byte[1024];int len;while ((len = is.read(data)) != -1) {// 写出到磁盘fos.write(data, 0, len);}// 释放资源fos.close();client.close();}
}
查看上传之前的原文件大小和上传之后的文件大小:
5.2.3 多线程改进TCP图片上传
实际开发中一个服务器对应N多个客户端,其他客户端均可以上传图片。我们的代码在同一时间只允许一个人上传图片,如果这个人上传的文件较大,那么势必会造成其他用户处于等待状态;针对这种情况我们可以使用多线程来解决。
服务器每次接受到一个客户端时,都开启一个线程来独立处理这个客户端的上传任务。这样在很多人同时来上传文件时,都可以一起上传。
- 多线程改进服务器:
package com.dfbz.demo01_图片上传;import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.UUID;/*** @author lscl* @version 1.0* @intro:*/
public class Demo05_TCP_服务端_多线程 {public static void main(String[] args) throws Exception {// 创建一个服务器ServerSocket serverSocket = new ServerSocket(8888);while (true) {// 每接收到一个客户端都创建一个新的线程为它服务Socket client = serverSocket.accept();System.out.println("客户端【" + client.getInetAddress().getHostAddress() + "】连接成功啦!");new Thread() {@Overridepublic void run() {try {// 获取与客户端的输入流(用于读取客户端发送过来的字节)InputStream is = client.getInputStream();// 关联本地的一个文件(随机生成一个名称)FileOutputStream fos = new FileOutputStream(UUID.randomUUID() + ".png");byte[] data = new byte[8192];int len;while ((len = is.read(data)) != -1) {fos.write(data, 0, len);}fos.close();client.close();} catch (IOException e) {e.printStackTrace();}}}.start();}}
}
5.2 在线聊天案例
我们刚刚使用了TCP完成了聊天功能的编写;我们会发现我们的程序是由问题的,就是读写是串行的!
我们整个应用程序只有一个线程,那就mian线程,代码都是从上往下执行,如果main线程当前在读操作,那么就不能写。而且如果此时一方如果没有发送信息给另一方,那么另一方的read方法将会一直处于阻塞状态,代码不会往下执行;此时想往对方写出数据肯定是不行的;
我们利用多线程技术来改造我们之前的代码,让我们的代码既一直读,又可以一直写;
5.2.1 多线程UDP在线聊天案例
【多线程改进发送端】
package com.dfbz.demo02_多线程实现聊天案例;import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;/*** @author lscl* @version 1.0* @intro:*/
public class Demo01_UDP_发送端 {public static void main(String[] args) throws Exception {new Thread(() -> {try {// 1. 创建一个套接字,用于发送数据到接收端DatagramSocket socket = new DatagramSocket();// 2. 创建一个UDP报文,用于封装要发送到接收端的数据(接收端的端口为9999)byte[] data = new byte[8192];DatagramPacket packet = new DatagramPacket(data, data.length, InetAddress.getLocalHost(), 9999);// 3. 死循环发送信息给接收端Scanner scanner = new Scanner(System.in);while (true) {// 接收键盘录入数据String line = scanner.nextLine();// 将数据设置到UDP报文中packet.setData(line.getBytes());packet.setLength(line.getBytes().length);// 将此UDP报文发送给接收端socket.send(packet);}} catch (Exception e) {e.printStackTrace();}}).start();new Thread(() -> {try {// 1. 创建一个套接字,用于接收来自接收端的数据(发送端的端口为7777)DatagramSocket socket = new DatagramSocket(7777);// 2. 创建一个UDP报文,用于接收来自接收端的数据byte[] data = new byte[8192];DatagramPacket packet = new DatagramPacket(data, data.length);// 3. 死循环接收来自接收端响应的信息while (true) {// 接收来自接收端的UDP报文socket.receive(packet);data = packet.getData();int len = packet.getLength();System.out.println("来自接收端【" + packet.getAddress().getHostAddress() + "】的信息: 【" + new String(data, 0, len) + "】");}} catch (Exception e) {e.printStackTrace();}}).start();}
}
【多线程改进接收端】
package com.dfbz.demo02_多线程实现聊天案例;import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;/*** @author lscl* @version 1.0* @intro:*/
public class Demo02_UDP_接收端 {public static void main(String[] args) throws Exception {// 接收端的读线程new Thread(() -> {try {// 1. 创建一个套接字,用于接收来自发送端的数据(接收端的端口为9999)DatagramSocket socket = new DatagramSocket(9999);// 2. 创建一个UDP报文,用于接收来自发送端的数据byte[] data = new byte[8192];DatagramPacket packet = new DatagramPacket(data, data.length);// 3. 死循环接收来自发送端响应的信息while (true) {// 接收来自发送端发送的UDP报文socket.receive(packet);data = packet.getData();int len = packet.getLength();System.out.println("来自发送端的【" + packet.getAddress().getHostAddress() + "】的信息: 【" + new String(data, 0, len) + "】");}} catch (Exception e) {e.printStackTrace();}}).start();// 接收端的写线程new Thread(() -> {try {// 1. 创建一个套接字,用于发送数据给发送端DatagramSocket socket = new DatagramSocket();// 2. 创建一个UDP报文,用于封装要发送到发送端的数据(发送端的端口为7777)byte[] data = new byte[8192];DatagramPacket packet = new DatagramPacket(data, data.length, InetAddress.getLocalHost(), 7777);// 3. 死循环发送信息给发送端Scanner scanner = new Scanner(System.in);while (true) {// 接收键盘录入数据String line = scanner.nextLine();// 将数据设置到UDP报文中packet.setData(line.getBytes());packet.setLength(line.getBytes().length);// 将此UDP报文发送给发送端socket.send(packet);}} catch (Exception e) {e.printStackTrace();}}).start();}
}
5.2.2 多线程TCP在线聊天案例
【多线程改进客户端】
package com.dfbz.demo02_多线程实现聊天案例;import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;/*** @author lscl* @version 1.0* @intro:*/
public class Demo03_TCP_客户端 {public static void main(String[] args) throws IOException {// 创建一个客户端,去连接服务器Socket socket = new Socket("127.0.0.1", 9999);// 获取与服务器的输入输出流InputStream is = socket.getInputStream();OutputStream os = socket.getOutputStream();// 读线程,专门用于读取服务器的信息new Thread() {@Overridepublic void run() {try {byte[] data = new byte[1024];while (true) {/*一直死循环读取服务器发送过来的数据如果服务器没有数据来也只是阻塞当前线程,并不会影响其他线程*/int len = is.read(data);System.out.println("接收到来自服务器的信息【" + new String(data, 0, len) + "】");}} catch (IOException e) {e.printStackTrace();}}}.start();// 写线程,专门向服务器写出数据new Thread() {@Overridepublic void run() {try {Scanner scanner = new Scanner(System.in);while (true) {/*一直死循环读取键盘录入的数据如果键盘没有输入数据,也只是阻塞当前线程,并不会影响其他线程*/String str = scanner.nextLine();os.write(str.getBytes());}} catch (IOException e) {e.printStackTrace();}}}.start();}
}
【多线程改进服务端】
package com.dfbz.demo02_多线程实现聊天案例;import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;/*** @author lscl* @version 1.0* @intro:*/
public class Demo04_TCP_服务端 {public static void main(String[] args) throws Exception {// 创建一台服务器ServerSocket serverSocket = new ServerSocket(9999);// 接收一个客户端Socket client = serverSocket.accept();// 获取与这个客户端的输入输出流InputStream is = client.getInputStream();OutputStream os = client.getOutputStream();// 获取客户端的IP地址String hostAddress = client.getInetAddress().getHostAddress();// 获取客户端的应用程序端口int port = client.getPort();System.out.println("有一个客户端来连接了,地址【" + hostAddress + "】,端口【" + port + "】");// 读线程new Thread() {@Overridepublic void run() {try {byte[] data = new byte[1024];while (true) {/*一直死循环读取客户端发送过来的数据如果客户端没有数据来也只是阻塞当前线程,并不会影响其他线程*/int len = is.read(data);System.out.println("接收到了来自客户端【" + hostAddress + "】的信息【" + new String(data, 0, len) + "】");}} catch (IOException e) {e.printStackTrace();}}}.start();// 写线程new Thread() {@Overridepublic void run() {try {Scanner scanner = new Scanner(System.in);while (true) {/*一直死循环读取键盘录入的数据如果键盘没有输入数据,也只是阻塞当前线程,并不会影响其他线程*/String str = scanner.nextLine();os.write(str.getBytes());}} catch (IOException e) {e.printStackTrace();}}}.start();}
}
相关文章:
Java实现UDP与TCP应用程序
三、Java实现UDP应用程序 3.1 InetAddress类 java.net.InteAddress类是用于描述IP地址和域名的一个Java类; 常用方法如下: public static InetAddress getByName(String host):根据主机名获取InetAddress对象public String getHostName()…...
[python3]Excel解析库-calamine,10倍openpyxl性能
calamine 是一个用于读取多种电子表格格式(如 Excel、LibreOffice Calc 等)的 Python 库。它支持 .xls, .xlsx, .ods 和 .csv 文件格式,提供了简单易用的 API 来加载和处理电子表格数据。calamine 的一大特点是它的轻量级和高效性,…...
Clisoft SOS设置Server和Project
Clisoft SOS设置Server和Project 一、关于SOS Servers、Clients、Projects和Work Areas 以下三个图是官方文档中介绍的三种情况 图1:带有两个客户端的SOS服务器 图2:使用本地缓存服务器 图3:远程设计团队的缓存服务器 因为SOS软件需要…...
基于FPGA的出租车里程时间计费器
基于FPGA的出租车里程时间计费器 功能描述一、系统框图二、verilog代码里程增加模块时间增加模块计算价格模块上板视频演示 总结 功能描述 (1);里程计费功能:3公里以内起步价8元,超过3公里后每公里2元,其中…...
AnaConda下载PyTorch慢的解决办法
使用Conda下载比较慢,改为pip下载 复制下载链接到迅雷下载 激活虚拟环境,安装whl,即可安装成功 pip install D:\openai.wiki\ChatGLM2-6B\torch-2.4.1cu121-cp38-cp38-win_amd64.whl...
Hello 2025(A-C)
补题链接:Dashboard - Hello 2025 - Codeforces A. MEX Table 思路 除了含0的列和行其他的都是0,输出max(n,m)1即可 代码 #include<bits/stdc.h> using namespace std;#define vcoistnt ios_base::sync_with_stdio(false); cin.tie(NULL); co…...
Burpsuite20241102macM1版安装
1、安装jdk11 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" brew update brew install openjdk11 echo export PATH"/opt/homebrew/opt/openjdk11/bin:$PATH" >> ~/.zshrc source ~/.zshrc j…...
jenkins入门10--自动化构建
build periodically:设定类似cron周期性时间触发构建 * * * * * (五颗星,中间用空格隔开) 第一颗表示分钟,取值0~59 第二颗表示小时,取值0~23 第三颗表示一个月的第几天,取值1~31 第四颗表示第几月…...
Java基础概念
自动装箱 Integer i 10; //装箱 int n i; //拆箱 普通数据类型:直接在栈内存中分配空间,存储的是具体的值。包装类:作为对象在堆内存中分配空间。包装类实际上是对普通数据类型的封装,每个包装类都包含了对应的数据类…...
57.在 Vue 3 中使用 OpenLayers 点击选择 Feature 设置特定颜色
在 Web 开发中,地图应用是非常常见的需求,而 OpenLayers 是一个非常强大的地图库,它提供了丰富的地图操作功能。今天,我们将一起学习如何在 Vue 3 中结合 OpenLayers 使用点击事件来选择地图上的 Feature,并设置特定的…...
HTML——61. 单行文本框和密码输入框(主讲input元素的type属性)
<!DOCTYPE html> <html><head><meta charset"UTF-8"><title>单行文本框和密码输入框</title></head><body><!--input元素的type属性:(必须要有)--> <!--单行文本框:1.type"text"2.可…...
h264之多视点mvc编码及解码过程(JMVC平台举例)
h264标准参考平台JMVC是针对MVC标准的,JMVC支持多视点编码、合流、多视点解码操作。可以利用JMVC生成h264 mvc码流和解码。 JMVC的下载地址是:jvet / JMVC GitLabH.264/AVC multi-view coding (MVC) extension JMVC reference softwarehttps://vcgit.hh…...
深度学习blog-深刻理解线性变换和矩阵
深度学习中避免不了矩阵运算,或者张量(其实是矩阵数组)运算。卷积是矩阵加、乘法,注意力也是一样。本质都一样,所谓注意力,卷积、滤波,是对不必了解数学的人说的,底层都是矩阵运算&a…...
C语言 扫雷程序设计
目录 1.main函数 2.菜单打印menu函数 3.游戏game函数 4.宏定义 5.界面初始化 6.打印界面 7.设置雷 8.统计排查坐标周围雷的个数 9.排查雷 10.总代码 test.c代码 game.h代码 game.c代码 结语: 一个简单的扫雷游戏,通过宏定义可以修改行列的…...
[笔记] Jenkins 安装与配置全攻略:Ubuntu 从零开始搭建持续集成环境
随着 DevOps 流程的普及,持续集成(CI)和持续交付(CD)已成为现代软件开发中不可或缺的一部分。Jenkins 作为一款开源的自动化服务器,广泛应用于 CI/CD 管道的构建与管理。它不仅支持多种编程语言和工具链&am…...
【51单片机零基础-chapter3:按键:独立按键|||附带常见C语句.逻辑运算符】
将unsigned char var0;看作沟通二进制和十进制的桥梁 var是8位,初始为0000 0000; 同时可以进行十进制的运算 逻辑运算 位运算 & 按位与(有0则0) | 按位或(有1则1) ~ 按位非 ^ 按位异或(相同则1,不同为0) <<按位左移 >>按位右移 位运算符解释: 0011 1100 <&…...
深入浅出:深层网络处理技术的教学指南
引言 在人工智能的浪潮中,深层网络处理技术(Deep Learning)无疑是最耀眼的明星之一。无论是图像识别、自然语言处理,还是语音识别,深层网络都展现出了强大的能力。然而,对于初学者来说,深层网络…...
深入浅出Node.js-1(node.js入门)
全新专栏带你快速掌握node.js Node.js入门 html,css,js 30年了 nodejs环境 09年出现 15年 nodejs为我们解决了2个方面的问题: 【锦上添花】让我们前端工程师拥有了后端开发能力(开接口,访问数据库) - 大公司BFF(5…...
Django的runserver
当年执行 python manage runserver命令时 1. 先执行 runserver 中的 handle方法 2. 执行 self.run()方法 3. 执行 self.inner_run() 3.1 inner_run 下 run方法的封装 3.1.1 接着看 handle 怎么来的 封装了一个方法 接着找返回函数 3.1.2在 basehttp 下 3.1.3 get_wsgi_appl…...
MySQL 存储引擎
InnoDB InnoDB是MySQL的默认存储引擎,自MySQL 5.5版本起开始使用。它提供了具有提交、回滚和崩溃恢复能力的事务安全(ACID兼容)存储引擎。 主要特性: 事务支持:完全支持ACID(原子性、一致性、隔离性、持久…...
React知识盲点——组件通信、性能优化、高级功能详解(大纲)
组件通信 React 组件通信详解 在 React 中,组件通信是一个核心概念,主要指的是如何让不同的组件共享和传递数据。React 提供了多种机制来实现组件间的数据传递和状态共享。以下是几种常见的组件通信方式,包括:父子组件通信&…...
Maven 详细配置:Maven 项目 POM 文件解读
Maven 是 Java 开发领域中广泛使用的项目管理和构建工具,通过其核心配置文件——POM(Project Object Model)文件,开发者能够定义项目的基本信息、依赖关系、插件配置以及构建生命周期等关键要素。POM 文件不仅是 Maven 项目的核心…...
selenium
pythonselenium selenium是一个第三方库,python有很多库; 1、什么是ui自动化? 通过模拟手工操作用户ui页面的方式,用代码去实现自动化操作和验证的行为。 2、ui自动化的优点? (1)解决重复性的功能测试…...
网络安全:设备原理与操作
设备型号概述 网络安全企业有哪些? 国外:思科,Juniper,惠普,3Com,。。。。 国内:华为,中性,锐捷,蓝盾,绿盟,山石网科,36…...
pytorch中nn.Conv2d详解及参数设置原则
文章目录 基础参数1. in_channels (输入通道数)2. out_channels (输出通道数)3. kernel_size (卷积核大小)4. stride (步幅)5. padding (填充)6. dilation (膨胀)7. groups (分组卷积)8. bias (偏置) 如何设置参数?1. **in_channels 和 out_channels(输入…...
select下拉框,首次进入页面没有显示value的情况
bug场景: 类似这种bug情况排查如下: 首先 理解含义 options就是存放键值对的,id就是key,对上了它就自动把label显示 而且如果你用来当作key和label的字段,与后端返回的不一致,还可以进行更改 其次 排查接…...
接口项目操作图-thinkphp6-rabbitmq
一、用户开户流程 用户首次需要联系商务开通账户,需要提供手机号及来访问的IP。开好户之后,平台方将提供用户访问的key值及header头部参数的公钥加密文件、body访问参数以及返回数据的公私钥加解密文件。 二、用户请求流程 用户将拿到的key值进行rsa公钥…...
thinkphp6.0常用设计模式实例
单例模式 (Singleton) 场景:确保一个类只有一个实例,并提供一个全局访问点。 实际业务:数据库连接、日志记录器、配置管理等。 ThinkPHP 6.0 实现: namespace app\common;class DatabaseConnection {private static $instance …...
微服务保护——Sentinel
什么是微服务保护? 微服务保护是一系列用于保障微服务架构稳定、可靠运行的策略与技术手段,在复杂的分布式微服务系统里,它能避免局部故障引发连锁反应,从而维持整个系统的可用性,主要涵盖以下几个关键部分:…...
php 多进程那点事,用 swoole 如何解决呢 ?
在 PHP 中,多进程的处理通常会遇到一些挑战,比如资源共享、进程间通信、性能优化等。Swoole 是一个高性能的协程和多进程框架,旨在为 PHP 提供异步、并发、协程等功能,解决了传统 PHP 环境中的多进程管理问题。通过使用 Swoole&am…...
STM32+ADC+DMA快速循环转换
测试平台:STM32F405RGT6 uint32_t AD_Buf[100]{0}; HAL_ADC_Start_DMA(&hadc2,(uint32_t *)AD_Buf,100); while(1) {printf("AD_Buf:%d\n",AD_Buf[0]); }...
移动电商的崛起与革新:以开源AI智能名片2+1链动模式S2B2C商城小程序为例的深度剖析
摘要:本文旨在探讨移动电商的崛起背景、特点及其对传统电商模式的革新影响,并以开源AI智能名片21链动模式S2B2C商城小程序为具体案例,深入分析其在移动电商领域的创新实践。随着移动互联网技术的飞速发展,移动电商已成为电商行业的…...
QT实现 端口扫描暂停和继续功能 3
上篇QT给端口扫描工程增加线程2-CSDN博客 为按钮pushButton_Stop添加clicked事件,功能为暂停扫描,并在暂停后显示继续按钮,点击继续按钮之后继续扫描 1.更新UI 添加继续按钮 点击转到槽则会自动声明 2. 更新 MainWindow.h 需要新增的部分…...
C_字符数组存储汉字字符串及其索引
字符串就是字符数组,可以定义一个char类型的数组来存储字符串。 如果要存储多个字符串则可以定义一个char类型的二维数组。 存储多个汉字字符串的话,可以考虑用char类型的二维数组。 不过要注意,一个汉字在内存中占用的字节数确实大于一个…...
Linux标准IOday1
1:思维导图 2:将 student.c这个练习题,改成链表后实现 头文件link.h #ifndef __STRUCT_H__ #define __STRUCT_H__ #include <stdio.h> #include <stdlib.h> typedef struct Student{char name[20];double math;double chinese;double english;double…...
SEO内容优化:如何通过用户需求赢得搜索引擎青睐?
在谷歌SEO优化中,内容一直是最重要的因素之一。但要想让内容真正发挥作用,关键在于满足用户需求,而不是简单地堆砌关键词。谷歌的算法越来越智能化,更注重用户体验和内容的实用性。 了解目标用户的需求。通过工具如Google Trends…...
API调用淘宝京东商品详情接口示例参考,json格式数据示例
以下是API调用淘宝和京东商品详情接口的JSON格式数据示例: 淘宝商品详情接口JSON数据示例 淘宝商品详情接口(通常称为item_get或类似的名称)是淘宝开放平台提供的一个API接口,允许开发者根据商品的ID(Item ID&#x…...
css实现垂直文本
效果 知识 writing-mode: <value>; 可选值 horizontal-tb: 默认值。文本从左到右(或从右到左)排列,然后从上到下。vertical-rl: 文本从上到下排列,然后从右到左。适用于垂直书写的方向,如日语和中文。vertica…...
【AI日记】25.01.07
【AI论文解读】【AI知识点】【AI小项目】【AI战略思考】【AI日记】【读书与思考】 AI 参加:kaggle 比赛 Forecasting Sticker Sales 读书 书名:国家为什么会失败阅读原因:2024 年诺贝尔经济学奖得主的力作,之前我已经读过他另一…...
logback日志
一、使用两个以上spring环境变量做三目操作 <springProperty name"application_name" scope"context" source"spring.application.name"/><springProperty name"trace_app_name" scope"context" source"sprin…...
Android NDK开发入门3之基本语法
JNI语法基础 函数生成语法: extern “ C” 作⽤:避免编绎器按照C的⽅式去编绎C函数 1、C不⽀持函数的重载,编译之后函数名不变; 2、C⽀持函数的重载(这点与Java⼀致),编译之后函数名会改变…...
unity学习9:unity的Asset 导入和导出
目录 1 Assets 资产/资源 1.1 编辑器里Assets 和explorer文件夹 里一一对应 1.2 在编辑器里操作,和文件夹内操作,多数相同还是有些不同 2 往Assets里导入零散文件 2.1 往Assets里导入零散文件 2.2 把fbx文件导入到hierarcy /scene 里,…...
Unity学习笔记(七)使用状态机重构角色攻击
前言 本文为Udemy课程The Ultimate Guide to Creating an RPG Game in Unity学习笔记 攻击状态重构 首先我们重构攻击状态的动画 之前的动画,我们是使用状态(isAttacking)攻击次数(comboCounter)完成动画的过渡,这样虽然能完成功能,但是如…...
【整理集合大全】MySQL(4) 数据库增删改查SQL语句
查看数据库 show databases; 使用数据库 use 数据库名;创建数据库 CREATE DATABASE 数据库名;删除数据库 DROP DATABASE 数据库名;创建表 create table 表名(列名1 类型(长度) [约束],列名2 类型(长度) [约束],…… );长度区别 int类型带长度:不影响存取值&…...
Flutter 鸿蒙化 flutter和鸿蒙next混和渲染
前言导读 这一个节课我们讲一下PlatformView的是使用 我们在实战中有可能出现了在鸿蒙next只加载一部分Flutter的情况 我们今天就讲一下这种情况具体实现要使用到我们的PlatformView 效果图 具体实现: 一、Native侧 使用 DevEco Studio工具打开 platform_view_example\oho…...
Flask返回浏览器无乱码方法
# -*- coding: utf-8 -*- from flask import Flask, request, jsonify, Response import os import json import re from datetime import datetime import logging import sys import crawling_web_knowledgeapp Flask(__name__)app.json.ensure_ascii False # 解决中文乱码…...
Tauri教程-基础篇-第二节 Tauri的核心概念下篇
“如果结果不如你所愿,就在尘埃落定前奋力一搏。”——《夏目友人帐》 “有些事不是看到了希望才去坚持,而是因为坚持才会看到希望。”——《十宗罪》 “维持现状意味着空耗你的努力和生命。”——纪伯伦 Tauri 技术教程 * 第四章 Tauri的基础教程 第二节…...
直播预告|StarRocks 3.4,打造 AI 时代的智能数据基座,应用场景全面扩展
随着新年的到来,StarRocks 3.4 即将上线,为 AI Workload 和更多应用场景提供强大支持!此次升级聚焦于提升 AI 场景支持,并扩展更多应用场景,全方位提升数据分析体验。 更强的 AI 场景支持: 引入 Vector In…...
Maven的基本使用
Maven apache 旗下的开源项目,是一款用于管理构建Java的项目的工具 一 作用 1依赖管理:管理jar包,避免依赖冲突 2统一项目结构 : 3项目构建: 二 安装 下面是全球唯一的中央仓库 https://repo1.maven.org/maven2…...
【深度学习入门_基础篇】线性代数本质
开坑本部分主要为基础知识复习,新开坑中,学习记录自用。 学习目标: 熟悉向量、线性组合、线性变换、基变换、矩阵运算、逆函数、秩、列空间、零空间、范式、特征指、特征向量等含义与应用。 强烈推荐此视频: 【官方双语/合集】…...