一、概念:
1. InetAddress:封装计算机的ip地址 ,没有端口
//使用getLocalHost方法创建InetAddress对象
InetAddress addr = InetAddress.getLocalHost();
System.out.println(addr.getHostAddress()); //返回:192.168.1.110
System.out.println(addr.getHostName()); //输出计算机名
//根据域名得到InetAddress对象
addr = InetAddress.getByName("www.163.com");
System.out.println(addr.getHostAddress()); //返回 163服务器的ip:61.135.253.15
System.out.println(addr.getHostName()); //输出:www.163.com
//根据ip得到InetAddress对象
addr = InetAddress.getByName("61.135.253.15");
System.out.println(addr.getHostAddress()); //返回 163服务器的ip:61.135.253.15
System.out.println(addr.getHostName()); //输出ip而不是域名。如果这个IP地 址不存在或DNS服务器不允许进行IP地址和域名的映射,getHostName方法就直接返回这个IP地址。
2. 端口
(1)指令:
• 查看所有端口:netstat -ano
• 查看指定端口:netstat -aon|findstr "808"
• 查看指定进程:tasklist|findstr "808"
• 查看具体程序:使用任务管理器查看PID
(2)InetSocketAddress:包含端口,用于socket通信的
InetSocketAddress socketAddress = new InetSocketAddress("127.0.0.1",8080);
InetSocketAddress socketAddress2 = new InetSocketAddress("localhost",9000);
System.out.println(socketAddress.getHostName());
System.out.println(socketAddress2.getAddress());
3. URL:统一资源定位符
(1)由4部分组成: * 协议 * 存放资源的主机域名 * 端口号 * 资源文件名
URL u = new URL("http://www.baidu.com:80/index.html?uname=shsxt&age=18#a");
System.out.println("获取与此url关联的协议的默认端口: "+u.getDefaultPort());
System.out.println("getFile:"+u.getFile()); //端口号后面的内容
System.out.println("主机名:"+u.getHost()); //www.baidu.com
System.out.println("路径:"+u.getPath()); //端口号后,参数前的内容
System.out.println("端口:"+u.getPort()); //存在返回80.否则返回-1
System.out.println("协议:"+u.getProtocol());
System.out.println("参数部分:"+u.getQuery());
System.out.println("锚点:"+u.getRef());
URL u1 = new URL("http://www.abc.com/aa/");
URL u2 = new URL(u1,"2.html"); //相对路径构建url对象 "
System.out.println(u2.toString()); //http://www.abc.com/aa/2.html
(2)下载网络资源
//获取URL
URL url = new URL("https://www.jd.com");
//下载资源(当无法抓取到网络代码时)
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("User-Agent","Mozilla/5.0 (Windows NT 10.0; WOW64;" +
"rv:46.0) Gecko/20100101 Firefox/46.0");
InputStream is = url.openStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is,"UTF-8"));
String msg = null;
while(null != (msg =br.readLine())){
System.out.println(msg);
}
br.close();
}
(3)总结
4. 传输协议
(1)TCP(transfer control protocol ):一种面向连接(连接导向)的、可靠的、基于字节流的运输 层(Transport layer)通信协议
• 面向连接
• 点到点的通信
• 高可靠性
• 占用系统资源多、效率低
(2)UDP(User DatagramProtocol):一种无连接的传输层协议,提供面向事务的简单不可靠信息 传送服务
• 非面向连接,传输不可靠,可能丢失
• 发送不管对方是否准备好,接收方收到也不确认
• 可以广播发送
• 非常简单的协议,开销小
(3)套接字Socket
套接字就像是传输层为应用层开的一个小口,应用程序通过这个小口向远程发送数据,或者 接收远程发来的数据
5. UDP编程
(1)使用基于UDP协议的Socket网络编程实现,不需要利用IO流实现数据的传输,每个数据发送 单元被统一封装成数据包的方式,发送方将数据包发送到网络中,数据包在网络中去寻找他的目的地。
• DatagramSocket:用于发送或接收数据包的套接字
• DatagramPacket:数据包
(2)基本流程:
* 发送端
* 1. 使用DatagramSocket 指定端口创建发送端
* 2. 准备数据。一定转成字节数组
* 3. 封装成DatagramPackage包裹,需要指定目的地
* 4. 发送包裹send(datagramPackage p)*
* 5. 释放资源
* 接收端
* 1. 使用DatagramSocket 指定端口创建接收端
* 2. 准备容器 封装成 DatagramPackage 包裹
* 3. 阻塞式的接收包裹receive(DatagramPackage p)
* 4. 分析数据 * byte[] getData() * get Length()
* 5. 释放资源 *
* 报错 * java.net.BindException: Address already in use: Cannot bind
* 注意: 同一个地址下,端口不允许重复
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
/**基本流程: * 发送端
* 1. 使用DatagramSocket 指定端口创建发送端
* 2. 准备数据。一定转成字节数组
* 3. 封装成DatagramPackage包裹,需要指定目的地
* 4. 发送包裹send(datagramPackage p)*
* 5. 释放资源
* Created by DuHongWei on 2021/6/15 15:44
*/
public class UDPClient {
public static void main(String[] args) throws IOException {
System.out.println("发送方启动中...");
// 1. 使用DatagramSocket 指定端口创建发送端
DatagramSocket client = new DatagramSocket(8888);
// 2. 准备数据。一定转成字节数组
String data = "ddd";
byte[] datas = data.getBytes();
// 3. 封装成DatagramPackage包裹,需要指定目的地
DatagramPacket packet = new DatagramPacket(datas,0,
datas.length,new InetSocketAddress("localhost",9999)); //此处的9999要和接收方的端口保持一致,否则数据将丢失
// 4. 发送包裹send(datagramPackage p)*
client.send(packet);
// 5. 释放资源
client.close();
}
}
import java.net.DatagramPacket;
import java.net.DatagramSocket;
/**基本流程 * 接收端
* 1. 使用DatagramSocket 指定端口创建接收端
* 2. 准备容器 封装成 DatagramPackage 包裹
* 3. 阻塞式的接收包裹receive(DatagramPackage p)
* 4. 分析数据
* byte[] getData()
* get Length()
* 5. 释放资源
*
* 报错
* java.net.BindException: Address already in use: Cannot bind
* 注意: 同一个地址下,端口不允许重复
* Created by DuHongWei on 2021/6/15 15:43
*/
public class UDPServer {
public static void main(String[] args) throws Exception {
System.out.println("接收方启动中...");
// 1. 使用DatagramSocket 指定端口创建接收端
DatagramSocket server = new DatagramSocket(9999);
// 2. 准备容器 封装成 DatagramPackage 包裹
byte[] container=new byte[1024];
DatagramPacket packet = new DatagramPacket(container,0,container.length);
// 3. 阻塞式的接收包裹receive(DatagramPackage p)
server.receive(packet);
// 4. 分析数据 byte[] getData() getLength()返回实际收到的数据的字节数
byte[] datas = packet.getData();
int len = packet.getLength();
System.out.println(new String(datas,0,len));
// 5. 释放资源
server.close();
}
}
(3)发送接收基本类型(字符串,boolean,整型,Date等)
/**基本类型: * 发送端
* 1. 使用DatagramSocket 指定端口创建发送端
* 2. 准备数据。一定转成字节数组
* 3. 封装成DatagramPackage包裹,需要指定目的地
* 4. 发送包裹send(datagramPackage p)*
* 5. 释放资源
* Created by DuHongWei on 2021/6/15 15:44
*/
public class UDPTypeClient {
public static void main(String[] args) throws IOException {
System.out.println("发送方启动中...");
// 1. 使用DatagramSocket 指定端口创建发送端
DatagramSocket client = new DatagramSocket(8888);
// 2. 准备数据。一定转成字节数组
//写出
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(baos));
//操作数据类型 + 数据
dos.writeUTF("杜肚肚");
dos.writeInt(18);
dos.writeBoolean(false);
dos.writeChar('d');
dos.flush();
byte[] datas = baos.toByteArray();
// 3. 封装成DatagramPackage包裹,需要指定目的地
DatagramPacket packet = new DatagramPacket(datas,0,
datas.length,new InetSocketAddress("localhost",9999)); //此处的9999要和接收方的端口保持一致,否则数据将丢失
// 4. 发送包裹send(datagramPackage p)*
client.send(packet);
// 5. 释放资源
client.close();
}
}
/**基本类型 * 接收端
* 1. 使用DatagramSocket 指定端口创建接收端
* 2. 准备容器 封装成 DatagramPackage 包裹
* 3. 阻塞式的接收包裹receive(DatagramPackage p)
* 4. 分析数据 将字节数组还原为对应的类型
* byte[] getData()
* get Length()
* 5. 释放资源
*
* 报错
* java.net.BindException: Address already in use: Cannot bind
* 注意: 同一个地址下,端口不允许重复
* Created by DuHongWei on 2021/6/15 15:43
*/
public class UDPTypeServer {
public static void main(String[] args) throws Exception {
System.out.println("接收方启动中...");
// 1. 使用DatagramSocket 指定端口创建接收端
DatagramSocket server = new DatagramSocket(9999);
// 2. 准备容器 封装成 DatagramPackage 包裹
byte[] container=new byte[1024];
DatagramPacket packet = new DatagramPacket(container,0,container.length);
// 3. 阻塞式的接收包裹receive(DatagramPackage p)
server.receive(packet);
// 4. 分析数据 byte[] getData() getLength()返回实际收到的数据的字节数
byte[] datas = packet.getData();
int len = packet.getLength();
//读取
DataInputStream dis = new DataInputStream(new BufferedInputStream(new ByteArrayInputStream(datas)));
//顺序与写出顺序一致
String msg = dis.readUTF();
int age = dis.readInt();
Boolean flag = dis.readBoolean();
char ch = dis.readChar();
System.out.println(msg+"-->"+flag);
// 5. 释放资源
server.close();
}
}
(4)发送接收引用类型(对象)
/**引用类型: * 发送端
* 1. 使用DatagramSocket 指定端口创建发送端
* 2. 准备数据。一定转成字节数组
* 3. 封装成DatagramPackage包裹,需要指定目的地
* 4. 发送包裹send(datagramPackage p)*
* 5. 释放资源
* Created by DuHongWei on 2021/6/15 15:44
*/
public class UDPObjClient {
public static void main(String[] args) throws IOException {
System.out.println("发送方启动中...");
// 1. 使用DatagramSocket 指定端口创建发送端
DatagramSocket client = new DatagramSocket(8888);
// 2. 准备数据。一定转成字节数组 引用类型
//写出 ---> 对象的序列化
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(baos));
//操作数据类型 + 数据
oos.writeUTF("杜肚肚");
oos.writeInt(18);
oos.writeBoolean(false);
oos.writeChar('d');
//加入对象
oos.writeObject("嘟嘟嘟嘟嘟");
oos.writeObject(new Date());
Employee emp = new Employee("肚肚",1);
oos.writeObject(emp); //如果不实现Serializable方法,则emp不能够序列化,因为没有继承Serializable
oos.flush();
byte[] datas = baos.toByteArray();
// 3. 封装成DatagramPackage包裹,需要指定目的地
DatagramPacket packet = new DatagramPacket(datas,0,
datas.length,new InetSocketAddress("localhost",6666)); //此处的9999要和接收方的端口保持一致,否则数据将丢失
// 4. 发送包裹send(datagramPackage p)*
client.send(packet);
// 5. 释放资源
client.close();
}
}
/**引用类型 * 接收端
* 1. 使用DatagramSocket 指定端口创建接收端
* 2. 准备容器 封装成 DatagramPackage 包裹
* 3. 阻塞式的接收包裹receive(DatagramPackage p)
* 4. 分析数据 将字节数组还原为对应的类型
* byte[] getData()
* get Length()
* 5. 释放资源
*
* 报错
* java.net.BindException: Address already in use: Cannot bind
* 注意: 同一个地址下,端口不允许重复
* Created by DuHongWei on 2021/6/15 15:43
*/
public class UDPObjServer {
public static void main(String[] args) throws Exception {
System.out.println("接收方启动中...");
// 1. 使用DatagramSocket 指定端口创建接收端
DatagramSocket server = new DatagramSocket(6666);
// 2. 准备容器 封装成 DatagramPackage 包裹
byte[] container=new byte[1024];
DatagramPacket packet = new DatagramPacket(container,0,container.length);
// 3. 阻塞式的接收包裹receive(DatagramPackage p)
server.receive(packet);
// 4. 分析数据 byte[] getData() getLength()返回实际收到的数据的字节数
byte[] datas = packet.getData();
int len = packet.getLength();
//读取 ---> 反序列化
ObjectInputStream ois = new ObjectInputStream(new BufferedInputStream(new ByteArrayInputStream(datas)));
//顺序与写出顺序一致
String msg = ois.readUTF();
int age = ois.readInt();
Boolean flag = ois.readBoolean();
char ch = ois.readChar();
System.out.println(flag);
//对象的数据还原
Object str = ois.readObject();
Object date = ois.readObject();
Object employee = ois.readObject();
if(str instanceof String){ //避免类型转换错误
String strObj = (String)str;
System.out.println(strObj);
}
if(date instanceof Date){
Date dateObj = (Date)date;
System.out.println(dateObj);
}
if(employee instanceof Employee){
Employee employeeObj = (Employee) employee;
System.out.println(employeeObj.getName()+"--->"+employeeObj.getSalary());
}
// 5. 释放资源
server.close();
}
}
//javabean 封装数据
public class Employee implements java.io.Serializable{
private transient String name; //transient的作用是该数据不需要序列化,在打印时显示为null
private double salary;
public Employee() {
}
public Employee(String name, double salary) {
this.name = name;
this.salary = salary;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
}
(5)文件存储(图片)
/**
* 文件上传: 发送端
* 1、使用DatagramSocket 指定端口 创建发送端
* 2、将基本类型 转成字节数组
* 3、 封装成DatagramPacket 包裹,需要指定目的地
* 4、发送包裹send(DatagramPacket p) *
* 5、释放资源
* @author 裴新 QQ:3401997271
*
*/
public class UdpFileClient {
public static void main(String[] args) throws Exception {
System.out.println("发送方启动中.....");
// 1、使用DatagramSocket 指定端口 创建发送端
DatagramSocket client =new DatagramSocket(8888);
//2、准备数据 一定转成字节数组
byte[] datas = IOUtils.fileToByteArray("src/logo.png");
//byte[] datas = IOUtils.fileToByteArray("src/ndl.png"); //图片太大,传不过去
//3、 封装成DatagramPacket 包裹,需要指定目的地
DatagramPacket packet =new DatagramPacket(datas,0,datas.length,
new InetSocketAddress("localhost",6666));
//4、发送包裹send(DatagramPacket p) *
client.send(packet);
// 5、释放资源
client.close();
}
}
/**
* 文件存储: 接收端
* Address already in use: Cannot bind 同一个协议下端口不允许冲突
* 1、使用DatagramSocket 指定端口 创建接收端
* 2、准备容器 封装成DatagramPacket 包裹
* 3、阻塞式接收包裹receive(DatagramPacket p)
* 4、分析数据 将字节数组还原为对应的类型
* byte[] getData()
* getLength()
* 5、释放资源
* @author 裴新 QQ:3401997271
*
*/
public class UdpFileServer {
public static void main(String[] args) throws Exception {
System.out.println("接收方启动中.....");
// 1、使用DatagramSocket 指定端口 创建接收端
DatagramSocket server =new DatagramSocket(6666);
// 2、准备容器 封装成DatagramPacket 包裹
byte[] container =new byte[1024*60];
DatagramPacket packet = new DatagramPacket(container,0,container.length);
// 3、阻塞式接收包裹receive(DatagramPacket p)
server.receive(packet); //阻塞式
// 4、分析数据 将字节数组还原为对应的类型
// byte[] getData()
// getLength()
byte[] datas =packet.getData();
int len = packet.getLength();
IOUtils.byteArrayToFile(datas, "src/copy.png");
// 5、释放资源
server.close();
}
}
/**
*1、 图片读取到字节数组
*2、 字节数组写出到文件
* @author 裴新
*
*/
public class IOUtils {
/**
* 1、图片读取到字节数组
* 1)、图片到程序 FileInputStream
* 2)、程序到字节数组 ByteArrayOutputStream
*/
public static byte[] fileToByteArray(String filePath) {
//1、创建源与目的地
File src = new File(filePath);
byte[] dest =null;
//2、选择流
InputStream is =null;
ByteArrayOutputStream baos =null;
try {
is =new FileInputStream(src);
baos = new ByteArrayOutputStream();
//3、操作 (分段读取)
byte[] flush = new byte[1024*10]; //缓冲容器
int len = -1; //接收长度
while((len=is.read(flush))!=-1) {
baos.write(flush,0,len); //写出到字节数组中
}
baos.flush();
return baos.toByteArray();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
//4、释放资源
try {
if(null!=is) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
/**
* 2、字节数组写出到图片
* 1)、字节数组到程序 ByteArrayInputStream
* 2)、程序到文件 FileOutputStream
*/
public static void byteArrayToFile(byte[] src,String filePath) {
//1、创建源
File dest = new File(filePath);
//2、选择流
InputStream is =null;
OutputStream os =null;
try {
is =new ByteArrayInputStream(src);
os = new FileOutputStream(dest);
//3、操作 (分段读取)
byte[] flush = new byte[5]; //缓冲容器
int len = -1; //接收长度
while((len=is.read(flush))!=-1) {
os.write(flush,0,len); //写出到文件
}
os.flush();
} catch (IOException e) {
e.printStackTrace();
}finally {
//4、释放资源
try {
if (null != os) {
os.close();
}
} catch (Exception e) {
}
}
}
}
6. TCP编程
(1)详细 步骤( ( 通信原理) )
创建客户端
* 1、建立连接: 使用Socket创建客户端 +服务的地址和端口
* 2、操作: 输入输出流操作
* 3、释放资源
创建服务器
* 1、指定端口 使用ServerSocket创建服务器
* 2、阻塞式等待连接 accept
* 3、操作: 输入输出流操作
* 4、释放资源
• 单向:客户端向服务器端发送字符串,服务器获取字符串并输出
/**
* 熟悉流程
* 创建客户端
* 1、建立连接: 使用Socket创建客户端 +服务的地址和端口
* 2、操作: 输入输出流操作
* 3、释放资源
* Created by DuHongWei on 2021/6/15 20:44
*/
public class Client {
public static void main(String[] args) throws IOException {
System.out.println("-----Client-----");
//1、建立连接: 使用Socket创建客户端 +服务的地址和端口
Socket client =new Socket("localhost",8888);
//2、操作: 输入输出流操作
DataOutputStream dos =new DataOutputStream(client.getOutputStream());
String data ="hello";
dos.writeUTF(data);
dos.flush();
//3、释放资源
dos.close();
client.close();
}
}
/**
* 熟悉流程
* * 创建服务器
* * 1、指定端口 使用ServerSocket创建服务器
* * 2、阻塞式等待连接 accept
* * 3、操作: 输入输出流操作
* * 4、释放资源
* Created by DuHongWei on 2021/6/15 20:44
*/
public class Server {
public static void main(String[] args) throws IOException {
System.out.println("-----Server-----");
// 1、指定端口 使用ServerSocket创建服务器
ServerSocket server = new ServerSocket(8888);
// 2、阻塞式等待连接 accept
Socket client = server.accept();
System.out.println("一个客户端建立了连接");
// 3、操作: 输入输出流操作
DataInputStream dis = new DataInputStream(client.getInputStream());
String data = dis.readUTF();
System.out.println(data);
// 4、释放资源
dis.close();
client.close();
server.close();
}
}
• 双向:服务器端给出客户端反馈,客户端得到反馈并输出
/**
* 模拟登录 双向
* 创建客户端
* 1、建立连接: 使用Socket创建客户端 +服务的地址和端口
* 2、操作: 输入输出流操作
* 3、释放资源
* Created by DuHongWei on 2021/6/15 20:44
*/
public class LoginTwoWayClient {
public static void main(String[] args) throws IOException {
System.out.println("-----Client-----");
BufferedReader console =new BufferedReader(new InputStreamReader(System.in));
System.out.print("请输入用户名:");
String uname =console.readLine();
System.out.print("请输入密码:");
String upwd =console.readLine();
//1、建立连接: 使用Socket创建客户端 +服务的地址和端口
Socket client =new Socket("localhost",8888);
//2、操作: 输入输出流操作
DataOutputStream dos =new DataOutputStream(client.getOutputStream());
dos.writeUTF("uname="+uname+"&"+"upwd="+upwd);
dos.flush();
DataInputStream dis =new DataInputStream(client.getInputStream());
String result =dis.readUTF();
System.out.println(result);
//3、释放资源
dos.close();
client.close();
}
}
/**
* 模拟登录 双向
* * 创建服务器
* * 1、指定端口 使用ServerSocket创建服务器
* * 2、阻塞式等待连接 accept
* * 3、操作: 输入输出流操作
* * 4、释放资源
* Created by DuHongWei on 2021/6/15 20:44
*/
public class LoginTwoWayServer {
public static void main(String[] args) throws IOException {
System.out.println("-----Server-----");
// 1、指定端口 使用ServerSocket创建服务器
ServerSocket server =new ServerSocket(8888);
// 2、阻塞式等待连接 accept
Socket client =server.accept();
System.out.println("一个客户端建立了连接");
// 3、操作: 输入输出流操作
DataInputStream dis =new DataInputStream(client.getInputStream());
String datas =dis.readUTF();
String uname = "";
String upwd = "";
//分析
String[] dataArray = datas.split("&");
for(String info:dataArray) {
String[] userInfo =info.split("=");
if(userInfo[0].equals("uname")) {
System.out.println("你的用户名为:"+userInfo[1]);
uname = userInfo[1];
}else if(userInfo[0].equals("upwd")) {
System.out.println("你的密码为:"+userInfo[1]);
upwd = userInfo[1];
}
}
DataOutputStream dos =new DataOutputStream(client.getOutputStream());
if (uname.equals("ddd") && upwd.equals("111")){//成功
dos.writeUTF("登录成功,欢迎登录");
}else{
//失败
dos.writeUTF("用户名或者密码错误");
}
// 4、释放资源
dis.close();
client.close();
server.close();
}
}
• 文件:客户端向服务器端上传文件,服务器端获取文件并反馈结果
/**
* 上传文件
* Created by DuHongWei on 2021/6/15 20:44
*/
public class FileClient {
public static void main(String[] args) throws IOException {
System.out.println("-----Client-----");
//1、建立连接: 使用Socket创建客户端 +服务的地址和端口
Socket client =new Socket("localhost",8888);
//2、操作: 文件上传
InputStream is = new BufferedInputStream(new FileInputStream("src/ndl.png"));
OutputStream os = new BufferedOutputStream(client.getOutputStream());
byte[] flush = new byte[1024];
int len = -1;
while ((len =is.read(flush))!=-1){
os.write(flush,0,len);
}
os.flush();
//3、释放资源
os.close();
is.close();
client.close();
}
}
/**
* 存储文件
* Created by DuHongWei on 2021/6/15 20:44
*/
public class FileServer {
public static void main(String[] args) throws IOException {
System.out.println("-----Server-----");
// 1、指定端口 使用ServerSocket创建服务器
ServerSocket server =new ServerSocket(8888);
// 2、阻塞式等待连接 accept
Socket client =server.accept();
System.out.println("一个客户端建立了连接");
// 3、操作: 文件存储
InputStream is = new BufferedInputStream(client.getInputStream());
OutputStream os = new BufferedOutputStream(new FileOutputStream("src/tcp.png"));
byte[] flush = new byte[1024];
int len = -1;
while ((len =is.read(flush))!=-1){
os.write(flush,0,len);
}
os.flush();
//3、释放资源
os.close();
is.close();
server.close();
}
}
• 多线程:服务器接收多个客户端的请求,并给出反馈 每个客户请求开启一个线程
/**
* 模拟登录 双向
* 创建客户端
* 1、建立连接: 使用Socket创建客户端 +服务的地址和端口
* 2、操作: 输入输出流操作
* 3、释放资源
* Created by DuHongWei on 2021/6/15 20:44
*/
public class LoginMultiClient {
public static void main(String[] args) throws IOException {
System.out.println("-----Client-----");
BufferedReader console = new BufferedReader(new InputStreamReader(System.in));
//1、建立连接: 使用Socket创建客户端 +服务的地址和端口
Socket client = new Socket("localhost", 8888);
//2、操作: 输入输出流操作 先请求后响应
new Send(client).send();
new Receive(client).receive();
client.close();
}
static class Send {
private Socket client;
private DataOutputStream dos;
private BufferedReader console ;
private String msg;
public Send(Socket client) {
console=new BufferedReader(new InputStreamReader(System.in));
this.msg =init();
this.client = client;
try {
dos=new DataOutputStream(client.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
}
}
private String init() {
try {
System.out.print("请输入用户名:");
String uname =console.readLine();
System.out.print("请输入密码:");
String upwd =console.readLine();
return "uname="+uname+"&"+"upwd="+upwd;
} catch (IOException e) {
e.printStackTrace();
}
return "";
}
public void send() {
try {
dos.writeUTF(msg);
dos.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//接收
static class Receive{
private Socket client;
private DataInputStream dis;
public Receive(Socket client) {
this.client = client;
try {
dis=new DataInputStream(client.getInputStream());
} catch (IOException e) {
e.printStackTrace();
}
}
public void receive() {
String result;
try {
result = dis.readUTF();
System.out.println(result);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 模拟登录 双向
* * 创建服务器
* * 1、指定端口 使用ServerSocket创建服务器
* * 2、阻塞式等待连接 accept
* * 3、操作: 输入输出流操作
* * 4、释放资源
* Created by DuHongWei on 2021/6/15 20:44
*/
public class LoginMultiServer {
public static void main(String[] args) throws IOException {
System.out.println("-----Server-----");
// 1、指定端口 使用ServerSocket创建服务器
ServerSocket server = new ServerSocket(8888);
boolean isRunning = true;
// 2、阻塞式等待连接 accept
while (isRunning) {
Socket client = server.accept();
System.out.println("一个客户端建立了连接");
new Thread(new Channel(client)).start();
}
server.close();
}
//一个channel就代表一个客户端
static class Channel implements Runnable {
private Socket client;
//输入流
private DataInputStream dis;
//输出流
private DataOutputStream dos;
public Channel(Socket client) {
this.client = client;
try {
//输入
dis = new DataInputStream(client.getInputStream());
//输出
dos = new DataOutputStream(client.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
release();
}
}
//接收数据
private String receive() {
String datas ="";
try {
datas = dis.readUTF();
} catch (IOException e) {
e.printStackTrace();
}
return datas;
}
//发送数据
private void send(String msg) {
try {
dos.writeUTF(msg);
dos.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
//释放资源
private void release() {
// 4、释放资源
try {
if(null != dos) {
dos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(null != dis) {
dis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(null != client) {
client.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
// 3、操作: 输入输出流操作
String uname ="";
String upwd ="";
//分析
String[] dataArray = receive().split("&");
for(String info:dataArray) {
String[] userInfo =info.split("=");
if(userInfo[0].equals("uname")) {
System.out.println("你的用户名为:"+userInfo[1]);
uname = userInfo[1];
}else if(userInfo[0].equals("upwd")) {
System.out.println("你的密码为:"+userInfo[1]);
upwd = userInfo[1];
}
}
if(uname.equals("shsxt") && upwd.equals("laopei")) { //成功
send("登录成功,欢迎回来");
}else { //失败
send("用户名或密码错误");
}
release();
}
}
}
二、案例(简易模拟聊天室)
/**
* 在线聊天室: 客户端
* 目标: 加入容器实现群聊和私聊
* Created by DuHongWei on 2021/6/16 11:02
*
*/
public class Client {
public static void main(String[] args) throws UnknownHostException, IOException {
System.out.println("-----Client-----");
BufferedReader br =new BufferedReader(new InputStreamReader(System.in));
System.out.println("请输入用户名:");
String name =br.readLine();
//1、建立连接: 使用Socket创建客户端 +服务的地址和端口
Socket client =new Socket("localhost",8888);
//2、客户端发送消息
new Thread(new Send(client,name)).start();
new Thread(new Receive(client)).start();
}
}
/**
* 在线聊天室: 服务器
* 目标: 群聊,私聊
* Created by DuHongWei on 2021/6/16 11:02
*/
public class Chat {
private static CopyOnWriteArrayList<Channel> all =new CopyOnWriteArrayList<Channel>();
public static void main(String[] args) throws IOException {
System.out.println("-----Server-----");
// 1、指定端口 使用ServerSocket创建服务器
ServerSocket server =new ServerSocket(8888);
// 2、阻塞式等待连接 accept
while(true) {
Socket client =server.accept();
System.out.println("一个客户端建立了连接");
Channel c =new Channel(client);
all.add(c); //管理所有的成员
new Thread(c).start();
}
}
//一个客户代表一个Channel
static class Channel implements Runnable{
private DataInputStream dis;
private DataOutputStream dos;
private Socket client;
private boolean isRunning;
private String name;
public Channel(Socket client) {
this.client = client;
try {
dis = new DataInputStream(client.getInputStream());
dos =new DataOutputStream(client.getOutputStream());
isRunning =true;
//获取名称
this.name =receive();
//欢迎你的到来
this.send("欢迎你的到来");
sendOthers(this.name+"来了shsxt聊天室",true);
} catch (IOException e) {
System.out.println("---1------");
release();
}
}
//接收消息
private String receive() {
String msg ="";
try {
msg =dis.readUTF();
} catch (IOException e) {
System.out.println("---2------");
release();
}
return msg;
}
//发送消息
private void send(String msg) {
try {
dos.writeUTF(msg);
dos.flush();
} catch (IOException e) {
System.out.println("---3------");
release();
}
}
/**
* 群聊:获取自己的消息,发给其他人
* 私聊: 约定数据格式: @xxx:msg
* @param msg
*/
private void sendOthers(String msg,boolean isSys) {
boolean isPrivate = msg.startsWith("@");
if(isPrivate) { //私聊
int idx =msg.indexOf(":");
//获取目标和数据
String targetName = msg.substring(1,idx);
msg = msg.substring(idx+1);
for(Channel other: all) {
if(other.name.equals(targetName)) {//目标
other.send(this.name +"悄悄地对您说:"+msg);//私聊消息
break;
}
}
}else { //群聊
for(Channel other: all) {
if(other==this) { //自己
continue;
}
if(!isSys) {
other.send(this.name +"对所有人说:"+msg);//群聊消息
}else {
other.send(msg); //系统消息
}
}
}
}
//释放资源
private void release() {
this.isRunning = false;
Utils.close(dis,dos,client);
//退出
all.remove(this);
sendOthers(this.name+"离开大家庭...",true);
}
@Override
public void run() {
while(isRunning) {
String msg = receive() ;
if(!msg.equals("")) {
//send(msg);
sendOthers(msg,false);
}
}
}
}
}
/**
* 使用多线程封装:接收端
* 1、接收消息
* 2、释放资源
* 3、重写run
* Created by DuHongWei on 2021/6/16 11:02
*/
public class Receive implements Runnable {
private DataInputStream dis ;
private Socket client;
private boolean isRunning;
public Receive(Socket client) {
this.client = client;
this.isRunning = true;
try {
dis =new DataInputStream(client.getInputStream());
} catch (IOException e) {
System.out.println("====2=====");
release();
}
}
//接收消息
private String receive() {
String msg ="";
try {
msg =dis.readUTF();
} catch (IOException e) {
System.out.println("====4====");
release();
}
return msg;
}
@Override
public void run() {
while(isRunning) {
String msg =receive();
if(!msg.equals("")) {
System.out.println(msg);
}
}
}
//释放资源
private void release() {
this.isRunning = false;
Utils.close(dis,client);
}
}
/**
* 使用多线程封装:发送端
* 1、发送消息
* 2、从控制台获取消息
* 3、释放资源
* 4、重写run
* Created by DuHongWei on 2021/6/16 11:02
*
*/
public class Send implements Runnable {
private BufferedReader console ;
private DataOutputStream dos;
private Socket client;
private boolean isRunning;
private String name;
public Send(Socket client,String name) {
this.client =client;
console =new BufferedReader(new InputStreamReader(System.in));
this.isRunning = true;
this.name = name;
try {
dos =new DataOutputStream(client.getOutputStream());
//发送名称
send(name);
} catch (IOException e) {
System.out.println("==1==");
this.release();
}
}
@Override
public void run() {
while(isRunning) {
String msg = getStrFromConsole();
if(!msg.equals("")) {
send(msg);
}
}
}
//发送消息
private void send(String msg) {
try {
dos.writeUTF(msg);
dos.flush();
} catch (IOException e) {
System.out.println(e);
System.out.println("===3==");
release();
}
}
/**
* 从控制台获取消息
* @return
*/
private String getStrFromConsole() {
try {
return console.readLine();
} catch (IOException e) {
e.printStackTrace();
}
return "";
}
//释放资源
private void release() {
this.isRunning = false;
Utils.close(dos,client);
}
}
/**
* 工具类
* Created by DuHongWei on 2021/6/16 11:02
*/
public class Utils {
/**
* 释放资源
*/
public static void close(Closeable... targets ) {
for(Closeable target:targets) {
try {
if(null!=target) {
target.close();
}
}catch(Exception e) {
}
}
}
}