Java 网络编程 学习笔记

1.基本概念
  • 网络:将不同区域的计算级链接到一起     局域网  城域网  互联网
  • 地址:IP地址,确定网络上的一个绝对地址,位置。
  • 端口号:区分计算级软件的,2个字节,范围  0~65535,一共65536个
    • 在同一个协议下,端口号不能重复,不同的协议下,可以重复
    • 1024以下的端口不要使用

       端口冲突解决所用命令:

    • 查看所有端口: netstat –ano
    • 查看指定端口: netstat –ano|findstr "8080"
    • 查看指定进程: tasklist|findstr "2356"
    • 查看具体程序:使用任务管理器查看PID
  • 资源定位:URL:统一资源定位符(包含资源+定位)  URI:统一资源(仅资源) 
  • 数据的传输
    • 协议:TCP  和  UDP
      • TCP:电话,类似于三次握手,面向连接的安全可靠的效率相对低下的协议
      • UDP:短息,非面向连接,效率高
    • 传输
      • 先封装
      • 后解封


2.常用类操作  API
  • InetAddress        封装计算机的ip地址,没有端口
    • 成员方法
    • getHostAddress:返回地址
    • getHostName:返回计算机名
  • InetSocketAddress      包含端口,用于socket通信
    • 构造器
    • new InetSocketAddress(地址|域名,端口)
    • 方法
    • get Address():返回地址信息
    • getPort():返回端口号
    • getHostName():返回主机名
  • URL  定位资源   互联网三大基石之一(html  |  http)
    • image
    • 在www上,每一信息资源都有统一且唯一的地址,即统一资源定位符 Uniform  Resource  Locator。如:http://www.google.com:80/index.html。由四部分组成,分别为:
                          • 协议
                          • 存放资源的主机域名
                          • 端口号
                          • 资源文件名
  1 package www.wangxiaoyue.net;
  2 
  3 import java.net.MalformedURLException;
  4 import java.net.URL;
  5 
  6 /**
  7  * URL:统一资源定位器,互联网三大基石之一(html , http) ,作用是区分资源
  8  * 1、协议
  9  * 2、域名、计算机
 10  * 3、端口:默认80
 11  * 4、请求资源
 12  * http://www.baidu.com:80/index.html?uname=shsxt&age=18#a
 13  */
 14 public class URLTest01 {
 15     public static void main(String[] args) throws MalformedURLException {
 16         URL url = new URL("http://www.baidu.com:80/index.html?uname=shsxt&age=18#a");
 17         //获取四个值
 18         System.out.println("协议:" + url.getProtocol());
 19         System.out.println("域名 | ip:" + url.getHost());
 20         System.out.println("端口:" + url.getPort());
 21         System.out.println("请求资源1:" + url.getFile());
 22         System.out.println("请求资源2:" + url.getPath());
 23         //获取参数
 24         System.out.println("参数:" + url.getQuery());
 25         System.out.println("锚点:" + url.getRef());
 26     }
 27 }
 28 


3.传输协议   UDP 、TCP
  • UDP(User  DatagramProtocol)传输协议
    • 一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务
    • 特点
      • 非面向连接,传输不可靠,可能丢失
      • 发送不管对方是否准备好,接收方收到也不确认
      • 可以广播发送
      • 非常简单的协议,开销小
  • TCP(transfer  control  protocol)
    • 一种面向连接(连接导向)的、可靠的、基于字节流的运输层(Transport  layer)通信协议
    • 特点
      • 面向连接
      • 点到点的通信
      • 高可靠性
      • 占用系统资源多,效率低
  • 套接字Socket
    • 我们开发的网络应用程序位于应用层,TCP和UDP属于传输层协议。在应用层和传输层之间,则使用套接字进行分离


4.UDP编程

需求:完成在线咨询功能:

  • 学生和咨询师在线一对一交流

分析:

  • 使用基于UDP协议的Socket网络编程实现
  • 不需要利用IO流实现数据的传输
  • 每个数据发送单元被统一封装成数据包的方式,发送方将数据包发送到网络中,数据包在网络中去寻找他的目的地

UDP基本概念

  • DatagramSocket:用于发送或接收数据包的套接字
  • DatagramPacket:数据包


