网络编程(Java)
一、概述(CS-BS架构)
1.1 网络编程
可以计设备中的程序与网络上其他设备中的程序进行数据交互的技术(实现网络通信)。
1.2 基本的通信架构
本的通信架构有2种形式:CS架构(Client客户端/Server服务端)、BS架构(Browser浏览器/Server服务端)。
client-Server(cS)架构
Browser-Server(BS)架构
二、IP
2.1、IP地址
IP(Internet Protocol):全称”互联网协议地址”,是分配给上网设备的唯一标识。
目前,被广泛采用的IP地址形式有两种:IPv4、IPv6。
2.2、IPv4
IPv4是InternetProtocolversion 4的缩写,它使用32位地址,通常以点分十进制表示。
2.3、IPv6
IPv6是Internet Protocolversion 6的缩写,它使用128位地址,号称可以为地球上的每一粒沙子编号。
IPv6分成8段,每段每四位编码成一个十六进制位表示,每段之间用冒号(:)分开,将这种方式称为冒分十六进制。
2.4、IP域名(Domain Name)
用于在互联网上识别和定位网站的人类可读的名称。
例如:
www.baidu.com
DNS域名解析(Domain Name System)
是互联网中用于将域名转换为对应IP地址的分布式命名系统,它充当了互联网的"电话薄“,
将易记的域名映射到数字化2的IP地址,使用用户可以通过域名来访问网站和其他网络资源
2.5、公网IP、内网IP
公网IP:是可以连接到互联网的IP地址
内网IP:也叫局域网IP是只能组织机构内部使用的I P地址;例如,192.168. 开头就是常见的局域网地址,范围为192.168.0.0--192.168.255.255 专门为组织机构内部使用
本机IP
127.0.0.1 、localhost:代表本机IP 只会寻找当前程序所在的主机
IP常用命令
ipconfig:查看本机IP地址。
ping 地址 :检查网络是否连通
2.6、InetAddress
代表IP地址。
InetAddress的常用方法
InetAddress类的常用方法 | 说明 |
public static InetAddress getlocalHosto throws UnknownHostException | 获取本机IP,返回一个InetAddress对象 |
public String getHostNameO | 获取该IP地址对象对应的主机名 |
public String getHostAddresS( | 获取该ip地址对象中的ip地址信息。 |
public static InetAddress getByName(String host) throws UnknownHostException | 根据ip地址或者域名,返回一个inetAddress对象 |
public boolean]isReachable(int timeout) thhrows IOException | 判断主机在指定毫秒内与该ip对应的主机是否能连通 |
代码:
package Demo04;import java.net.InetAddress;
import java.net.UnknownHostException;public class IntetAddressDemo01 {public static void main(String[] args) {// 获取本机IPtry {//1、获取本级IP对象InetAddress ip1 = InetAddress.getLocalHost();System.out.println(ip1);System.out.println(ip1.getHostName());System.out.println(ip1.getHostAddress());//2、获取对象IP对象InetAddress ip2 = InetAddress.getByName("www.baidu.com");System.out.println(ip2.getHostName());System.out.println(ip2.getHostAddress());//3、判断本机是否与对象主机互通System.out.println(ip2.isReachable(5000));} catch (Exception e) {e.printStackTrace();}}
}
三、端口-协议
3.1、端口
用来标记标记正在计算机设备上运行的应用程序,被规定为一个16位的二进制,利,范围是0~65535。
端口分类
周知端口:0~1023,被预先定义的知名应用占用(如:HTTP占用80,FTP占用21)
注册端口:1024~49151,1,分配给用户进程或某些应用程序。
动态端口:49152到65535,之所以称为动态端口,是因为它一般不固定分配某种进程,而是动 态分配。
注意:我们自己开发的程序-般选择使用注册端口,且一个设备中不能出现两个程序的端口号一样,否则报错。
3.2、通通信协协议
网络上通信的设备事先规定的连接规则以及传输数据的规则被称为网络通信协议。
3.3、 OSI网络参考模型
OSI网络参考模型:全球网络互联标准。
TCP/IP络模型:事实上的国际标准。
3.4、传输层的2个通信协议
UDP(User Datagram Protocol):用户数据报协议。
UDP协议 通信效率高 (视频直播)
特点:无连接、不可靠通信
不事先建立连接,数据按照包发,一包数据包含:自己的IP、端口、目的地IP、端口和数据(限制在64KB内)等。
发送方不管对方是否在线,数据在中间丢失也不管,如果接受方收到数据也不返回确认,故是不可靠的。
TCP(Transmission Control Protocol):传输控制协议。
特点:面向连接、可靠通信。
TCP的最终目的:要保证在不可靠的信道上实现可靠的数据传输。
TCP主要有三个步骤实现可靠传输:三次握手建立连接,传输数据确认,四次握手挥手断开连接。
三次握手建立靠连接
可靠连接:确保通信的双方收发消息都是没问题的(全双工)
四次挥手断开连接
确保通信的双方收发消息都已经完成
四、UDP通信
4.1、UDP通信的实现
特点:无连接、不可靠通信。
不事先建立连接;发送端每次把要发送的数据(限制在64KB内)、接收端IP、等信息封装成一个数据包,发出去就不管了。
Java提供了一个java.net.DatagramSocket类来实现UDP通信。
DatagramSocket: 用于创建客户端、服务端
构造器 | 说明 |
public DatagramSocket() | 创建客户端的Socket对象, 系统会随机分配一个端口号。 |
public DatagramSocket(int port) | 创建服务端的Socket对象, 并指定端口号 |
方法 | 说明 |
public void send(DatagramPacket dp) | 发送数据包 |
public void receive(DatagramPacket p) | 使用数据包接收数据 |
DatagramPacket:创建数据包
构造器 | 说明 |
public DatagramPacket(byte[] buf, int length, InetAddress address, int port) | 创建发出去的数据包对象 |
public DatagramPacket(byte[] buf, int length) | 创建用来接收数据的数据包 |
方法 | 说明 |
public int getLength() | 获取数据包,实际接收到的字节个数 |
4.2、使用UDP通信实现:发送消息、接收消息
4.2.1、一发一收
1、客户端实现步骤
package Demo05;import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;public class UDPClientDemo1 {public static void main(String[] args) throws Exception {//完成UDP通信 客户端开发System.out.println("客户端启动了~");//1、创建发送对象DatagramSocket socket = new DatagramSocket();//2、创建数据包对象封装要发送的数据byte[] bytes = "我是客户端 发送一份小龙虾".getBytes();/*** 参数一,发送的数据,字节数组* 参数二,发送的字节长度* 参数三,目的地的IP地址* 参数四,服务器的端口号*/DatagramPacket packet = new DatagramPacket(bytes, bytes.length, InetAddress.getLocalHost(),8080);//3、让发送端对象发送数据包的数据socket.send(packet);//4、关闭套接字socket.close();}
}
2、服务端实现步骤
package Demo05;import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;public class UDPServerDemo2 {public static void main(String[] args) throws Exception {
// 完成UDP通信 服务端开发System.out.println("服务端启动了~");//1、创建接收端对象,注册端口DatagramSocket socket = new DatagramSocket(8080);//2、创建数据包对象,用于接收数据byte[] buf = new byte[1024 * 64];DatagramPacket packet = new DatagramPacket(buf, buf.length);//3、让接收端对象开始接收数据socket.receive(packet);//4、获取接收的数据String data = new String(buf);System.out.println("服务器接受了:" + data);//5、获取对方的ip对象和程序端口String ip = packet.getAddress().getHostAddress();int port = packet.getPort();System.out.println("对方ip:" + ip + "对方端口:" + port);}
}
4.2.2、多发多收
1、客户端可以反复发送数据
客户端实现步骤
package Demo06;import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;public class UDPClientDemo1 {public static void main(String[] args) throws Exception {//完成UDP通信 多发多收 客户端开发System.out.println("客户端启动了~");//1、创建发送对象DatagramSocket socket = new DatagramSocket();Scanner sc = new Scanner(System.in);while (true) {//2、创建数据包对象封装要发送的数据System.out.println("请输入:");String msg = sc.nextLine(); //你好 在干嘛?if ("exit".equals(msg)) {System.out.println("客户端成功退出~");socket.close();break;}byte[] bytes = msg.getBytes();/*** 参数一,发送的数据,字节数组* 参数二,发送的字节长度* 参数三,目的地的IP地址* 参数四,服务器的端口号*/DatagramPacket packet = new DatagramPacket(bytes, bytes.length, InetAddress.getLocalHost(), 8080);//3、让发送端对象发送数据包的数据socket.send(packet);}}
}
2、接收端可以反复接收数据
接收端实现步骤
package Demo06;import java.net.DatagramPacket;
import java.net.DatagramSocket;public class UDPServerDemo2 {public static void main(String[] args) throws Exception {
// 完成UDP通信 多发多收 服务端开发System.out.println("服务端启动了~");//1、创建接收端对象,注册端口DatagramSocket socket = new DatagramSocket(8080);//2、创建数据包对象,用于接收数据byte[] buf = new byte[1024 * 64];DatagramPacket packet = new DatagramPacket(buf, buf.length);while (true) {//3、让接收端对象开始接收数据socket.receive(packet);//等待式接受数据//4、获取接收的数据int len = packet.getLength(); //获取当前收到数据的长度String data = new String(buf, 0, len);System.out.println("服务器接受了:" + data);//5、获取对方的ip对象和程序端口String ip = packet.getAddress().getHostAddress();int port = packet.getPort();System.out.println("对方ip:" + ip + "对方端口:" + port);System.out.println("--------------------------------");}}
}
五、TCP通信
通信双方事先会采用“三次握手”方式建立可靠连接,实现端到端的通信;底层能保证数据成功传给服务端。
Java提供了一个java.net.Socket类来实现TCP通信。

构造器 | 说明 |
public Socket(string host , int port) | 根据指定的服务器ip、端口号请求与服务端建立连接,连接通过,就获得了客户端socket |
方法 | 说明 |
public Outputstream getoutputstream() | 获得字节输出流对象 |
public Inputstream getInputstream() | 获得字节输入流对象 |
5.1、TCP通信的实现一发一收
客户端开发
package Demo07;import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;public class ClientDemo01 {public static void main(String[] args) throws Exception {//实现tcp通信 一发一收 客户端开发//1、常见Socket管道对象Socket socket = new Socket("127.0.0.1", 9999);//2、从socket通信管道得到一个字节输出流OutputStream os = socket.getOutputStream();//3、特殊数据流DataOutputStream dos = new DataOutputStream(os);dos.writeInt(1);dos.writeUTF("hello world");//4、释放资源socket.close();}
}
服务端开发
服务端是通过java.net包下的ServerSocket类来实现的
ServerSocket
构造器 | 说明 |
public ServerSocket(int port) | 为服务端程序注册端口 |
方法 | 说明 |
public Socket accept() | 阻塞等待客户端的连接请求,一旦与某个客户端成功连接,则返回服务端这边的Socket对象。 |
package Demo07;import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;public class ServerDemo02 {public static void main(String[] args) throws Exception {//目标:实现TCP通信下一发一收:服务器开发System.out.println("服务器端口启动了...");//1、实现tcp通信 多发多收 服务器开发ServerSocket ss = new ServerSocket(9999);//2、调用accept方法,等待客户端连接Socket socket = ss.accept();//3、从socket中获取输入流,读取客户端数据InputStream is = socket.getInputStream();//4、把字节输入流包装成特殊的数据属入流DataInputStream dis = new DataInputStream(is);//5、读取数据int id = dis.readInt();String msg = dis.readUTF();System.out.println("id= " + id + ",收到的客户端消息msg= " + msg);//6、客户端的ip和端口(谁给我发的)System.out.println("客户端的ip:" + socket.getInetAddress().getHostAddress());System.out.println("客户端的端口:" + socket.getPort());}
}
5.2、TCP通信的实现多发多收
使用TCP通信实现:多发多收消息
package Demo08;import java.io.DataOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;public class ClientDemo01 {public static void main(String[] args) throws Exception {//实现tcp通信 多发多收 客户端开发//1、常见Socket管道对象Socket socket = new Socket("127.0.0.1", 9999);//2、从socket通信管道得到一个字节输出流OutputStream os = socket.getOutputStream();//3、特殊数据流DataOutputStream dos = new DataOutputStream(os);Scanner scanner = new Scanner(System.in);while (true) {System.out.println("请输入:");String msg = scanner.nextLine();if ("exit".equals(msg)) {System.out.println("退出成功!");dos.close();//4、释放资源socket.close();break;}dos.writeUTF(msg);dos.flush();}}
}
package Demo08;import java.io.DataInputStream;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;public class ServerDemo02 {public static void main(String[] args) throws Exception {//目标:实现TCP通信下 多发多收:服务器开发System.out.println("服务器端口启动了...");//1、实现tcp通信 多发多收 服务器开发ServerSocket ss = new ServerSocket(9999);//2、调用accept方法,等待客户端连接Socket socket = ss.accept();//3、从socket中获取输入流,读取客户端数据InputStream is = socket.getInputStream();//4、把字节输入流包装成特殊的数据属入流DataInputStream dis = new DataInputStream(is);//5、读取数据while (true) {String msg = dis.readUTF();System.out.println("收到的客户端msg:" + msg);//6、客户端的ip和端口(谁给我发的)System.out.println("客户端的ip:" + socket.getInetAddress().getHostAddress());System.out.println("客户端的端口:" + socket.getPort());System.out.println("----------------------------------------------------------------");}}
}
目前我们开发的服务端程序,是否可以支持同时与多个客户端通信 ?

package Demo06;import java.io.DataOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;public class ClientDemo01 {public static void main(String[] args) throws Exception {//实现tcp通信 多发多收 客户端开发//1、常见Socket管道对象Socket socket = new Socket("127.0.0.1", 9999);//2、从socket通信管道得到一个字节输出流OutputStream os = socket.getOutputStream();//3、特殊数据流DataOutputStream dos = new DataOutputStream(os);Scanner scanner = new Scanner(System.in);while (true) {System.out.println("请输入:");String msg = scanner.nextLine();if ("exit".equals(msg)) {System.out.println("退出成功!");dos.close();//4、释放资源socket.close();break;}dos.writeUTF(msg);dos.flush();}}
}
package Demo06;import java.io.DataInputStream;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;public class ServerDemo02 {public static void main(String[] args) throws Exception {//目标:实现TCP通信下 多发多收:服务器开发System.out.println("----------------------------------------------------------------");System.out.println("服务器端口启动了...");//1、实现tcp通信 多发多收 服务器开发ServerSocket ss = new ServerSocket(9999);while (true) {//2、调用accept方法,等待客户端连接Socket socket = ss.accept();System.out.println("一个客户端上线了~" + socket.getInetAddress().getHostAddress());//3、把这个客户端管道交给一个独立的子线程专门负责接收这个管道的消息new ServerReader(socket).start();}}
}
package Demo06;import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;public class ServerReader extends Thread {private Socket socket;public ServerReader(Socket socket) {this.socket = socket;}@Overridepublic void run() {try {//读取管道的消息//3、获取字节输入流InputStream is = socket.getInputStream();//4、把字节输入流包装成特殊的数据属入流DataInputStream dis = new DataInputStream(is);//5、读取数据while (true) {String msg = dis.readUTF();System.out.println("收到的客户端msg:" + msg);//6、客户端的ip和端口(谁给我发的)System.out.println("客户端的ip:" + socket.getInetAddress().getHostAddress());System.out.println("客户端的端口:" + socket.getPort());System.out.println("----------------------------------------------------------------");}} catch (Exception e) {e.printStackTrace();System.out.println("客户端断开连接~" + socket.getInetAddress().getHostAddress());}}
}
5.3、BS架构
BS架构的原理
注意:服务器必须给浏览器响应HTTP协议规定的数据格式,否则浏览器不识别返回的数据。


package Demo07;import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.*;public class ServerDemo02 {public static void main(String[] args) throws Exception {//目标:实现TCP通信下 多发多收:服务器开发System.out.println("----------------------------------------------------------------");System.out.println("服务器端口启动了...");//1、实现tcp通信 多发多收 服务器开发ServerSocket ss = new ServerSocket(8080);ExecutorService pool = new ThreadPoolExecutor(3,10,10,TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(100),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());while (true) {//2、调用accept方法,等待客户端连接Socket socket = ss.accept();System.out.println("一个客户端上线了~" + socket.getInetAddress().getHostAddress());//3、把这个客户端管道包装成一个任务给线程池处理pool.execute(new ServerReaderRunnable(socket));}}
}
package Demo07;import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;public class ServerReaderRunnable implements Runnable {private Socket socket;public ServerReaderRunnable(Socket socket) {this.socket = socket;}@Overridepublic 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("<!DOCTYPE html>\n" +"<html>\n" +"<head>\n" +" <meta charset=\"utf-8\">\n" +" <title>段落p标签</title>\n" +"</head>\n" +"<body>\n" +"<p>\n" +" 窗前明月光\n" +" <br>\n" +" 疑是地上霜\n" +" <br>\n" +" 举头望明月\n" +" <br>\n" +" 低头思故乡\n" +"</p>\n" +"</body>\n" +"</html>");ps.close();socket.close();} catch (Exception e) {e.printStackTrace();System.out.println("客户端断开连接~" + socket.getInetAddress());}}
}
构造器
构造器