4. Java 网络编程课堂笔记

一、概念:

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) {
				
			}
		}
	}
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值