注意:同一协议下,端口号禁止重复,否则报端口冲突异常

发送端

  1 package com.wangxiaoyue.udp;
  2 
  3 import java.io.IOException;
  4 import java.net.DatagramPacket;
  5 import java.net.DatagramSocket;
  6 import java.net.InetSocketAddress;
  7 
  8 /**
  9  * 发送端
 10  * 1、使用DatagramSocket  指定端口 创建发送端
 11  * 2、准备数据,一定转成字节数组
 12  * 3、封装成DatagramPacket 包裹,需要指定目的地
 13  * 4、发送包裹 send(DatagramPacket p)
 14  * 5、释放资源
 15  */
 16 public class UdpClient {
 17 
 18     public static void main(String[] args) throws IOException {
 19         System.out.println("发送方启动中。。。");
 20         //1、使用DatagramSocket  指定端口 创建发送端
 21         DatagramSocket client = new DatagramSocket();
 22         //2.准备数据,一定转成字节数组
 23         String data = "这是一条没什么用但是能够测试程序的数据";
 24         byte[] datas = data.getBytes();
 25         //3.封装成DatagramPacket 包裹,需要指定目的地
 26         DatagramPacket datagramPacket = new DatagramPacket(datas, 0, datas.length, new InetSocketAddress("localhost", 9999));
 27         //4、发送包裹 send(DatagramPacket p)
 28         client.send(datagramPacket);
 29         //5、释放资源
 30         client.close();
 31     }
 32 }
 33 

接收端

  1 package com.wangxiaoyue.udp;
  2 
  3 import java.io.IOException;
  4 import java.net.DatagramPacket;
  5 import java.net.DatagramSocket;
  6 import java.net.SocketException;
  7 
  8 /**
  9  * 接收端
 10  * 1、使用DatagramSocket  指定端口 创建接收端
 11  * 2、准备容器,封装成DatagramPacket 包裹
 12  * 3、阻塞式接受包裹receive(DatagramPacket p)
 13  * 4、分析数据
 14  *      byte[]  getData()
 15  *              getLength()
 16  * 5、释放资源
 17  */
 18 public class UdpServer {
 19     public static void main(String[] args) throws IOException {
 20         System.out.println("接收端启动中。。。");
 21         //1、使用DatagramSocket  指定端口 创建接收端
 22         DatagramSocket server = new DatagramSocket(9999);
 23         //2、准备容器,封装成DatagramPacket 包裹
 24         byte[] container = new byte[1024];
 25         DatagramPacket datagramPacket = new DatagramPacket(container,0,container.length);
 26         //3、阻塞式接受包裹receive(DatagramPacket p)
 27         server.receive(datagramPacket);
 28         //4、分析数据   byte[]  getData()   getLength()
 29         byte[] datas = datagramPacket.getData();
 30         int length = datagramPacket.getLength();
 31         //5、释放资源
 32         server.close();
 33         System.out.println("数据:   "+new String(datas,0,length)+"   长度:   "+length);
 34     }
 35 }
 36 

重点需要关注字节数组  byte[] datas.

操作基本类型,需要使用DataInputStream   DataOutputStream

操作对象类型,需要使用ObjectInputStream   ObjectOutputStream

操作文件类型,需要使用FileInputStream   FileOutputStream

可以看出的是,以上使用的都是字节流,因为我们传输的数据是存放在字节数组中的。同时,可以加上BufferedOutputStream和BufferedInoputStream 缓冲流,可以显著提高效率。不过工作中一般使用Commons IO 这个工具类。


