java学习笔记-网络编程
1.概念
一、网络
- 将不同区域的计算机连接到一起 局域网、城域网、互联网等等
二、地址
- IP地址 确定网络上 一个绝对地址|位置——>房子的地址
三、端口号
- 区分计算机软件的 ——房子的房门 2个字节 0-65535 共65536个
- 在同一个协议下,端口号不能重复 不同协议下可以重复
- 1024以下的不要使用 80-->http 21-->ftp
四、资源定位
- URL 统一资源定位(URL含URI:统一资源)
五、数据的传输
- 协议:TCP和UDP协议
- TCP(transfer control protocal):电话 类似于三次握手 面向连接 安全可靠 效率低下
- UDP(User Datagream protocal):短信 非面向连接 效率高
- 传输
- 先封装
- 后拆封
相关类
-
InetAddress(主机名+IP) InetSocketAddress(主机名+IP+端口)
-
URL
-
TCP:ServerSocket Socket
-
UDP:DatagramSocket DatagramPacket
2.地址 及端口
1.InetAddress:封装 IP 及 DNS
方法:
getHostAddress() 返回字符串形式的IP地址
getHostName() 返回域名|如果是本机则为计算机名
InetAddress.getLocalHost();返回本机主机名和ip地址
InetAddress.getByName("ip地址|域名");通过提供的ip地址或者域名返回IP地址对象
案例演示
package ip;
import java.net.InetAddress;
import java.net.UnknownHostException;
/*
这个类没有封装端口
*/
public class InetDemo01 {
public static void main(String[] args) throws UnknownHostException {
//使用getLocalHost方法创建InetAddress对象
InetAddress addr=InetAddress.getLocalHost();//返回本机ip地址
System.out.println(addr.getHostName());//输出计算机名
System.out.println(addr.getHostAddress());
//根据域名得到InetAddress对象
addr=InetAddress.getByName("www.163.com");//输出域名
System.out.println(addr.getHostAddress());//输出ip地址:36.156.46.9
addr=InetAddress.getByName("36.156.46.9");
//此处getHostName()输出ip而不是域名。
//如果这个IP地 址不存在或DNS服务器不允许进行IP地址和域名的映射,
//getHostName方法就直接返回这个IP地址。
System.out.println(addr.getHostName());
System.out.println(addr.getHostAddress());
}
}
运行结果
DESKTOP-A8O68AD
192.168.0.110
36.156.46.22
36.156.46.9
36.156.46.9
2.InetSocketAddress:封装端口
1.创建对象:
InetSocketAddress(String hostName,int port);
InetSocketAddress(InetAddress addr,int port);
前面的构造器调用了后面的构造器
2.方法
getAddress():获取ip地址
getHostName():获取域名|ip地址
getPort():获取端口号
案例演示
package ip;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
/*
封装端口:在InetAddress基础上+端口
*/
public class InetSocketDemo01 {
public static void main(String[] args) throws UnknownHostException {
InetSocketAddress address=new InetSocketAddress("127.0.0.1",9999);
//address=new InetSocketAddress(InetAddress.getByName("127.0.0.1"),9999);
System.out.println(address.getHostName());
System.out.println(address.getPort());
InetAddress addr=address.getAddress();
System.out.println(addr.getHostName());
System.out.println(addr.getHostAddress());
}
}
运行结果
127.0.0.1
9999
127.0.0.1
127.0.0.1
3.URL
概念和组成
URI(Uniform resource identifier)统一资源标识符,作为一个资源的唯一标识
URL(uniform resource Locator)统一资源定位器,它是一种具体的URI。
统一资源定位符由4部分组成:协议、存放资源的主机域名、端口 资源文件名(/)
URL:
一、创建
URL(String spec):绝对路径构建
URL(URL context,String spec):相对路径构建
二、方法
URL url=new URL("http://www.baidu.com:80/index.html#aa?uname=litianyijie");
System.out.println("协议:"+url.getProtocol());
System.out.println("域名:"+url.getHost());
System.out.println("端口:"+url.getPort());
System.out.println("资源:"+url.getFile());
System.out.println("相对路径的资源:"+url.getPath());
System.out.println("锚点:"+url.getRef());
//getQuery()返回?号之后的参数,存在锚点返回null,不存在锚点返回参数
System.out.println("参数:"+url.getQuery());
案例演示
package url;
import java.net.MalformedURLException;
import java.net.URL;
public class URLDemo01 {
//MalformedURLException:URL构建畸形异常,非正确构建
public static void main(String[] args) throws MalformedURLException {
/*
* 协议http
* 域名www.baidu.com
* 端口80
* 网页编程index.html
* 锚点:#aa:对本网页或本网站的资源的跳转
* 参数:?:跟用户进行交互
*/
//绝对路径构建
URL url=new URL("http://www.baidu.com:80/index.html#aa?uname=litianyijie");
System.out.println("协议:"+url.getProtocol());
System.out.println("域名:"+url.getHost());
System.out.println("端口:"+url.getPort());
System.out.println("资源:"+url.getFile());
System.out.println("相对路径的资源:"+url.getPath());
System.out.println("锚点:"+url.getRef());
//getQuery()返回?号之后的参数,存在锚点返回null,不存在锚点返回参数
System.out.println("参数:"+url.getQuery());
url=new URL("http://www.baidu.com:80/a");
//相对路径构建时,忽略第一个参数最后一个反斜杠后面的字符,
//如果第二个String参数带反斜杠则直接忽略第一个参数端口号后的所有字符
url=new URL(url,"c.txt");
System.out.println(url);//
}
}
三、流
openStream()
案例演示--查看源代码的方式
package url;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.URL;
/*
* 获取资源(即源代码)
*/
public class URLDemo02 {
public static void main(String[] args) throws IOException {
URL url=new URL("http://www.baidu.com");//主页 默认资源
// //获得资源 网络流
// InputStream is=url.openStream();
// byte[]flush=new byte[1024];
// int len=0;
// while((len=is.read(flush))!=-1) {
// System.out.println(new String(flush,0,len));
// }
// is.close();
BufferedReader br=new BufferedReader
(new InputStreamReader(url.openStream(),"utf-8"));
BufferedWriter bw=new BufferedWriter
(new OutputStreamWriter(new FileOutputStream("baidu.html"),"utf-8"));
String msg=null;
while((msg=br.readLine())!=null) {
// System.out.println(msg);
bw.append(msg);
bw.newLine();
}
bw.flush();
bw.close();
br.close();
}
}
UDP通信
UDP:以数据为中心 非面向连接 不安全 数据可能丢失 效率高
一、类 DatagramSocket DatagramPacket
1.客户端
- 创建客户端 DatagramSocket 类 + 指定端口
- 准备数据 字节数组
- 打包 DatagramPacket + 服务器地址 及 端口
- 发送
- 释放资源
2.服务器端
- 创建服务端 DatagramSocket 类 + 指定端口
- 准备接收容器(字节数组) 封装 DatagramPacket
- 包 接受数据
- 分析
- 释放资源
案例演示
先启动服务端
package udp;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
/*
服务端
1、创建服务端+端口
2、准别接受容器
3、封装成 包
4、接收数据
5、分析数据
6、释放资源
*/
public class MyServer {
public static void main(String[] args) throws IOException {
//1、创建服务端+端口
DatagramSocket server=new DatagramSocket(8888);
//2、准别接受容器
byte[]container=new byte[1024];
//3、封装成包DatagramPacket(byte[] buf,int length)
DatagramPacket packet=new DatagramPacket(container,container.length);
//4、接收数据
server.receive(packet);
//5、分析数据
byte[]data=packet.getData();
int len=packet.getLength();
System.out.println(new String(data,0,len));
//6、释放资源
server.close();
}
}
客户端
package udp;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;
/*
客户端
1. 创建客户端 DatagramSocket 类 + 指定端口
2. 准备数据 字节数组
3. 打包 DatagramPacket + 服务器地址 及 端口
4. 发送
5. 释放资源
*/
public class MyClient {
/*
* UDP编程,客户端创建时并未指定要发送目的地的端口,而是由数据包来指定端口,即使不启动服务器也不会报错
*/
public static void main(String[] args) throws IOException {
// 1. 创建客户端 DatagramSocket 类 + 指定端口
DatagramSocket client=new DatagramSocket(6666);
// 2. 准备数据 字节数组
String msg="udp编程";
byte[]data=msg.getBytes();
// 3. 打包 DatagramPacket + 服务器地址 及 端口
DatagramPacket packet=new DatagramPacket(data,data.length,new InetSocketAddress("localhost",8888));
// 4. 发送
client.send(packet);
// 5. 释放资源
client.close();
}
}
案例演示2
服务器端
package udp;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
/*
服务端
1、创建服务端+端口
2、准别接受容器
3、封装成 包
4、接收数据
5、分析数据 字节数组-->double
6、释放资源
*/
public class Server {
public static void main(String[] args) throws IOException {
//1、创建服务端+端口
DatagramSocket server=new DatagramSocket(8888);
//2、准别接受容器
byte[]container=new byte[1024];
//3、封装成包DatagramPacket(byte[] buf,int length)
DatagramPacket packet=new DatagramPacket(container,container.length);
//4、接收数据
server.receive(packet);
//5、分析数据
double data=convert(packet.getData());
int len=packet.getLength();
System.out.println(data);
//6、释放资源
server.close();
}
/*
* 字节数组+Data输入流
*/
public static double convert(byte[] data) throws IOException {
DataInputStream dis=new DataInputStream(new ByteArrayInputStream(data));
double num=dis.readDouble();
return num;
}
}
客户端:
package udp;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
/*
客户端
1. 创建客户端 DatagramSocket 类 + 指定端口
2. 准备数据 字节数组 double-->字节数组 字节数组输出流
3. 打包 DatagramPacket + 服务器地址 及 端口
4. 发送
5. 释放资源
思考:double类型:89.12 数据+类型
*/
public class Client {
/*
* UDP编程,客户端创建时并未指定要发送目的地的端口,而是由数据包来指定端口,即使不启动服务器也不会报错
*/
public static void main(String[] args) throws IOException {
// 1. 创建客户端+ 端口
DatagramSocket client=new DatagramSocket(6666);
// 2. 准备数据 字节数组
double num=89.12;
byte[]data=convert(num);
// 3. 打包 DatagramPacket + 服务器地址 及 端口
DatagramPacket packet=new DatagramPacket(data,data.length,new InetSocketAddress("localhost",8888));
// 4. 发送
client.send(packet);
// 5. 释放资源
client.close();
}
/*
* 字节数组 数据源 + Data 输出流
*/
public static byte[] convert(double num) throws IOException {
byte[]data=null;
ByteArrayOutputStream bos=new ByteArrayOutputStream();
DataOutputStream dos=new DataOutputStream(bos);
dos.writeDouble(num);
dos.flush();
//获取数据
data=bos.toByteArray();
dos.close();
return data;
}
}
TCP_Socket通信
基于TCP:面向连接 安全 可靠 效率低,类似于打电话
一、面向连接:请求-响应 Request--Response
二、Socket编程
1、服务器:ServerSocket
2、客户端:Socket
案例演示
服务器端
package TCP;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
/*
* 必须先启动服务器后才能连接
1.创建服务器 指定端口 ServerSocket(int port)
2.接收客户端的连接
3.发送数据和接受数据
*/
public class Server {
public static void main(String[] args) throws IOException {
//1.创建服务器 指定端口 ServerSocket(int port)
ServerSocket server=new ServerSocket(8888);
//2.接收客户端的连接 阻塞式
Socket socket=server.accept();
System.out.println("一个客户端建立连接");//没有接收到连接时,mian线程阻塞不会抵达此处
//3.发送数据
String msg="桓因使用";
//输出流
/*使用BufferedW/R 时需要注意阻塞的情况
BufferedWriter bw=new BufferedWriter(
new OutputStreamWriter(
socket.getOutputStream()
)
);
bw.write(msg);
bw.newLine();//如果不添加换行符,按照BufferedReader流读取时产生阻塞
bw.flush();
*/
DataOutputStream dos=new DataOutputStream(socket.getOutputStream());
dos.writeUTF(msg);
dos.flush();
}
}
客户端
package TCP;
import java.io.DataInputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
/*
1.创建客户端 必须指定服务器+端口 此时就在连接
Socket(String host,int port)
2.接收数据+发送数据
*/
public class Client {
public static void main(String[] args) throws UnknownHostException, IOException {
//1.创建客户端 必须指定服务器+端口 此时就在连接
Socket client=new Socket("localhost",8888);
//2.接受数据
/*使用BufferdW/R 时需要注意阻塞的情况
BufferedReader br=new BufferedReader(
new InputStreamReader(
client.getInputStream()
)
);
String echo=br.readLine();//阻塞式方法
System.out.println(echo);
*/
DataInputStream dis=new DataInputStream(client.getInputStream());
String echo=dis.readUTF();
System.out.println(echo);
}
}
模拟一个聊天室
关闭流的工具类
package net;
import java.io.Closeable;
import java.io.IOException;
/*
* 工具类,用于封装常用的关闭流命令并提前处理异常,提高代码简洁性和效率
*/
public class CloseUtil {
public static void closeAll(Closeable... io) {
for(Closeable temp:io) {
if(temp!=null) {
try {
temp.close();
} catch (IOException e) {
}
}
}
}
}
线程实体类--客户端发送数据
package net;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
/*
* 客户端发送数据
*/
public class Send implements Runnable{
private DataOutputStream dos;//输出流
private BufferedReader console;//控制台输入流
private boolean isRunning=true;//线程控制
public Send(Socket client,String name) {
try {
//初始化线程
dos=new DataOutputStream(client.getOutputStream());
console=new BufferedReader(new InputStreamReader(System.in));
//为服务器中对应线程提供姓名标识
send(name);
} catch (IOException e) {
CloseUtil.closeAll(dos,console);
isRunning=false;
}
}
private void send(String msg) {
if(msg==null || msg.equals(""))return;
try {
dos.writeUTF(msg);
dos.flush();
} catch (IOException e) {
CloseUtil.closeAll(dos,console);
isRunning=false;
}
}
private String getInfo() {
try {
return console.readLine();
} catch (IOException e) {
}
return "";
}
@Override
public void run() {
while(isRunning) {
send(getInfo());
}
}
}
线程实体类--客户端接收并输出数据
package net;
import java.io.DataInputStream;
import java.io.IOException;
import java.net.Socket;
/*
* 客户端接收数据并输出
*/
public class Receive implements Runnable{
private DataInputStream dis;
private boolean isRunning=true;
public Receive(Socket client) {
try {
dis=new DataInputStream(client.getInputStream());
} catch (IOException e) {
isRunning=false;
CloseUtil.closeAll(dis);
}
}
private void receive() {
try {
String msg=dis.readUTF();
System.out.println(msg);
} catch (IOException e) {
isRunning=false;
CloseUtil.closeAll(dis);
}
}
@Override
public void run() {
while(isRunning) {
receive();
}
}
}
客户端
package net;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
public class Client {
public String name;//客户端名
public static void main(String[] args) throws IOException {
System.out.println("请输入您的ID:");
BufferedReader console=new BufferedReader(new InputStreamReader(System.in));
String name=console.readLine();//键入客户端名
if(name==null||name.equals(""))
return;
/*
* 发送数据和接收数据的线程相互独立
* 发送数据属于主动触发,在想要发送的时候键入信息即可
* 接收数据属于被动触发,只要其他客户端有新的信息发送就会通过服务器反馈到该线程
*/
Socket client=new Socket("localhost",9999);//创建客户端
new Thread(new Send(client,name)).start();//启动客户端发送数据的线程
new Thread(new Receive(client)).start();//启动客户端接收并显示/输出数据的线程
}
}
服务器端
package com.bjsxt.net.tcp.chat.demo04;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
/**
* 创建服务器
* 写出数据:输出流
* 读取数据:输入流
* @author Administrator
*
*/
public class Server {
private List<MyChannel> all = new ArrayList<MyChannel>();
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
new Server().start();
}
public void start() throws IOException{
ServerSocket server =new ServerSocket(9999);
while(true){
Socket client =server.accept();
MyChannel channel = new MyChannel(client);
all.add(channel);//统一管理
new Thread(channel).start(); //一条道路
}
}
/**
* 一个客户端 一条道路
* 1、输入流
* 2、输出流
* 3、接收数据
* 4、发送数据
* @author Administrator
*
*/
private class MyChannel implements Runnable{
private DataInputStream dis ;
private DataOutputStream dos ;
private boolean isRunning =true;
private String name;
public MyChannel(Socket client ) {
try {
dis = new DataInputStream(client.getInputStream());
dos = new DataOutputStream(client.getOutputStream());
this.name =dis.readUTF();//服务器每次启动线程都从客户端初始化过程中获取提交的姓名标识获取
this.send("欢迎您进入聊天室");//向当前线程客户端点反馈信息
sendOthers(this.name+"进入了聊天室",true);//向其它线程同步反馈姓名标识
} catch (IOException e) {
//e.printStackTrace();
CloseUtil.closeAll(dis,dos);
isRunning =false;
}
}
/**
* 读取数据
* @return
*/
private String receive(){
String msg ="";
try {
msg=dis.readUTF();
} catch (IOException e) {
//e.printStackTrace();
CloseUtil.closeAll(dis);
isRunning =false;
all.remove(this); //移除自身
}
return msg;
}
/**
* 发送数据
*/
private void send(String msg){
if(null==msg ||msg.equals("")){
return ;
}
try {
dos.writeUTF(msg);
dos.flush();
} catch (IOException e) {
//e.printStackTrace();
CloseUtil.closeAll(dos);
isRunning =false;
all.remove(this); //移除自身
}
}
/**
* 发送给其他客户端
* -私聊则仅供两条线程之间的交互
* -公共频道
* --系统信息
* --聊天信息
*/
private void sendOthers(String msg,boolean sys){
//是否为私聊 约定
if(msg.startsWith("@")&& msg.indexOf(":")>-1 ){ //私聊
//获取name
String name =msg.substring(1,msg.indexOf(":"));
String content = msg.substring(msg.indexOf(":")+1);
for(MyChannel other:all){
if(other.name.equals(name)){
other.send(this.name+"对您悄悄地说:"+content);
}
}
}else{
//遍历容器
for(MyChannel other:all){
if(other ==this){
continue;
}
if(sys){ //系统信息
other.send("系统信息:"+msg);
}else{
//发送其他客户端
other.send(this.name+"对所有人说:"+msg);
}
}
}
}
@Override
public void run() {
while(isRunning){
sendOthers(receive(),false);
}
}
}