网络编程概念
可以让设备中的程序与网络上其他设备中的程序进行数据交互(实现网络通信)
Java网络编程解决方案
java.net.*包下提供了网络编程的解决方案
基本通信架构
基本的通信架构有两种形式:CS架构,BS架构
注意:无论是BS架构还是CS架构都必须依赖网络编程
网络通信的三要素
ip地址:
设备在网络中的地址,是唯一的标识
IPV6:共128位,IPV6分成8段表示,每段每四位编码成一个16进制位表示,数之间用冒号(:)分开
inetAdress
-
代表ip地址
package com.cy;
import java.net.InetAddress;
import java.net.UnknownHostException;/**
-
目标:掌握InetAdress类的使用
*/
public class InetAdressTest {
public static void main(String[] args) throws Exception{
//1、获取本机IP地址对象
InetAddress ip1 = InetAddress.getLocalHost();//获取本地ip地址对象
System.out.println(ip1.getHostName());//获取主机名字
System.out.println(ip1.getHostAddress());//获取主机具体ip地址信息//2、获取指定ip地址或者域名的IP地址对象 InetAddress ip2 = InetAddress.getByName("www.baidu.com"); System.out.println(ip2.getHostName()); System.out.println(ip2.getHostAddress()); //ping www.baidu.com System.out.println(ip2.isReachable(6000));}
}
-
端口号:
- 应用程序在设备中唯一的标识
- 标记正在计算机设备上运行的应用程序的,被规定为一个16位的二进制,范围是0~65535
分类
- 周知端口:0~1023,被预先定义的知名应用占用(如:HTTP占用80,FTP占用21 )
- 注册端口:1024~49151,分配给用户进程或某些应用程序
- 动态端口:49152~65535,之所以称为动态端口,是因为它一般不固定分配某种进程,而是动态分配
**注意:**我们自己开发的程序一般选择使用注册端口,且一个设备中不能出现两个程序的端口号一样,否则出错。
协议:
- 连接和数据在网络中传输的规则
- 网络上的通信设备事先规定的连接规则,以及传输数据的规则被称为网络通信协议
开放式网络互联标准:
- OSI网络参考模型,全球网络互联标准
- TCP/IP网络模型:事实上的国际标准
两种通信协议
-
UDP:用户数据报协议
特点:无连接,不可靠通信,但是通信效率高
-
TCP: 传输控制协议
特点:面向连接,可靠通信
三次握手建立可靠连接,数据传输进行确认,四次挥手断开通信
UDP 通信
UDP单次收发
Server.java
package com.cy.d2_udp1;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
/**
* 目标:完成UDP通信快速入门-服务端开发
*/
public class Server {
public static void main(String[] args)throws Exception{
System.out.println("======服务端启动======");
//1、创建服务端对象(创建一个接韭菜的人)
DatagramSocket socket = new DatagramSocket(6666);
//2、创建一个数据包对象,用于接收数据(创建一个韭菜盘子)
byte[] buffer = new byte[1024*64]; //64kb
DatagramPacket packet = new DatagramPacket(buffer,buffer.length);
//3、开始正式使用数据包来接收客户端发来的数据
socket.receive(packet);
//4、从字节数组中,把接收到的数据打印出来
//接收多少就倒出多少
//接收本次数据包接收了多少数据
int len = packet.getLength();
String rs = new String(buffer,0,len);
System.out.println(rs);
System.out.println(packet.getAddress().getHostAddress());
System.out.println(packet.getPort());
socket.close();//释放资源
}
}
Client.java
package com.cy.d2_udp1;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/**
* 目标:完成UDP通信快速入门------客户端开发:实现一发一收
* */
public class Client {
public static void main(String[] args)throws Exception{
//1、创建客户端对象(发韭菜出去的人)
DatagramSocket socket = new DatagramSocket(7777);
//2、创建数据包对象,封装发出去的数据(创建一个韭菜盒子)
/*public DatagramPacket(byte buf[], int length,
InetAddress address, int port)
参数一:封装要发出去的数据
参数二:发出去的数据大小
参数三:服务端的IP地址(找服务端的主机)
参数四:服务端程序的端口
*/
byte[] bytes = "我是快乐的客户端,我爱你abc".getBytes();
DatagramPacket packet = new DatagramPacket(bytes,bytes.length
,InetAddress.getLocalHost(),6666);
//3、开始正式发送这个数据包的数据出去
socket.send(packet);
System.out.println("客户端数据发送完毕");
socket.close();//释放资源
}
}
UDP通信------多发多收
client.java
package com.cy.d2_udp1;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;
/**
* 目标:完成UDP通信快速入门------客户端开发:实现多发多收
* */
public class Client {
public static void main(String[] args)throws Exception{
//1、创建客户端对象(发韭菜出去的人)
DatagramSocket socket = new DatagramSocket();
//2、创建数据包对象,封装发出去的数据(创建一个韭菜盒子)
/*public DatagramPacket(byte buf[], int length,
InetAddress address, int port)
参数一:封装要发出去的数据
参数二:发出去的数据大小
参数三:服务端的IP地址(找服务端的主机)
参数四:服务端程序的端口
*/
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("请说:");
String msg = sc.nextLine();
//一旦发现用户输入的exit命令,就退出客户端
if("exit".equals(msg)){
System.out.println("欢迎下次光临,退出成功");
socket.close();
break;//跳出死循环
}
byte[] bytes = msg.getBytes();
// byte[] bytes = "我是快乐的客户端,我爱你abc".getBytes();
DatagramPacket packet = new DatagramPacket(bytes,bytes.length
,InetAddress.getLocalHost(),6666);
//3、开始正式发送这个数据包的数据出去
socket.send(packet);
}
// System.out.println("客户端数据发送完毕");
// socket.close();//释放资源
}
}
server.java
package com.cy.d2_udp1;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
/**
* 目标:完成UDP通信快速入门-服务端开发
*/
public class Server {
public static void main(String[] args)throws Exception{
System.out.println("======服务端启动======");
//1、创建服务端对象(创建一个接韭菜的人)
DatagramSocket socket = new DatagramSocket(6666);
//2、创建一个数据包对象,用于接收数据(创建一个韭菜盘子)
byte[] buffer = new byte[1024*64]; //64kb
DatagramPacket packet = new DatagramPacket(buffer,buffer.length);
while (true) {
//3、开始正式使用数据包来接收客户端发来的数据
socket.receive(packet);
//4、从字节数组中,把接收到的数据打印出来
//接收多少就倒出多少
//接收本次数据包接收了多少数据
int len = packet.getLength();
String rs = new String(buffer,0,len);
System.out.println(rs);
System.out.println(packet.getAddress().getHostAddress());
System.out.println(packet.getPort());
System.out.println("------------------------------------------");
// socket.close();//释放资源
}
}
}
TCP 通信
Client.java
package com.cy.d4_tcp1;
import java.io.DataOutputStream;
import java.io.OutputStream;
import java.net.Socket;
public class Client {
public static void main(String[] args)throws Exception{
//1、创建socket对象,并同时请求与服务端程序进行连接
Socket socket = new Socket("127.0.0.1",8888);
//2、从socket通信管道中得到一个字节输出流,用来发数据给服务端程序
OutputStream os = socket.getOutputStream();
//3、把低级的字节输出流包装成数据输出流
DataOutputStream dos = new DataOutputStream(os);
//4、开始写数据出去
dos.writeUTF("在一起好吗?");
dos.close();
socket.close();//释放连接资源
}
}
Server.java
package com.cy.d4_tcp1;
import java.io.DataInputStream;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args)throws Exception{
System.out.println("-----------------服务端启动成功-------------");
//1、创建ServerSocket对象,同时为服务端注册端口
ServerSocket serversocket = new ServerSocket(8888);
//2、使用ServerSocket对象,调用一个accept方法,等待客户端的连接请求
Socket socket = serversocket.accept();
//3、从socket通信管道中得到一个字节输入流
InputStream is = socket.getInputStream();
//4、把原始的数据输入流包装成数据输入流
DataInputStream dis = new DataInputStream(is);
//5、使用数据输入流读取客户端发送过来的消息
String rs = dis.readUTF();
System.out.println(rs);
//其实也可以获取客户端的IP地址
System.out.println(socket.getRemoteSocketAddress());
dis.close();
socket.close();
}
}
多发多收
Client.java
package com.cy.d5_tcp2;
import javax.print.Doc;
import java.io.DataOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;
public class Client {
public static void main(String[] args)throws Exception{
//1、创建socket对象,并同时请求与服务端程序进行连接
Socket socket = new Socket("127.0.0.1",8888);
//2、从socket通信管道中得到一个字节输出流,用来发数据给服务端程序
OutputStream os = socket.getOutputStream();
//3、把低级的字节输出流包装成数据输出流
DataOutputStream dos = new DataOutputStream(os);
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("请说");
String msg = sc.nextLine();
//4、开始写数据出去
//一旦用户输入exit就退出输入端
if("exit".equals(msg)){
System.out.println("欢迎下次光临,退出成功");
dos.close();
socket.close();
break;
}
dos.writeUTF(msg);
dos.flush();
}
}
}
Server.java
package com.cy.d5_tcp2;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args)throws Exception{
System.out.println("-----------------服务端启动成功-------------");
//1、创建ServerSocket对象,同时为服务端注册端口
ServerSocket serversocket = new ServerSocket(8888);
//2、使用ServerSocket对象,调用一个accept方法,等待客户端的连接请求
Socket socket = serversocket.accept();
//3、从socket通信管道中得到一个字节输入流
InputStream is = socket.getInputStream();
//4、把原始的数据输入流包装成数据输入流
DataInputStream dis = new DataInputStream(is);
while (true) {
//5、使用数据输入流读取客户端发送过来的消息
try {
String rs = dis.readUTF();
System.out.println(rs);
} catch (IOException e) {
System.out.println(socket.getRemoteSocketAddress() + "离线");
dis.close();
socket.close();
break;
}
}
}
}
支持多个客户端同时通信
Client.java
package com.cy.d6_tcp3;
import java.io.DataOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;
public class Client {
public static void main(String[] args)throws Exception{
//1、创建socket对象,并同时请求与服务端程序进行连接
Socket socket = new Socket("127.0.0.1",8888);
//2、从socket通信管道中得到一个字节输出流,用来发数据给服务端程序
OutputStream os = socket.getOutputStream();
//3、把低级的字节输出流包装成数据输出流
DataOutputStream dos = new DataOutputStream(os);
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("请说");
String msg = sc.nextLine();
//4、开始写数据出去
//一旦用户输入exit就退出输入端
if("exit".equals(msg)){
System.out.println("欢迎下次光临,退出成功");
dos.close();
socket.close();
break;
}
dos.writeUTF(msg);
dos.flush();
}
}
}
Server.java
package com.cy.d6_tcp3;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args)throws Exception{
System.out.println("-----------------服务端启动成功-------------");
//1、创建ServerSocket对象,同时为服务端注册端口
ServerSocket serversocket = new ServerSocket(8888);
while (true) {
//2、使用ServerSocket对象,调用一个accept方法,等待客户端的连接请求
Socket socket = serversocket.accept();
System.out.println("有人上线了" + socket.getRemoteSocketAddress());
//3、把这个客户端对应的socket通信管道,交给一个独立的线程负责处理。
new ServerReaderThread(socket).start();
}
}
}
ServerReaderThread.java
package com.cy.d6_tcp3;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
public class ServerReaderThread extends Thread{
private Socket socket;
public ServerReaderThread(Socket socket){
this.socket = socket;
}
@Override
public void run(){
try {
InputStream is = socket.getInputStream();
DataInputStream dis = new DataInputStream(is);
while (true){
try {
String msg = dis.readUTF();
System.out.println(msg);
} catch (IOException e) {
System.out.println("有人下线了" + socket.getRemoteSocketAddress());
dis.close();
socket.close();
break;
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
TCP通信综合案例------群聊
Client.java
package com.cy.d6_tcp3;
import java.io.DataOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;
public class Client {
public static void main(String[] args)throws Exception{
//1、创建socket对象,并同时请求与服务端程序进行连接
Socket socket = new Socket("127.0.0.1",8888);
//创建一个独立的线程,负责随机从socket中接收服务端发来的消息
new ClienReaderThread(socket).start();
//2、从socket通信管道中得到一个字节输出流,用来发数据给服务端程序
OutputStream os = socket.getOutputStream();
//3、把低级的字节输出流包装成数据输出流
DataOutputStream dos = new DataOutputStream(os);
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("请说");
String msg = sc.nextLine();
//4、开始写数据出去
//一旦用户输入exit就退出输入端
if("exit".equals(msg)){
System.out.println("欢迎下次光临,退出成功");
dos.close();
socket.close();
break;
}
dos.writeUTF(msg);
dos.flush();
}
}
}
Server.java
package com.cy.d6_tcp3;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
public class Server {
public static List<Socket> onlineSockets = new ArrayList<>();
public static void main(String[] args)throws Exception{
System.out.println("-----------------服务端启动成功-------------");
//1、创建ServerSocket对象,同时为服务端注册端口
ServerSocket serversocket = new ServerSocket(8888);
while (true) {
//2、使用ServerSocket对象,调用一个accept方法,等待客户端的连接请求
Socket socket = serversocket.accept();
onlineSockets.add(socket);
System.out.println("有人上线了" + socket.getRemoteSocketAddress());
//3、把这个客户端对应的socket通信管道,交给一个独立的线程负责处理。
new ServerReaderThread(socket).start();
}
}
}
ServerReaderThread.java
package com.cy.d6_tcp3;
import java.io.*;
import java.net.Socket;
public class ServerReaderThread extends Thread{
private Socket socket;
public ServerReaderThread(Socket socket){
this.socket = socket;
}
@Override
public void run(){
try {
InputStream is = socket.getInputStream();
DataInputStream dis = new DataInputStream(is);
while (true){
try {
String msg = dis.readUTF();
System.out.println(msg);
//把这个消息分发给全部客户端进行接收
sendMsgToAll(msg);
} catch (IOException e) {
System.out.println("有人下线了" + socket.getRemoteSocketAddress());
Server.onlineSockets.remove(socket);
dis.close();
socket.close();
break;
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private void sendMsgToAll(String msg) throws IOException {
//发送给全部在线的socket管道接收
for (Socket onlineSocket : Server.onlineSockets) {
OutputStream os = onlineSocket.getOutputStream();
DataOutputStream dos = new DataOutputStream(os);
dos.writeUTF(msg);
dos.flush();
}
}
}
ClienReaderThread.java
package com.cy.d6_tcp3;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
public class ClienReaderThread extends Thread{
private Socket socket;
public ClienReaderThread(Socket socket){
this.socket = socket;
}
@Override
public void run(){
try {
InputStream is = socket.getInputStream();
DataInputStream dis = new DataInputStream(is);
while (true){
try {
String msg = dis.readUTF();
System.out.println(msg);
//把这个消息分发给全部客户端进行接收
} catch (IOException e) {
System.out.println("自己下线了" + socket.getRemoteSocketAddress());
dis.close();
socket.close();
break;
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
实现BS架构
Server.java
package com.cy.d8_tcp5;
import com.cy.d8_tcp5.ServerReaderThread;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args)throws Exception{
System.out.println("-----------------服务端启动成功-------------");
//1、创建ServerSocket对象,同时为服务端注册端口
ServerSocket serversocket = new ServerSocket(8081);
while (true) {
//2、使用ServerSocket对象,调用一个accept方法,等待客户端的连接请求
Socket socket = serversocket.accept();
System.out.println("有人上线了" + socket.getRemoteSocketAddress());
//3、把这个客户端对应的socket通信管道,交给一个独立的线程负责处理。
new ServerReaderThread(socket).start();
}
}
}
ServerReaderThread.java
package com.cy.d8_tcp5;
import javax.xml.crypto.Data;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
public class ServerReaderThread extends Thread{
private final Socket socket;
public ServerReaderThread(Socket socket){
this.socket = socket;
}
@Override
public void run(){
//立即响应网页内容,"黑马程序员"给浏览器展示
try {
OutputStream os = socket.getOutputStream();
PrintStream ps = new PrintStream(os);
ps.println("HTTP/1.1 200 OK");
ps.println("Content-Type:text/html;charset=UTF-8");
ps.println();
ps.println("<div style='color:red;font-size:120px;text-align:center'>黑马程序员666</div>");
ps.close();
socket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
线程池优化BS架构
Server.java
package com.cy.d9_tcp6.d8_tcp5;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.*;
public class Server {
public static void main(String[] args)throws Exception{
System.out.println("-----------------服务端启动成功-------------");
//1、创建ServerSocket对象,同时为服务端注册端口
ServerSocket serversocket = new ServerSocket(8081);
//创建出一个线程池,负责处理管道的任务
//参数1:核心线程数量(io密集型的话,当前计算机线程数量*2)
//参数2:最大线程数量
//参数3:临时线程存活时间
//参数4:s为单位
//任务5:缓存几个客户端的请求
//任务6:运用线程工厂
//任务7:拒绝任务策略
ThreadPoolExecutor pool = new ThreadPoolExecutor(16*2,16*2,0, TimeUnit.SECONDS
,new ArrayBlockingQueue<>(8), Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
while (true) {
//2、使用ServerSocket对象,调用一个accept方法,等待客户端的连接请求
Socket socket = serversocket.accept();
System.out.println("有人上线了" + socket.getRemoteSocketAddress());
//3、把这个客户端对应的socket通信管道,交给一个独立的线程负责处理。
pool.execute(new ServerReaderRunnable(socket));
}
}
}
ServerReaderRunnable.java
package com.cy.d9_tcp6.d8_tcp5;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
public class ServerReaderRunnable implements Runnable{
private final Socket socket;
public ServerReaderRunnable(Socket socket){
this.socket = socket;
}
@Override
public void run(){
//立即响应网页内容,"黑马程序员"给浏览器展示
try {
OutputStream os = socket.getOutputStream();
PrintStream ps = new PrintStream(os);
ps.println("HTTP/1.1 200 OK");
ps.println("Content-Type:text/html;charset=UTF-8");
ps.println();
ps.println("<div style='color:red;font-size:120px;text-align:center'>黑马程序员666</div>");
ps.close();
socket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}