使用面向对象思想,对接收端和发送端进行封装

  1 package com.wangxiaoyue.udp;
  2 
  3 import java.io.BufferedReader;
  4 import java.io.IOException;
  5 import java.io.InputStreamReader;
  6 import java.net.DatagramPacket;
  7 import java.net.DatagramSocket;
  8 import java.net.InetSocketAddress;
  9 import java.net.SocketException;
 10 
 11 /**
 12  * 发送端
 13  */
 14 public class TalkSend implements Runnable {
 15 
 16     //1、使用DatagramSocket  指定端口 创建发送端
 17     private DatagramSocket client;
 18     private String toIp;
 19     private int toPort;
 20 
 21     TalkSend(int port, String toIp, int toPort) {
 22         this.toPort = toPort;
 23         this.toIp = toIp;
 24         try {
 25             client = new DatagramSocket(port);
 26         } catch (SocketException e) {
 27             e.printStackTrace();
 28         }
 29     }
 30 
 31     @Override
 32     public void run() {
 33         while (true) {
 34             //2.准备数据,一定转成字节数组
 35             BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
 36             String data = null;
 37             try {
 38                 data = reader.readLine();
 39             } catch (IOException e) {
 40                 e.printStackTrace();
 41             }
 42             byte[] datas = data.getBytes();
 43             //3.封装成DatagramPacket 包裹,需要指定目的地
 44             DatagramPacket datagramPacket = new DatagramPacket(datas, 0, datas.length, new InetSocketAddress(this.toIp, this.toPort));
 45             //4、发送包裹 send(DatagramPacket p)
 46             try {
 47                 client.send(datagramPacket);
 48             } catch (IOException e) {
 49                 e.printStackTrace();
 50             }
 51             if ("bye".equals(data)) {
 52                 break;
 53             }
 54         }
 55         //5、释放资源
 56         client.close();
 57     }
 58 }
 59 
  1 package com.wangxiaoyue.udp;
  2 
  3 import java.io.IOException;
  4 import java.net.DatagramPacket;
  5 import java.net.DatagramSocket;
  6 import java.net.SocketException;
  7 
  8 /**
  9  * 接收端:使用面向对象封装
 10  */
 11 public class TalkReceive implements Runnable {
 12     private DatagramSocket server;
 13     private String from;
 14     TalkReceive(int port,String from) {
 15         this.from = from;
 16         try {
 17             this.server = new DatagramSocket(port);
 18         } catch (SocketException e) {
 19             e.printStackTrace();
 20         }
 21     }
 22 
 23     @Override
 24     public void run() {
 25         while (true) {
 26             //2、准备容器,封装成DatagramPacket 包裹
 27             byte[] container = new byte[1024];
 28             DatagramPacket datagramPacket = new DatagramPacket(container,0,container.length);
 29             //3、阻塞式接受包裹receive(DatagramPacket p)
 30             try {
 31                 server.receive(datagramPacket);
 32             } catch (IOException e) {
 33                 e.printStackTrace();
 34             }
 35             //4、分析数据   byte[]  getData()   getLength()
 36             byte[] datas = datagramPacket.getData();
 37             int length = datagramPacket.getLength();
 38             String data = new String(datas, 0, length);
 39             System.out.println(this.from+":"+data);
 40             if ("bye".equals(data)) {
 41                 break;
 42             }
 43         }
 44         //5、释放资源
 45         server.close();
 46     }
 47 
 48 }
 49 

模拟学生和老师两个客户端,启用多线程进行通讯

  1 package com.wangxiaoyue.udp;
  2 
  3 /**
  4  * 学生端
  5  */
  6 public class TalkStudent {
  7     public static void main(String[] args) {
  8         new Thread(new TalkReceive(7777,"教师")).start();
  9         new Thread(new TalkSend(5555,"localhost",8888)).start();
 10     }
 11 }
 12 
  1 package com.wangxiaoyue.udp;
  2 
  3 /**
  4  * 教师端
  5  */
  6 public class TalkTeacher {
  7     public static void main(String[] args) {
  8         new Thread(new TalkReceive(8888,"学生")).start();
  9         new Thread(new TalkSend(6666,"localhost",7777)).start();
 10     }
 11 }
 12 

注意发送和接收消息的端口一定要相同:

     即:1.首先每个端是两个线程,也就是启动两个服务。两个端一共就是四个端口。由于我们启动在一台机器上,故这四个服务的启动端口首先不能有重复。

          2.发送端需要指定的除了发送服务启动在哪个端口上,还需要接收方的IP地址+端口号。此时,发送方的端口号就是接收端启动的端口号。

5.TCP编程

需求:完成网络登录功能:

  • 用户输入用户名密码,服务器给出登录成功或失败的提示

