网络编程
概述
需要回顾一些计算机网络知识
- 网页编程——javaweb——B/S
- 网络编程——TCP/IP——C/S
网络通信的要素
- 网络编程中有两个主要的问题
- 如何准确的定位到网络上的一台或者多台主机
- 找到主机之后如何进行通信
- 网络编程中的要素
- IP 和 端口号
- 网络通信协议
- 万物皆对象
IP地址表示
Java中网络编程相关资源在net包下,其中表示IP地址的类是InetAddress,具体见帮助文档,一些方法不建议硬记,用到的时候知道在哪里查即可。
//测试IP
public class Demo03 {
public static void main(String[] args) {
try {
//查询本机地址
InetAddress inetAddress1 = InetAddress.getByName("172.0.0.1");
System.out.println(inetAddress1);
InetAddress inetAddress2 = InetAddress.getByName("localhost");
System.out.println(inetAddress2);
InetAddress inetAddress3 = InetAddress.getLocalHost();
System.out.println(inetAddress3);
//查询网站ip地址
InetAddress inetAddress4 = InetAddress.getByName("www.baidu.com");
System.out.println(inetAddress4);
//常用方法
// System.out.println(inetAddress4.getAddress());
System.out.println(inetAddress4.getHostName());//域名,或者自己电脑名字
System.out.println(inetAddress4.getHostAddress());//ip
System.out.println(inetAddress4.getCanonicalHostName());//规范名字
} catch (UnknownHostException e) {
throw new RuntimeException(e);
}
}
}
/*
/172.0.0.1
localhost/127.0.0.1
-略-
www.baidu.com/39.156.66.14
[B@74a14482
www.baidu.com
39.156.66.14
39.156.66.14
*/
端口
端口表示计算机上的一个程序的进程
-
不同的进程有不同的端口号!用来区分软件!
-
被规定 0~65535
-
TCP,UDP:65535 * 2 tcp:80,udp:80吗,单个协议下,端口号不能冲突
-
端口分类
- 公有端口 0~1023
- HTTP:80
- HTTPS:443
- FTP:21
- Telent : 23
- 程序注册端口:1024~49151,分配用户或者程序
- Tomcat:8080
- MySQL:3306
- Oracle:1521
- 动态、私有:49152~65535
- 公有端口 0~1023
-
常用dos命令
netstat -ano #查看所有的端口 netstat -ano|findstr "5900" #查看指定的端口tasklist|findstr "8696" #查看指定端口的进程 Ctrl+ shift + EsC #任务管理器快捷键
-
Java关于端口的方法
public class Demo04 { public static void main(String[] args) { InetSocketAddress socketAddress1 = new InetSocketAddress("172.0.0.1", 8080); InetSocketAddress socketAddress2 = new InetSocketAddress("localhost", 8080); System.out.println(socketAddress1); System.out.println(socketAddress2); System.out.println(socketAddress1.getAddress()); System.out.println(socketAddress1.getHostName());//地址 System.out.println(socketAddress1.getPort());//端口号 }
}
### 通信协议
回顾计算机网络中协议是什么,有哪些方面的协议,分层思想,TCP/IP协议簇、和TCP和UDP的区别、TCP连接的建立与断开。可以看一眼石墨文档。
### TCP实现聊天
**客户端**
1. 连接服务器Socket
2. 发送消息
```java
//客户端
public class TcpClientDemo01 {
public static void main(String[] args) {
Socket socket = null;
OutputStream os = null;
try {
//1. 要知道服务器的地址,端口号
InetAddress serverIP = InetAddress.getByName("127.0.0.1");
int port = 9999;
//2. 创建一个socket连接
socket = new Socket(serverIP, port);
//3. 发送消息 IO流
os = socket.getOutputStream();
os.write("来学习网络编程".getBytes());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (os != null) {
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
服务器
-
建立服务的端口 ServerSocket
-
等待用户的链接 accept
-
接收用的消息
//服务端 public class TcpServerDemo01 { public static void main(String[] args) throws IOException { ServerSocket serverSocket = null; Socket socket = null; InputStream is = null; ByteArrayOutputStream baos = null; try { //1. 我要有一个地址 serverSocket = new ServerSocket(9999); while (true) { //2. 等待客户端连接过来 socket = serverSocket.accept(); //3. 读取客户端的消息 is = socket.getInputStream(); //管道流,相比传统的的方式,相当于多了一个过滤器,避免出现乱码 baos = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int count = 0; while ((count = is.read(buffer)) != -1) { baos.write(buffer, 0, count); } System.out.println(baos.toString()); } //传统方式 // byte[] buffer = new byte[1024]; // int count = 0; // while ((count = is.read(buffer)) != -1) { // String msg = new String(buffer, 0, count); // System.out.println(msg); // } } catch (Exception e) { e.printStackTrace(); } finally { //关闭资源,后打开的先关闭 if (baos != null) { baos.close(); } if (is != null) { is.close(); } if (socket != null) { socket.close(); } if (serverSocket != null) { serverSocket.close(); } } } }
TCP文件上传实现
-
客户端
public class TcpClient { public static void main(String[] args) throws Exception { //1.创建一个Socket连接 Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 9000); //2.创建一个输出流 OutputStream os = socket.getOutputStream(); //3.读取文件 FileInputStream fis = new FileInputStream(new File("1.png")); //4.写出文件 byte[] buffer = new byte[1024]; int count = 0; while ((count = fis.read(buffer)) != -1) { os.write(buffer, 0, count); } //通知服务器,我已经结束了 socket.shutdownOutput();//我已经传输完毕了 //确定服务器接收完毕,才能够断开连接,这里打印出来了 InputStream inputStream = socket.getInputStream(); //Stirng byte[] ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buffer2 = new byte[1024]; int count2; //getInputStream() 方法本身并不阻塞,但后续从该流中读取数据时(如使用 read() 方法),通常是阻塞式的 while ((count2 = inputStream.read(buffer2)) != -1) { baos.write(buffer2, 0, count2); } System.out.println(baos.toString()); //5.关闭资源 baos.close(); inputStream.close(); fis.close(); os.close(); socket.close(); } }
-
服务器
public class TcpServer { public static void main(String[] args) throws IOException { //1.创建服务 ServerSocket serverSocket = new ServerSocket(9000); //2.监听客户端的链接 Socket socket = serverSocket.accept();//阻塞式监听,会一直等待客户端连接 //3.获取输入流 InputStream is = socket.getInputStream(); //4.文件输出 FileOutputStream fos = new FileOutputStream(new File("receive.png")); byte[] buffer = new byte[1024]; int count = 0; while ((count = is.read(buffer)) != -1) { fos.write(buffer, 0, count); } //5.通知客户端已经接受完了 OutputStream os = socket.getOutputStream(); os.write("我接受完毕了,你可以断开了".getBytes()); //6.关闭资源 os.close(); fos.close(); is.close(); socket.close(); serverSocket.close(); } }
Tomcat
本质上还是信息传递
服务端
- 自定义 S
- Tomcat服务器 S:Java后台开发
客户端
- 自定义 C
- 浏览器 B
UDP消息发送
发短信:不用连接,需要知道对方的地址!
发送端,即便没有正确接收也不会报错,是一种不可靠的协议
public class UdpClient {
public static void main(String[] args) throws Exception {
//1.建立一个Socket
DatagramSocket socket = new DatagramSocket();
//2.建个包
String msg = "你好,对岸的人";
InetAddress localhost = InetAddress.getByName("localhost");
int port = 9090;
//数据,数据长度,发给谁
DatagramPacket packet = new DatagramPacket(msg.getBytes(), 0, msg.getBytes().length, localhost, port);
//3.发送包
socket.send(packet);
//4.关闭流
socket.close();
}
}
接收端
public class UdpServer {
public static void main(String[] args) throws Exception {
//开放端口
DatagramSocket socket = new DatagramSocket(9090);
//接收数据包
byte[] buffer = new byte[1024];
DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length);
socket.receive(packet);//阻塞接收
System.out.println(packet.getAddress().getHostAddress());
System.out.println(new String(packet.getData(), 0, packet.getLength()));
//关闭连接
socket.close();
}
}
Upd聊天实现
发送方
public class UdpUser1 {
public static void main(String[] args) throws Exception {
DatagramSocket socket = new DatagramSocket(8888);
//准备数据:控制台System.in
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
while (true) {
String data = reader.readLine();
byte[] datas = data.getBytes();
DatagramPacket packet = new DatagramPacket(datas, 0, datas.length, new InetSocketAddress("localhost", 6666));
socket.send(packet);
if (data.equals("bye")) {
break;
}
}
socket.close();
}
}
接收方
public class UdpUser2 {
public static void main(String[] args) throws Exception {
DatagramSocket socket = new DatagramSocket(6666);
while (true) {
//准备接收包裹
byte[] container = new byte[1024];
DatagramPacket packet = new DatagramPacket(container, 0, container.length);
socket.receive(packet);//阻塞式接收包裹
//断开连接 bye
byte[] data = packet.getData();
String receiveData = new String(data, 0, packet.getLength());
//这里写data.length是不对的,因为它来自container
// 同时packet.getData().length也不对:
//这个方法返回的是 DatagramPacket 内部使用的字节数组(也就是你在创建 DatagramPacket 时传入的 container 数组)。
//这个数组的长度是固定的,是你初始化时指定的(在你的代码中是 1024)
System.out.println(receiveData);
if (receiveData.equals("bye")) {
break;
}
}
socket.close();
}
}
UDP多线程在线咨询
-
发送线程
public class TalkSend implements Runnable{ DatagramSocket socket = null; BufferedReader reader = null; private int fromPort; private String toIp; private int toProt; public TalkSend(int fromPort, String toIp, int toProt) { this.fromPort = fromPort; this.toIp = toIp; this.toProt = toProt; try { socket = new DatagramSocket(fromPort); //准备数据:控制台System.in reader = new BufferedReader(new InputStreamReader(System.in)); } catch (SocketException e) { throw new RuntimeException(e); } } @Override public void run() { try { while (true) { String data = reader.readLine(); byte[] datas = data.getBytes(); DatagramPacket packet = new DatagramPacket(datas, 0, datas.length, new InetSocketAddress(this.toIp, this.toProt)); socket.send(packet); if (data.equals("bye")) { break; } } } catch (Exception e) { e.printStackTrace(); } socket.close(); } }
-
接收线程
public class TalkReceive implements Runnable{ DatagramSocket socket = null; private int formPort; private String msgFrom; public TalkReceive(int formPort, String msgFrom) { this.formPort = formPort; this.msgFrom = msgFrom; try { socket = new DatagramSocket(formPort); } catch (SocketException e) { throw new RuntimeException(e); } } @Override public void run() { try { while (true) { //准备接收包裹 byte[] container = new byte[1024]; DatagramPacket packet = new DatagramPacket(container, 0, container.length); socket.receive(packet);//阻塞式接收包裹 //断开连接 bye byte[] data = packet.getData(); String receiveData = new String(data, 0, packet.getLength()); System.out.println(msgFrom + ":" + receiveData); if (receiveData.equals("bye")) { break; } } } catch (Exception e) { e.printStackTrace(); } socket.close(); } }
-
对象类1
public class TalkStudent { public static void main(String[] args) { //开启两个线程 new Thread(new TalkSend(5555, "localhost", 7777)).start(); new Thread(new TalkReceive(6666, "老师")).start();//相当于备注了 } }
-
对象类2
public class TalkTeacher { public static void main(String[] args) { //开启两个线程 new Thread(new TalkSend(8888, "localhost", 6666)).start(); new Thread(new TalkReceive(7777, "学生")).start();//相当于备注了 } }
URL
https://www.baidu.com/
统一资源定位符:定位资源的,定位互联网上的某一个资源
DNS 域名解析 www.baidu.com xxx.x..x..x
协议://ip地址:端口/项目名/资源
常用方法
public class URLMothed {
public static void main(String[] args) throws MalformedURLException {
URL url = new URL("http://localhost:8080/helloworld/index.jsp?username=ario&password=123");
System.out.println(url.getProtocol());//协议
System.out.println(url.getHost());//主机ip
System.out.println(url.getPort());//端口
System.out.println(url.getPath());//文件
System.out.println(url.getFile());//全路径
System.out.println(url.getQuery());//参数
}
}
从网上下载资源
public class UrlDown {
public static void main(String[] args) throws Exception {
//下载地址
URL url = new URL("https://ws6.stream.qqmusic.qq.com/C400002PmWEJ4VJHtR.m4a?guid=2492379522&vkey=CC4138C4021DDEDE5D73D32A5C5821F1FA28CBFEEFAFCC7D29C9EB21B5E8665905D63F55E64210BEEA59E18BCDC1511C0F1EA476254B84AF__v21ea059de&uin=1152921505246736145&fromtag=120032&src=C4000042W3aI296cbd.m4a");
//连接这个资源 HTTP
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
InputStream inputStream = urlConnection.getInputStream();
FileOutputStream fos = new FileOutputStream("music.m4a");
byte[] buffer = new byte[1024];
int count = 0;
while ((count = inputStream.read(buffer)) != -1) {
fos.write(buffer, 0, count);//写出这个数据
}
fos.close();
inputStream.close();
urlConnection.disconnect();
}
}