Java的网络通信
网络通信概述
网络通信编程的三要素:①IP地址 ②端口号 ③协议
IP常用命令 | 说明 |
---|---|
ipconfig | 查看本机ip地址 |
ping IP地址 | 检查网络是否连通 |
本机ip:127.0.0.1 or localhost
IP地址操作类:InetAddress
UDP通信
无连接、不可靠传输,UDP通信模型类似于传菜(盘子+菜),一次最多传输64KB
DatagramPacket(韭菜盘子)
DatagramSocket(收发双方对象)
UDP单发单收
接收端
public class UDPSever {
public static void main(String[] args) throws Exception {
//1.创建接收端对象
DatagramSocket socket = new DatagramSocket(8888);
//2.创建接收端数据包(盘子)
byte[] bytes = new byte[1024 * 64]; //64KB
DatagramPacket packet = new DatagramPacket(bytes, bytes.length);
//3.接受数据
socket.receive(packet);
//4.多余数据的裁剪
String string = new String(bytes,0, packet.getLength());
System.out.println("收到了:" + string);
//5.关闭套接字
socket.close();
}
}
发送端
public class UDPClient {
public static void main(String[] args) throws Exception {
//1.创建客户端对象(不声明端口则为默认端口号)
DatagramSocket socket = new DatagramSocket();
//2.创建客户端数据包(盘子)
byte[] bytes = "hello!this is my first udp program!".getBytes();
DatagramPacket packet = new DatagramPacket(bytes, bytes.length, InetAddress.getLocalHost(), 8888);
//3.发送数据
socket.send(packet);
//4.关闭套接字
socket.close();
}
}
UDP多发多收
收发加循环
接收端
public class UDPSever {
public static void main(String[] args) throws Exception {
//1.创建接收端对象
DatagramSocket socket = new DatagramSocket(8888);
//2.创建接收端数据包(盘子)
byte[] bytes = new byte[1024 * 64]; //64KB
DatagramPacket packet = new DatagramPacket(bytes, bytes.length);
//3.接受数据
while (true) {
socket.receive(packet);
//4.多余数据的裁剪
String string = new String(bytes,0, packet.getLength());
System.out.println("收到了来自" + packet.getAddress() + "的消息:" + string);
}
}
}
发送端
public class UDPClient {
public static void main(String[] args) throws Exception {
//1.创建客户端对象(不声明端口则为默认端口号)
DatagramSocket socket = new DatagramSocket();
Scanner scanner = new Scanner(System.in);
while(true) {
System.out.println("发送内容:");
String msg = scanner.nextLine();
if("exit".equals(msg)) {
System.out.println("退出聊天室");
break;
}
byte[] bytes = msg.getBytes();
DatagramPacket packet = new DatagramPacket(bytes, bytes.length, InetAddress.getLocalHost(), 8888);
socket.send(packet);
}
//4.关闭套接字
socket.close();
}
}
UDP广播、组播
UDP广播
通过将发送方的目的地址设置为255.255.255.255(广播地址)来实现广播消息
UDP组播
组播地址:224.0.0.0 ~239.255.255.255
组播地址绑定:通过DatagramSocket的子类MulticastSocket绑定组播ip
//1.创建多播对象
MulticastSocket socket = new MulticastSocket(8888);
//2.加入多播组
socket.joinGroup(InetAddress.getByName("225.123.123.123"));
TCP通信
面向连接、安全、可靠,点对点通信,可进行大数据量传输,使用java.net.Socket类实现通信,Socket类将IO操作结合,IO的写操作即是数据的传输操作
TCP一发一收
客户端
public class TCPClient {
public static void main(String[] args) {
try {
//创建发送端对象
Socket socket = new Socket("127.0.0.0",8888);
//获取输出流
OutputStream stream = socket.getOutputStream();
//将字节流升级为打印流
PrintStream ps = new PrintStream(stream);
//发送数据
ps.print("hello my friend!");
//刷新缓冲区(必须)
ps.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
服务端
ServerSocket类注册端口
public class TCPServer {
public static void main(String[] args) {
try {
//1.建立ServerSocket服务端,注册端口
ServerSocket serverSocket = new ServerSocket(7777);
//2.等待客户端连接,获取客户端对象
Socket socket = serverSocket.accept();
//3.实例化一个字节输入流
InputStream inputStream = socket.getInputStream();
//4.将字节输入流包装成更高级的缓冲字符输入流(字节输入流->转换流->缓冲字符输入流)
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
String msg;
if((msg = reader.readLine()) != null) {
System.out.println(socket.getInetAddress() + "发送了:" + msg);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
TCP多发多收
为了实现接收多个客户端的消息,需要使用多线程技术
客户端
public class TCPClient {
public static void main(String[] args) {
try {
//1.创建发送端对象
Socket socket = new Socket("127.0.0.1",7777);
Scanner scanner = new Scanner(System.in);
//2.获取输出流
OutputStream stream = socket.getOutputStream();
//3.将字节流升级为打印流
PrintStream ps = new PrintStream(stream);
//4.实现多发功能
while (true) {
System.out.println("请发送:");
//发送数据
ps.println(scanner.nextLine());
//刷新缓冲区
ps.flush();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
服务端
public class TCPServer {
public static void main(String[] args) {
try {
//1.建立ServerSocket服务端,注册端口
ServerSocket serverSocket = new ServerSocket(7777);
//2.实现多收(多线程)
while (true) {
//3.等待连接
Socket socket = serverSocket.accept();
System.out.println(socket.getInetAddress() + "加入了");
//4.启动线程
new work(socket).start();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
class work extends Thread {
private Socket socket;
//传入socket对象
public work(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
InputStream inputStream = null;
try {
inputStream = socket.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
String msg;
//5.收发消息
while (true) {
if ((msg = reader.readLine()) != null) {
System.out.println(socket.getInetAddress() + "发送了:" + msg);
}
}
} catch (Exception ex) {
System.out.println(socket.getInetAddress() + "退出了");
}
}
}
TCP多发多收的优化——线程池
防止开启线程过多,可以使用线程池来创建线程。适合客户端通信时长较短的场景。仅需要修改服务端
服务端
public class TCPServer {
//定义一个静态线程池
private static ExecutorService pool = new ThreadPoolExecutor(3, 5,
6, TimeUnit.SECONDS,new ArrayBlockingQueue<>(2) );
public static void main(String[] args) {
try {
//1.建立ServerSocket服务端,注册端口
ServerSocket serverSocket = new ServerSocket(7777);
//2.实现多收(多线程)
while (true) {
//3.等待连接
Socket socket = serverSocket.accept();
System.out.println(socket.getInetAddress() + "加入了");
//4.启动线程
Runnable run = new Pools(socket);
pool.execute(run);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
class Pools implements Runnable {
private Socket socket;
public Pools(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
InputStream inputStream = null;
try {
inputStream = socket.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
String msg;
//5.收发消息
while (true) {
if ((msg = reader.readLine()) != null) {
System.out.println(socket.getInetAddress() + "发送了:" + msg);
}
}
} catch (Exception ex) {
System.out.println(socket.getInetAddress() + "退出了");
}
}
}
TCP项目实战
即时通信
即时通讯是指客户端可以接收到其他客户端发送的信息,思想是存储管道进行端口转发。
客户端
public class TCPClient {
public static void main(String[] args) {
try {
Socket socket = new Socket("127.0.0.1",7777);
Scanner scanner = new Scanner(System.in);
OutputStream stream = socket.getOutputStream();
PrintStream ps = new PrintStream(stream);
//1.开启客户端的读线程
new ReadThread(socket).start();
while (true) {
System.out.println("请发送:");
ps.println(scanner.nextLine());
ps.flush();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
class ReadThread extends Thread {
private Socket socket;
public ReadThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
InputStream inputStream = null;
try {
inputStream = socket.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
String msg;
while (true) {
if ((msg = reader.readLine()) != null) {
System.out.println("收到了:" + msg);
}
}
} catch (Exception e) {
System.out.println("服务器已将你踢出群聊");
}
}
}
服务端
public class TCPServer {
private static ExecutorService pool = new ThreadPoolExecutor(3, 5,
6, TimeUnit.SECONDS,new ArrayBlockingQueue<>(2) );
//1.创建一个列表用于存放所有客户端socket对象
public static List<Socket> socketList = new ArrayList<>();
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket(7777);
while (true) {
Socket socket = serverSocket.accept();
//2.将加入的客户端socket对象加入列表
socketList.add(socket);
System.out.println(socket.getInetAddress() + "加入了");
Runnable run = new Pools(socket);
pool.execute(run);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
class Pools implements Runnable {
private Socket socket;
public Pools(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
InputStream inputStream = null;
try {
inputStream = socket.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
String msg;
//3.转发信息给其他客户端
while (true) {
if ((msg = reader.readLine()) != null) {
for (Socket socket1 : TCPServer.socketList) {
OutputStream os = socket1.getOutputStream();
PrintStream ps = new PrintStream(os);
ps.println(msg);
ps.flush();
}
}
}
} catch (Exception ex) {
//4.在列表中删除退出的客户端信息
TCPServer.socketList.remove(socket);
System.out.println(socket.getInetAddress() + "退出了");
}
}
}
BS结构(了解)
BS结构不同于CS结构,BS结构不需要开发客户端,是通过浏览器去访问服务端
BS服务器必须返回HTTP协议的报文