分析

  • 使用基于CP协议的Socket网络编程实现
  • TCP协议基于请求-相应模式
  • 在网络通讯中,第一次主动发起通讯的程序被称作客户端(Client)程序
  • 第一次通讯中等待连接的程序被称作服务器端(Server)程序
  • 利用IO流实现数据的传输

详细步骤(通讯原理)

  • 服务端创建ServerSocket,在指定端口监听并处理请求
  • 客户端创建Socket,向服务端发送请求

1570635270(1)

网络登录功能分解

  • 单向:客户端向服务器发送字符串,服务器获取字符串并输出
  • 双向:服务器给出客户端反馈,客户端得到反馈并输出
  • 文件:客户端向服务器上传文件,服务器端获取文件并反馈结果
  • 多线程:服务器接收多个客户端的请求,并给出反馈,每个客户请求开启一个线程
基础功能实现(基础流程代码)
  1 package com.wangxiaoyue.tcp;
  2 
  3 import java.io.DataOutputStream;
  4 import java.io.IOException;
  5 import java.net.Socket;
  6 
  7 /**
  8  * 流程
  9  * 创建客户端
 10  * 1、使用Socket创建客户端,指定服务的IP和端口号进行连接
 11  * 2、操作:输入输出流操作
 12  * 3、释放资源
 13  */
 14 public class Client {
 15     public static void main(String[] args) throws IOException {
 16         System.out.println("客户端启动中。。。");
 17         //1、使用Socket创建客户端,指定服务的IP和端口号进行连接
 18         Socket client = new Socket("localhost", 8888);
 19         //2、操作:输入输出流操作
 20         DataOutputStream dos = new DataOutputStream(client.getOutputStream());
 21         String data = "hello";
 22         dos.writeUTF(data);
 23         //3、释放资源
 24         dos.close();
 25         client.close();
 26     }
 27 }
 28 
  1 package com.wangxiaoyue.tcp;
  2 
  3 import java.io.DataInputStream;
  4 import java.io.IOException;
  5 import java.net.ServerSocket;
  6 import java.net.Socket;
  7 
  8 /**
  9  * 流程
 10  * 创建服务器
 11  * 1、指定端口  使用ServerSocket创建服务器
 12  * 2、阻塞式等待连接accept
 13  * 3、操作:输入输出流操作
 14  * 4、释放资源
 15  */
 16 public class Server {
 17     public static void main(String[] args) throws IOException {
 18         System.out.println("服务端启动中。。。");
 19         //1、指定端口  使用ServerSocket创建服务器
 20         ServerSocket server = new ServerSocket(8888);
 21         //2、阻塞式等待连接accept
 22         Socket client = server.accept();
 23         System.out.println("一个客户端建立了连接");
 24         //3、操作:输入输出流操作
 25         DataInputStream dis = new DataInputStream(client.getInputStream());
 26         String data = dis.readUTF();
 27         System.out.println(data);
 28         //4、释放资源
 29         dis.close();
 30         client.close();
 31     }
 32 }
 33 


支持多个客户端连接同一服务器

使用面向对象的思想对代码进行封装

