🏡个人主页 :@ 守夜人st
🚀系列专栏:Java
…持续更新中敬请关注…
🙉博主简介:软件工程专业,在校学生,写博客是为了总结回顾一些所学知识点
目录
- 网络编程
- 实现网络编程的三要素:
- IP地址
- IP地址的分类:
- IP地址形式:
- IP常用命令
- 特殊IP地址:
- IP地址操作类-InetAddress
- 端口
- 协议
- UDP快速入门
- TCP快速入门
- TCP通信引入多线程
- TCP通信——线程池优化
- TCP通信实战案例——即时通信
- TCP通信实战案例——模拟B/S系统
网络编程
什么是网络编程?
- 网络编程可以让程序员与网络上的其他设备中的程序进行数据交互。
网络通信基本模式- 常见的通信模式有如下两种形式:Client-Server(CS)、Browser/Server(BS)
实现网络编程的三要素:
IP地址:设备在网络中的地址,是唯一的标识
端口:应用程序在设备中的唯一标识
协议:数据在网络中传输的规则,常见的协议有UDP协议和TCP协议。
IP地址
IP地址的分类:
IPv4:32位(4字节),采用点分十进制表示法(192.168.1.1)
IPv6:128位(16字节),采用冒分十六进制表示法(ABCD:EF01:2345:6789:ABCD:EF01:2345:6789)
IP地址形式:
公网地址和私有地址(局域网使用)。
192.168开头的就是常见的局域网地址,范围为192.168.0.0——192.168.255.255,专门为组织机构内部使用
IP常用命令
- Ipconfig:查看本机IP地址
- ping IP地址:检查网络是否连通
特殊IP地址:
- 本机IP:127.0.0.1或者localhost:称为回送地址也可称为本地回环地址,只会寻找当前所在本机
IP地址操作类-InetAddress
InetAddress表示Internet协议(IP)地址
名称 | 说明 |
---|---|
public static InetAddress getLocalHost() | 返回本主机的地址对象 |
public static InetAddress getByName(String host) | 得到指定主机的IP地址对象,参数是域名或者IP地址 |
public String getHostName() | 获取此IP地址的主机名 |
public String getHostAddress() | 返回IP地址字符串 |
public boolean isReachable(int timeout) | 在指定毫秒内连通该IP地址对应的主机,连通后返回ture |
package com.shouyeren.net_app;
import java.net.InetAddress;
public class InetAddressDemo01 {
public static void main(String[] args) throws Exception {
//获取本机IP地址对象
InetAddress ip = InetAddress.getLocalHost();
//得到指定主机的IP地址对象,参数是域名或者IP地址
System.out.println(ip.getHostName());
//获取此IP地址的主机名
System.out.println(ip.getHostAddress());
//获取域名的IP对象
InetAddress ip1 = InetAddress.getByName("www.baidu.com");
System.out.println(ip1.getHostName());
System.out.println(ip1.getHostAddress());
//获取公网IP对象
InetAddress ip2 = InetAddress.getByName("112.80.248.76");
System.out.println(ip2.getHostName());
System.out.println(ip2.getHostAddress());
//判断是否连通
System.out.println(ip1.isReachable(5000));
}
}
端口
端口号:标识正在计算机设备上运行的进程(程序),被规定为一个16位的二进制,范围是0——65535
端口类型
- 周知端口:0——1023,被预先定义的知名应用占用(如:HTTP占用80,FTP占用21)
- 注册端口:1024——49151,分配给用户进程或某些应用程序(如Tomcat占用8080,MySQL占用3306)
- 动态端口:49152——65535,之所以称为动态端口,是因为他一般不固定分配某种进程而是动态分配
- 注意:我们自己开发的程序选择注册端口,且一个设备中不能出现两个程序的端口号一样,否则会出错。
协议
- 连接和通信数据的规则被称为网络通信协议
网络通信协议有两套参考模型
- OSI参考模型:世界互联网协议标准,全球通信规范,由于此模型过于理想化,未能在英特网上进行广泛推行
- TCP/IP参考模型:事实上的国际标准
传输的两个常见协议
- TCP(Transmission Control Protocol):传输控制协议
- UDP(User Datagram Protocol):用户数据报协议
TCP协议的特点
- 使用TCP协议,必须双方先建立连接,它是一种面向连接的可靠通信协议
- 传输前,采用”三次握手“方式建立连接,所以是可靠的。
- 在连接中可进行大量数据量的传输。
- 连接、发送数据都需要确认,且传输完毕后,还需释放已建立的连接,通信效率较低
TCP协议通信场景
- 对信息安全要求较高的场景,例如:文件下载、金融等数据通信
UDP协议:
- UDP是一种无连接、不可靠的传输协议
- 将数据源IP、目的地IP和端口封装成数据包,不需要建立连接
- 每个数据包的大小限制在64KB内
- 发送不管对方是否准备好,接收方收到也不进行确认,故是不可靠的
- 可以广播发送,发送数据结束时无需释放资源,开销小,速度快
UDP快速入门
DatagramPacket:数据包对象
构造器 说明 public DatagramPacket(byte[] buf,int length,InetAddress address,int port) 创建发送端数据包对象 buf :要发送的内容,字节数组 ;length :发送内容的字节长度;address:接收端的IP地址对象;port:接收端的端口号 public DatagramPacket(byte[] buf,int length) 创建接受端的数据包对象
public class ClientDemo {
public static void main(String[] args) throws Exception {
DatagramSocket socket = new DatagramSocket();
System.out.println("===============客户端启动===============");
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("请说:");
String s = sc.nextLine();
if ("exit".equals(s)){
System.out.println("下线成功");
socket.close();
break;
}
byte[] buf = s.getBytes();
DatagramPacket packet = new DatagramPacket(buf, buf.length, InetAddress.getLocalHost(),8888);
socket.send(packet);
}
}
}
public class ServerDemo {
public static void main(String[] args) throws Exception {
System.out.println("==============服务端启动===============");
DatagramSocket socket = new DatagramSocket(8888);
byte[] buf = new byte[1024*64];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
while (true) {
socket.receive(packet);
int len = packet.getLength();
String rs = new String(buf,0,len);
System.out.println("收到了来自" + packet.getAddress() +" " + packet.getPort() + "的信息:" + rs);
}
}
}
TCP快速入门
TCP是一种面向连接,安全可靠的传输协议
传输前采用三次握手方式,点对点通信,是可靠的
在连接中可以进行大数据量的传输
package com.shouyeren.net_app.tcp;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 开发socket网络编程入门案例的服务端
*/
public class ServerDemo {
public static void main(String[] args) {
try {
System.out.println("===========服务端启动=============");
//1.注册端口
ServerSocket serverSocket = new ServerSocket(7777);
//必须使用accept方法,等待接收客户端的socket连接请求
Socket socket = serverSocket.accept();
//从socket通信管道获得一个字节输入流
InputStream in = socket.getInputStream();
//把自己输入流包装成缓冲字符输入流
BufferedReader buf = new BufferedReader(new InputStreamReader(in));
//按照行读取消息
String msg;
while ((msg = buf.readLine()) != null){
System.out.println(socket.getRemoteSocketAddress() + ":" + msg);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
package com.shouyeren.net_app.tcp;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;
/**
* 完成socket网络编程入门案例的客户端的开发
*/
public class ClientDemo {
public static void main(String[] args) {
try {
//1. 创建客户端的Socket对象,请求与服务端连接
Socket socket = new Socket("127.0.0.1",7777);
//2. 使用socket对象调用getOutputStream()方法得到字节输出流
OutputStream os = socket.getOutputStream();
//3. 把低级的字节输出流包装成打印流
PrintStream printStream = new PrintStream(os);
Scanner sc = new Scanner(System.in);
while (true) {
//4. 发送消息
System.out.println("请说:");
String msg = sc.nextLine();
printStream.println(msg);
printStream.flush();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
TCP通信引入多线程
需求一个服务端能够同时接收多个客户端发送的消息
package com.shouyeren.net_app.tcp;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 开发socket网络编程入门案例的服务端
*/
public class ServerDemo {
public static void main(String[] args) {
try {
System.out.println("===========服务端启动=============");
//1.注册端口
ServerSocket serverSocket = new ServerSocket(7777);
while (true) {
//必须使用accept方法,等待接收客户端的socket连接请求
Socket socket = serverSocket.accept();
System.out.println(socket.getRemoteSocketAddress() + "连接成功!!!");
//把新连接的Socket交给有个独立的线程处理并启动线程
new ServerReaderThread(socket).start();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
package com.shouyeren.net_app.tcp;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;
public class ServerReaderThread extends Thread{
private Socket socket;
public ServerReaderThread(Socket socket){
this.socket = socket;
}
@Override
public void run() {
try {
//从socket通信管道获得一个字节输入流
InputStream in = socket.getInputStream();
//把自己输入流包装成缓冲字符输入流
BufferedReader buf = new BufferedReader(new InputStreamReader(in));
//按照行读取消息
String msg;
while ((msg = buf.readLine()) != null){
System.out.println(socket.getRemoteSocketAddress() + ":" + msg);
}
} catch (Exception e) {
System.out.println(socket.getRemoteSocketAddress() + "下线了!!!");
}
}
}
TCP通信——线程池优化
完成socket通信,使用线程池完成优化
package com.shouyeren.net_app.socket_threadpool;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.*;
/**
* 开发socket网络编程入门案例的服务端
*/
public class ServerDemo {
//使用静态变量记住一个线程池对象
private static ExecutorService pool = new ThreadPoolExecutor(3,5,
6, TimeUnit.SECONDS,new ArrayBlockingQueue<>(2),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
public static void main(String[] args) {
try {
System.out.println("===========服务端启动=============");
//1.注册端口
ServerSocket serverSocket = new ServerSocket(7777);
while (true) {
//必须使用accept方法,等待接收客户端的socket连接请求
Socket socket = serverSocket.accept();
System.out.println(socket.getRemoteSocketAddress() + "连接成功!!!");
pool.execute(new ServerReaderRunnable(socket));
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
package com.shouyeren.net_app.socket_threadpool;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;
public class ServerReaderRunnable implements Runnable{
private Socket socket;
public ServerReaderRunnable(Socket socket){
this.socket = socket;
}
@Override
public void run() {
try {
//从socket通信管道获得一个字节输入流
InputStream in = socket.getInputStream();
//把自己输入流包装成缓冲字符输入流
BufferedReader buf = new BufferedReader(new InputStreamReader(in));
//按照行读取消息
String msg;
while ((msg = buf.readLine()) != null){
System.out.println(socket.getRemoteSocketAddress() + ":" + msg);
}
} catch (Exception e) {
System.out.println(socket.getRemoteSocketAddress() + "下线了!!!");
}
}
}
线程池优化的优势在哪里
- 服务端可以复用线程处理多个客户端,可以避免系统瘫痪
- 适合客户端通信时长较短的场景
TCP通信实战案例——即时通信
package com.shouyeren.net_app.tcp_sms;
import java.io.*;
import java.net.Socket;
import java.util.Scanner;
/**
* 完成socket网络编程入门案例的客户端的开发
*/
public class ClientDemo {
public static void main(String[] args) {
try {
//1. 创建客户端的Socket对象,请求与服务端连接
Socket socket = new Socket("127.0.0.1",8888);
new ClientReaderThread(socket).start();
//2. 使用socket对象调用getOutputStream()方法得到字节输出流
OutputStream os = socket.getOutputStream();
//3. 把低级的字节输出流包装成打印流
PrintStream printStream = new PrintStream(os);
Scanner sc = new Scanner(System.in);
while (true) {
//4. 发送消息
System.out.println("请说:");
String msg = sc.nextLine();
printStream.println(msg);
printStream.flush();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
class ClientReaderThread extends Thread{
private Socket socket;
public ClientReaderThread(Socket socket){
this.socket = socket;
}
@Override
public void run() {
try {
//从socket通信管道获得一个字节输入流
InputStream in = socket.getInputStream();
//把自己输入流包装成缓冲字符输入流
BufferedReader buf = new BufferedReader(new InputStreamReader(in));
//按照行读取消息
String msg;
while ((msg = buf.readLine()) != null) {
System.out.println("收到消息 : " + msg);
}
} catch (Exception e) {
System.out.println("服务端把你踢出去了!!!");
}
}
}
package com.shouyeren.net_app.tcp_sms;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
/**
* 开发socket网络编程入门案例的服务端
*/
public class ServerDemo {
//定义一个静态的List集合处在当前在线的Socket
public static List<Socket> allOnlineSocket = new ArrayList<>();
public static void main(String[] args) {
try {
System.out.println("===========服务端启动=============");
//1.注册端口
ServerSocket serverSocket = new ServerSocket(8888);
while (true) {
//必须使用accept方法,等待接收客户端的socket连接请求
Socket socket = serverSocket.accept();
System.out.println(socket.getRemoteSocketAddress() + "连接成功!!!");
allOnlineSocket.add(socket);
//把新连接的Socket交给有个独立的线程处理并启动线程
new ServerReaderThread(socket).start();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
class ServerReaderThread extends Thread{
private Socket socket;
public ServerReaderThread(Socket socket){
this.socket = socket;
}
@Override
public void run() {
try {
//从socket通信管道获得一个字节输入流
InputStream in = socket.getInputStream();
//把自己输入流包装成缓冲字符输入流
BufferedReader buf = new BufferedReader(new InputStreamReader(in));
//按照行读取消息
String msg;
while ((msg = buf.readLine()) != null) {
System.out.println(socket.getRemoteSocketAddress()+" : " + msg);
//把收到的消息转发给所有在线的客户端
sendMsgToAll(msg);
}
} catch (Exception e) {
System.out.println(socket.getRemoteSocketAddress() + "已下线!!!");
ServerDemo.allOnlineSocket.remove(socket);
}
}
private void sendMsgToAll(String msg) throws Exception {
for (Socket socket : ServerDemo.allOnlineSocket) {
PrintStream printStream = new PrintStream(socket.getOutputStream());
printStream.println(msg);
printStream.flush();
}
}
}
TCP通信实战案例——模拟B/S系统
服务器必须给浏览器响应HTTP协议格式的数据,否则浏览器不识别。
package com.shouyeren.net_app.BS;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerDemo {
public static void main(String[] args) {
try {
//1.注册端口
ServerSocket serverSocket = new ServerSocket(8888);
while (true) {
//必须使用accept方法,等待接收客户端的socket连接请求
Socket socket = serverSocket.accept();
System.out.println(socket.getRemoteSocketAddress() + "连接成功!!!");
//把新连接的Socket交给有个独立的线程处理并启动线程
new ServerReaderThread(socket).start();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
class ServerReaderThread extends Thread{
private Socket socket;
public ServerReaderThread(Socket socket){
this.socket = socket;
}
@Override
public void run() {
try {
PrintStream printStream = new PrintStream(socket.getOutputStream());
//协议类型和版本 响应成功消息
printStream.println("HTTP/1.1 200 OK");
//响应的数据类型 文本/网页
printStream.println("Content-Type:text/html;charset=UTF-8");
//这里需要一个空行
printStream.println();
//响应数据正文
printStream.println("<span style='color:red;font-size:90px'> 响应数据!!! </span>");
printStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}