客户端 (封装初始化、发送数据、接收数据、关闭资源)

  1 package com.wangxiaoyue.tcp;
  2 
  3 import com.sun.org.apache.bcel.internal.generic.InstructionConstants;
  4 
  5 import javax.sound.midi.Soundbank;
  6 import java.io.*;
  7 import java.net.Socket;
  8 
  9 /**
 10  * 创建客户端  使用面向对象思想封装
 11  * 1、使用Socket创建客户端,指定服务的IP和端口号进行连接
 12  * 2、操作:输入输出流操作
 13  * 3、释放资源
 14  */
 15 public class LoginMultiClient {
 16     public static void main(String[] args) throws IOException {
 17         System.out.println("客户端启动中。。。");
 18         String msg = Client.init("localhost", 8888);
 19         //2、操作:输入输出流操作
 20         Client.send(msg);
 21         String result = Client.read();
 22         System.out.println(result);
 23         Client.close();
 24         //3、释放资源
 25 
 26     }
 27 
 28     private static class Client {
 29         private static Socket client;
 30         private static DataOutputStream dos;
 31         private static DataInputStream dis;
 32         private static BufferedReader br;
 33 
 34         private static void send(String msg) {
 35             try {
 36                 dos = new DataOutputStream(client.getOutputStream());
 37                 dos.writeUTF(msg);
 38                 dos.flush();
 39             } catch (IOException e) {
 40                 e.printStackTrace();
 41             }
 42         }
 43 
 44         private static String read() {
 45             try {
 46                 dis = new DataInputStream(client.getInputStream());
 47                 String msg = dis.readUTF();
 48                 return msg;
 49             } catch (IOException e) {
 50                 e.printStackTrace();
 51                 return null;
 52             }
 53         }
 54 
 55         private static String init(String IP,int port) {
 56             try {
 57                 br = new BufferedReader(new InputStreamReader(System.in));
 58                 //1、使用Socket创建客户端,指定服务的IP和端口号进行连接
 59                 client = new Socket(IP, port);
 60                 System.out.println("请输入用户名:");
 61                 String uname = br.readLine();
 62                 System.out.println("请输入密码:");
 63                 String upwd = br.readLine();
 64                 return ("uname=" + uname + "&upwd=" + upwd);
 65             } catch (IOException e) {
 66                 e.printStackTrace();
 67                 return null;
 68             }
 69         }
 70         private static void close() {
 71             try {
 72                 br.close();
 73                 dis.close();
 74                 dos.close();
 75             } catch (IOException e) {
 76                 e.printStackTrace();
 77             }
 78         }
 79     }
 80 
 81 
 82 
 83 }
 84 

服务器端 (封装客户端连接后的一些操作,使之可以开启多线程同时响应多个客户端)

  1 package com.wangxiaoyue.tcp;
  2 
  3 import java.io.*;
  4 import java.net.Socket;
  5 
  6 /**
  7  * 创建客户端  使用面向对象思想封装
  8  * 1、使用Socket创建客户端,指定服务的IP和端口号进行连接
  9  * 2、操作:输入输出流操作
 10  * 3、释放资源
 11  */
 12 public class LoginMultiClient {
 13     public static void main(String[] args) throws IOException {
 14         System.out.println("客户端启动中。。。");
 15         Clientdo client = new Clientdo();
 16         String msg = client.init("localhost", 8888);
 17         //2、操作:输入输出流操作
 18         client.send(msg);
 19         String result = client.read();
 20         System.out.println(result);
 21         client.close();
 22         //3、释放资源
 23 
 24     }
 25 
 26     private static class Clientdo {
 27         private Socket client;
 28         private DataOutputStream dos;
 29         private DataInputStream dis;
 30         private BufferedReader br;
 31 
 32         private void send(String msg) {
 33             try {
 34                 dos = new DataOutputStream(client.getOutputStream());
 35                 dos.writeUTF(msg);
 36                 dos.flush();
 37             } catch (IOException e) {
 38                 e.printStackTrace();
 39             }
 40         }
 41 
 42         private String read() {
 43             try {
 44                 dis = new DataInputStream(client.getInputStream());
 45                 String msg = dis.readUTF();
 46                 return msg;
 47             } catch (IOException e) {
 48                 e.printStackTrace();
 49                 return null;
 50             }
 51         }
 52 
 53         private String init(String IP,int port) {
 54             try {
 55                 br = new BufferedReader(new InputStreamReader(System.in));
 56                 //1、使用Socket创建客户端,指定服务的IP和端口号进行连接
 57                 client = new Socket(IP, port);
 58                 System.out.println("请输入用户名:");
 59                 String uname = br.readLine();
 60                 System.out.println("请输入密码:");
 61                 String upwd = br.readLine();
 62                 return ("uname=" + uname + "&upwd=" + upwd);
 63             } catch (IOException e) {
 64                 e.printStackTrace();
 65                 return null;
 66             }
 67         }
 68         private void close() {
 69             try {
 70                 br.close();
 71                 dis.close();
 72                 dos.close();
 73             } catch (IOException e) {
 74                 e.printStackTrace();
 75             }
 76         }
 77     }
 78 }
 79 
posted @ 2019-10-07 22:51  用代码打败魔法  阅读(192)  评论(0编辑  收藏  举报