网络编程 TCP UDP

网络编程

网络通信

小结:1、网络编程中的两个主要问题:如何准确定位到网络上的一台或多台主机(ping www.baidu.comwww.baidu.com域名的意义就在于好记,IP地址不好记) 会返回一个IP,这个就是一个主机的地址);找到主机后如何进行通信(通过通信协议 TCP UDP)。

2、网络编程中的要素:IP和端口号,通过这两个能够确定是网络中的哪台主机的哪个应用。网络通信协议(TCP,UDP)。

3、万物皆对象,学习IP的类,端口的类,TCP UDP的类。

IP地址

package internet;

import java.net.InetAddress;    //表示IP地址
import java.net.UnknownHostException;

public class TestInetAddress {
   public static void main(String[] args) {
       try {
           InetAddress byName = InetAddress.getByName("127.0.0.1");  //InetAddress里面没有属性和构造器,只能调用静态方法
           System.out.println(byName);    //查询本机地址
           InetAddress byName1 = InetAddress.getByName("localhost");
           System.out.println(byName1);
           InetAddress localHost = InetAddress.getLocalHost();   //三种方法查询本机地址
           System.out.println(localHost);

           InetAddress byName2 = InetAddress.getByName("www.baidu.com");  //查询网站IP地址
           System.out.println(byName2);
      } catch (UnknownHostException e) {
           e.printStackTrace();
      }
  }
}

端口

端口表示计算机上一个程序的进程。

通过IP确定主机后,可以通过不同的端口号区分不同的进程(用来区分不同的软件)。

端口数是0~65535

又分为TCP和UDP两种协议,端口数65535*2,TCP和UDP同时存在80端口不冲突,但一个协议内,端口号不能冲突。

端口分类:公有端口:0~1023,一般是内部用的我们不碰。

Http:80 https:443 ftp:21 Telent:23监听

程序注册端口:1024~49151 ,分配用户或者程序

Tomcat:8080 MySQL:3306 Oracle:1521

动态,私有:49152~65535 一般也不碰

通信协议

传输层:TCP UDP对比:TCP类似于打电话(两者建立连接后,才开始发包),需要连接,稳定,要求三次握手(a第一次请求,对方有应答,a再次请求),四次挥手(a说要结束线程,b说好的,b说结束线程了吗,a说结束了),存在客户端,服务端,传输完成后,释放连接(安全 ),但是效率低。

UDP类似于发短信,不连接,不稳定,(都发过去了,不管对方准没准备好,收没收到),服务端和客户端没有明确的边界。

TCP实现聊天

服务端

package internet.lesson02;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

//服务端
public class TcpServerDemo01 {
   public static void main(String[] args) {
       ServerSocket serverSocket = null;   //提升一下对象(变量)的作用域
       Socket socket = null;
       InputStream is = null;
       ByteArrayOutputStream baos = null;

       try {
           //1、先给自身服务端设置一个地址,等待客户端连接
           serverSocket = new ServerSocket(9999);
           //4、等待客户端连接过来     //如果想不断等待客户端连接,写一个while(true)把服务端主程序放进去
           socket = serverSocket.accept();  //这里面的socket和TcpClientDemo01类里面的Socket socket = new Socket();这个socket是同一个对象,服务端开启一个插槽,客户端创建一个连接对象socket,准备插入插槽,serverSocket这个插槽接收这个连接对象。
           //6、读取客户端的消息(获取输入流)
           is = socket.getInputStream();

//           //7、读取消息
//           byte[] buffer = new byte[1024];   //创建一个缓冲区去接数据
//           int len;
//           //read()   从输入流中读取数据的下一个字节
//           //read(byte[] b) 将输入流is中读取一定数量 并将其存储在缓冲区数组 b 中。
//           //read(byte[] b, int off, int len)   将输入流中最多 len 个数据字节读入 byte 数组。
//           //read()==-1的含义   这是read()方法中的注释,意识就是read()从输入流中读取下一个字节。如果没有字节可读(也就是read()读到文件最后了)read()返回-1.
//           while ((len = is.read(buffer))!=-1){ //is.read(buffer)用is从缓冲区里面读数据,读完的值赋给len,不等于-1,说明里面还有值。
//               //public String(byte[] bytes, int offset, int length)这个方法
//               //通过使用平台的默认字符集解码指定的 byte 子数组,构造一个新的 String。
//               //参数: bytes:要解码为字符的 byte 数组
//               //     offset:要解码的第一个 byte 数组 的索引
//               //     length:要解码的 byte 数 的长度
//               String msg = new String(buffer,0,len);   //创建一个字符串开始拼接,把数组buffer中的值写出去,从buffer里面开始拼接,从0位拼到len位。
//               System.out.println(msg);
//           }
           //管道流
           baos = new ByteArrayOutputStream();  // 创建一个字节数组输出流ByteArrayOutputStream,把is的输入流接一下,再通过一个管道,输出出来
           byte[] buffer = new byte[1024];
           int len;
           while ((len = is.read(buffer))!=-1){  //假设这个流里有东西
               baos.write(buffer,0,len);   //把里面的东西写出来,buffer里面去写0位到len位,写出来的还是字节数组,
          }
           System.out.println(baos.toString());  //把写出来的字节数组toString()一下,转化成字符串输出出来
           //toString()返回该对象的字符串表示
//           baos.close();
//           is.close();
//           socket.close();
//           serverSocket.close();
      } catch (IOException e) {
           e.printStackTrace();
      } finally {
           //关闭资源:先开后关 管道里面可能还有水,需要一节一节关
           if(baos!=null){    //关闭每一个资源的标准写法,一定能处理异常
               try {
                   baos.close();
              } catch (IOException e) {
                   e.printStackTrace();
              }
          }
           if(is!=null){
               try {
                   is.close();
              } catch (IOException e) {
                   e.printStackTrace();
              }
          }
           if(socket!=null){
               try {
                   socket.close();
              } catch (IOException e) {
                   e.printStackTrace();
              }
          }
           if(serverSocket!=null){
               try {
                   serverSocket.close();
              } catch (IOException e) {
                   e.printStackTrace();
              }
          }
      }
  }
}

客户端

package internet.lesson02;

import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;

//客户端
public class TcpClientDemo01 {
   public static void main(String[] args) {

       Socket socket = null;
       OutputStream os = null;

       try {
           //2、客户端要知道服务端的地址和端口号
           InetAddress serverIP = InetAddress.getByName("127.0.0.1");
           int port = 9999;
           //3、创建一个socket连接
           socket = new Socket(serverIP,port);   //不写IP和端口的话,会报错Socket is not connected
           //5、发送消息IO流
           os = socket.getOutputStream();
           os.write("你好,李焕英".getBytes());   //将字符串转化成数组
      } catch (Exception e) {   //可以将异常作用域Exception调到最高
           e.printStackTrace();
      }finally {    //关闭资源
           if(os!=null){
               try {
                   os.close();
              } catch (IOException e) {
                   e.printStackTrace();
              }
          }
           if(socket!=null){    //
               try {
                   socket.close();
              } catch (IOException e) {
                   e.printStackTrace();
              }
          }
      }
  }
}

TCP文件上传实现

文件上传方法:

服务端:

package internet.lesson03;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class TcpServerDemo02 {
   public static void main(String[] args) throws Exception {  //提高作用域,直接抛出一个总的异常
       //1、创建一个服务端的地址和端口9000
       ServerSocket serverSocket = new ServerSocket(9000);
       //3、创建一个等待客户端连接的插槽(监听客户端的连接,阻塞式监听,会一直(监听)等待客户端的连接,连接后,程序才能继续往下运行,要不程序会一直停在这步运行)
       Socket socket = serverSocket.accept();
       //6、服务端接收客户端发来的数据流作为输入(获取输入流,拿到客户端发送的当前这个文件)
       InputStream is = socket.getInputStream();
       //7、需要通过一个管道把当前这个文件变得能写出来,不然这个文件(在服务端)输出不出来
       //加一个文件管道输出流, 输出   ,通过这个文件管道把从客户端接收到的文件变成一个对象fos(输出得到的结果)
       FileOutputStream fos = new FileOutputStream(new File("recieve.jpg"));  //输出得到的文件名设置为recieve.jpg
       byte[] buffer = new byte[1024];
       int len;
       while ((len=is.read(buffer))!=-1){   //把is里面的值写到buffer中去,并且判断buffer中是否还有值
           fos.write(buffer,0,len);     //因为is里面的值不能直接输出出来,需要把is通过管道转化变成fos对象,才能输出出来,才能调用write方法。把fos得到的值写入buffer中
      }
       //从数组byte[]中将len个数据写出到输出流fos上:void write(byte[] b, int off, int len);
       //通知客户端我接收完毕了,(写出去消息,类似于客户端发送消息)发送消息简单
       OutputStream outputStream = socket.getOutputStream();
       outputStream.write("我接收完毕了".getBytes());
       //关闭资源
       fos.close();
       is.close();
       socket.close();
       serverSocket.close();
  }
}
//传输顺序:文件 -->管道(文件转化成文件流) -->fis(判断文件流中有值,就写入os用来传输) -->os -->is(接受的is中有值,就写入fos输出) -->管道 -->fos

客户端:

package internet.lesson03;

import java.io.*;
import java.net.InetAddress;
import java.net.Socket;

public class TcpClientDemo02 {
   public static void main(String[] args) throws Exception {
       //2、创建一个连接服务端这个9000端口的对象socket:         InetAddress.getByName方法返回一个InetAddress对象,用来在给定主机名的情况下确定主机的 IP 地址
       Socket socket = new Socket(InetAddress.getByName("127.0.0.1"),9000);
       //4、客户端发送数据io流(创建一个输出流) ,一会用他去写,输出到服务端
       OutputStream os = socket.getOutputStream();
       //5、先把一个文件读进来,把它变成一个文件流,再输出出来
       //读取文件:把它变成一个文件流
       FileInputStream fis = new  FileInputStream(new File("D:\\代码\\JavaSE\\out\\production\\基础语法\\icon\\right.png"));//创建一个new FileInputStream的作用是把文件读进来,从new File()中读进来;header.jpg是针对于项目的相对路径。
       //写出文件:
       byte[] buffer = new byte[1024];    //fis.read(buffer)是把fis中的值赋给buffer数组
       int len;
       while ((len=fis.read(buffer))!=-1){   //如果buffer里面还有值,不返回-1
           os.write(buffer,0,len);  //输出流这个对象os就输出这个buffer数组中的0到len位字节,把buffer中的值写入到os输出流中
      }
       //客户端数据发送时,是先把文件通过管道变成文件流fis,这个文件流fis的read方法可以判断数组中有多少个值,然后给出返回值。
       //把要输出过去的输出流os的值一个个写入buffer中,用来客户端与服务端之间的io传输。
       socket.shutdownOutput();  //通知服务器,我已经传输完了。
       //确定服务端接收完毕,才能断开连接
       InputStream inputStream = socket.getInputStream();
       ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();  //返回一个byte数组类型,所以调用一个数组管道流,转化成ByteArrayOutputStream类的对象,才能写进bytes数组中。
       byte[] bytes = new byte[1024];
       int len2;
       while ((len2=inputStream.read(bytes))!=-1){
           byteArrayOutputStream.write(bytes,0,len2);
      }
       System.out.println(byteArrayOutputStream.toString());
       //从输入流inputStream中读入到数组bytes:int read(byte[] b);
       //从输入流中将len个数据读入到数组byte[]:int read(byte[] b, int off, int len);
       //关闭资源
       byteArrayOutputStream.close();
       inputStream.close();
       fis.close();
       os.close();
       socket.close();
  }
}

小结:服务端创建一个接口和插槽等待客户端连接,客户端创建一个与插槽同类的对象与插槽连接,客户端创建一个输出流os,服务端创建一个输入流is,客户端这边先把要上传的文件通过FileInputStream管道转化成文件流fis,判断如果fis中还有值(通过把fis的值读入到buffer数组中判断fis.read),则把buffer中的值写入到客户端的输出流os中(os.write),用来传输,到服务端is开始接受,通过把is的值读入buffer数组中is.read,判断是否还有输入,然后把is这个输入流的值经过FileOutputStream管道变成fos输出(fos.write)。

初识tomcat

同一个协议下,端口不能重复8080,否则会报错,提示端口被占用http协议:localhost IP .8080端口 /b.txt路径。

UDP

发短信,不用连接,需要知道对方的地址。没有特定的客户端和服务端,可以互相发送。

发送端:

package internet.lesson04;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
//不需要连接服务器
public class UdpClientDemo01 {
   public static void main(String[] args) throws Exception {
       //1、建立一个socket连接
       DatagramSocket socket = new DatagramSocket();
       //2、建个包
       String msg = "你好,服务器";
       InetAddress localhost = InetAddress.getByName("localhost");
       int port = 9090;
       //创建一个包,并且设置包的各种属性:包中的byte[]数据,数据数组的起始位,数组的长度,要发给谁(网址和端口)
       DatagramPacket packet = new DatagramPacket(msg.getBytes(),0,msg.getBytes().length,localhost,port);
       //3、发送包
       socket.send(packet);
       //4、关闭流
       socket.close();   //包不用关闭

  }
}

接收端:

package internet.lesson04;

import java.net.DatagramPacket;
import java.net.DatagramSocket;

public class UdpServerDemo01 {
   public static void main(String[] args) throws Exception {
       //1、创建一个开放的端口9090,socket是一个套接字(地址和端口的合体)
       DatagramSocket socket = new DatagramSocket(9090);
       //2、接收数据包
       //准备一个接收缓冲区   接受的请求
       byte[] buffer = new byte[1024];
       //创建一个接收报包 把缓存关联进去
       DatagramPacket packet = new DatagramPacket(buffer,0,buffer.length);

       socket.receive(packet);  //阻塞接收   把socket中的值赋给packet(socket等待用户发送数据,并把数据放到packet中)   udp用的是报包来传输,TCP是用数据流传输

       System.out.println(packet.getAddress().getHostAddress());
       System.out.println(new String(packet.getData(),0, packet.getLength()));   //把byte[]变成字符串用new String一个构造方法

       //关闭资源
       socket.close();
  }
}

UDP聊天实现

发送端:

package internet.chat;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;

public class UdpSenderDemo01 {
   public static void main(String[] args) throws Exception {
       //1、创建一个套接字连接(套接字由IP和端口组成)
       DatagramSocket socket = new DatagramSocket();
       //4、配置DatagramPacket packet包的数据,准备数据:控制台读取:System.in
           BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));   //new InputStreamReader意思是把控制台里的数据变成包装流,再通过BufferedReader,变成BufferedReader类的数据。
       while (true){
           String data = reader.readLine();   //读取那一行的东西   这个data数据是不可读的,我们要把它转化成字节,才可以读。
           byte[] datas = data.getBytes();    //把data转化成数据 byte类型
           //3、创建一个DatagramPacket类的对象,socket.send()方法需要,   new DatagramPacket()括号中需要配置数据报包的数据.   把datas这个数组打成一个包。
           DatagramPacket packet = new DatagramPacket(datas, 0, datas.length, new InetSocketAddress("localhost", 6666));     //这里面需要byte buf[]类的数据     把datas这个数组打成一个包。
           //2、用连接发送报包packet(需要一个DatagramPacket类型的对象,所以new一个DatagramPacket类的对象)
           socket.send(packet);
           if(data.equals("bye")){   //用字符串比对是否相等
               break;
          }
      }
       //5、关闭资源
       socket.close();
  }
}

接收端:udp不存在客户端和服务端,通过循环,不断接收和发送,实现聊天,互相发送。

package internet.chat;

import java.net.DatagramPacket;
import java.net.DatagramSocket;

public class UdpReceiveDemo01 {
   public static void main(String[] args) throws Exception {
       DatagramSocket socket = new DatagramSocket(6666);   //创建一个端口6666,接收sender发的东西
       while (true){
           //准备接收包裹
           byte[] buffer = new byte[1024];   //创建一个缓存容器,用来存放包packet
           DatagramPacket packet = new DatagramPacket(buffer,0,buffer.length);   //把packet放入容器buffer中   准备一个空容器,封装成DatagramPacket类的包裹,创建内部是数组buffer类型的DatagramPacket包裹用来接收socket传来的(有值的)packet,然后存放。
           socket.receive(packet);   //把套接字数据流存放到包packet中,阻塞式接收包裹   这句话的顺序不能错,先接收包裹,再读取包裹数据

           //断开连接,内容是bye的时候
           byte[] data = packet.getData();    //拿到packet包中的数据,是一个byte[]的数组   读取包裹数据
           String receiveData = new String(data, 0, data.length);   //需要把这个数组变成字符串,从0位到length位,可以把数据读出来
           System.out.println(receiveData);
           if(receiveData.equals("bye")){     //如果得到的数据是bye,跳出循环
               break;
          }
           //System.out.println(new String(packet.getData(),0, packet.getLength()));   //输出数据的简写
      }
       socket.close();


  }
}

UDP多线程在线咨询

发送端:

package internet.chat01;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;

public class ChatSender implements Runnable{
   DatagramSocket socket = null;
   BufferedReader reader = null;

   private String toIP;      //定义发送到的地址
   private int toPort;       //定义发送到的端口
   private int fromPort;    //定义从哪个地址发送的

   public ChatSender(String toIP, int toPort, int fromPort) {   //定义这几个变量的构造方法,能够从用户输入,获取这几个变量的值
       this.toIP = toIP;
       this.toPort = toPort;
       this.fromPort = fromPort;

       try {
           socket = new DatagramSocket(fromPort);     //创建一个套接字连接,并且说明来自哪个端口,说明发送方自己是谁
           //4、配置DatagramPacket packet包的数据,准备数据:控制台读取:System.in
           reader = new BufferedReader(new InputStreamReader(System.in));   //new InputStreamReader意思是把控制台里的数据变成包装流,再通过BufferedReader,变成BufferedReader类的数据。
      } catch (SocketException e) {
           e.printStackTrace();     //打印栈信息
      }

  }

   @Override
   public void run() {
       while (true){
           try {
               String data = reader.readLine();   //读取那一行的东西   这个data数据是不可读的,我们要把它转化成字节,才可以读。
               byte[] datas = data.getBytes();    //把data转化成数据 byte类型
               //3、创建一个DatagramPacket类的对象,socket.send()方法需要,   new DatagramPacket()括号中需要配置数据报包的数据.   把datas这个数组打成一个包。
               DatagramPacket packet = new DatagramPacket(datas, 0, datas.length, new InetSocketAddress(this.toIP, this.toPort));     //这里面需要byte buf[]类的数据,new InetSocketAddress(this.toIP, this.toPort)说明要发送到哪个地址,哪个端口
               //2、用连接发送报包packet(需要一个DatagramPacket类型的对象,所以new一个DatagramPacket类的对象)
               socket.send(packet);
               if(data.equals("bye")){   //用字符串比对是否相等
                   break;
              }
          } catch (Exception e){   //Exception范围有点大,好处是包含的异常范围广,缺点是运行效率会低一些
               e.printStackTrace();
          }
      }
       //5、关闭资源
       socket.close();
  }
}
//new BufferedReader(new InputStreamReader(System.in)),这是用来从键盘接受一行输入的代码,下面我们从里到外进行分析吧。
//System.in的类型是InputStream,它代表的是键盘接受的输入,就是说键盘是数据源;System.in的类型可以归结为节点流、字节流、输入流;接下来是InputStreamReader这个对象是处理流,字符流,输入流;
//最外面的是BufferedReader的类型是缓冲处理流、字符流、输入流。

接收端:

package internet.chat01;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

public class ChatReceive implements Runnable{
   DatagramSocket socket = null;

   private int port;      //定义我们要开启端口
   private String msgFrom;   //定义消息从哪里来

   public ChatReceive(int port,String msgFrom) {
       this.port = port;
       this.msgFrom = msgFrom;     //可以知道消息从哪里来
       try {
           socket = new DatagramSocket(port);   //创建一个端口,端口设置为port,接收sender发的东西
      } catch (SocketException e) {
           e.printStackTrace();
      }
  }

   @Override
   public void run() {
       while (true){
           try {    //整体快速try catch ---选择要抛出异常的多行代码,code-->surround with --> try catch   alt+ctrl+t
               //准备接收包裹
               byte[] buffer = new byte[1024];   //创建一个缓存容器,用来存放包packet
               DatagramPacket packet = new DatagramPacket(buffer,0,buffer.length);   //把packet放入容器buffer中   准备一个空容器,封装成DatagramPacket类的包裹,创建内部是数组buffer类型的DatagramPacket包裹用来接收socket传来的(有值的)packet,然后存放。
               socket.receive(packet);   //把套接字数据流存放到包packet中,阻塞式接收包裹   这句话的顺序不能错,先接收包裹,再读取包裹数据

               //断开连接,内容是bye的时候
               byte[] data = packet.getData();    //拿到packet包中的数据,是一个byte[]的数组   读取包裹数据
               String receiveData = new String(data, 0, data.length);   //需要把这个数组变成字符串,从0位到length位,可以把数据读出来
               System.out.println(msgFrom+":"+receiveData);
               if(receiveData.equals("bye")){     //如果得到的数据是bye,跳出循环
                   break;
              }
               //System.out.println(new String(packet.getData(),0, packet.getLength()));   //输出数据的简写
          } catch (IOException e) {
               e.printStackTrace();
          }
      }
       socket.close();

  }
}

用户学生:调用多线程

package internet.chat01;

public class ChatStudent {    //多个用户对话,实质上就是老师和学生类调用ChatSender和ChatReceive这两个线程运行而已,多线程运行,学生的ChatReceive端口要和老师的ChatSender端口一致。
   public static void main(String[] args) {
       new Thread(new ChatSender("localhost",2222,3333)).start();
       new Thread(new ChatReceive(7777,"老师")).start();
  }
}

用户老师:

package internet.chat01;

public class ChatTeacher {
   public static void main(String[] args) {
       new Thread(new ChatSender("localhost",7777,5555)).start();
       new Thread(new ChatReceive(2222,"学生")).start();
  }
}

URL下载网络资源

URL:统一资源定位符,定位资源的,定位互联网上的某一资源。

https://www.baidu.com/43

协议:http:// https ftp

DNS:域名解析:把www.baidu.com解析成10.22.33.44 映射

协议://ip地址:端口/项目名/资源 不能比这个多

http://localhost:8080/zhangsan/SecurityFile.txt 在webapps下面新建一个包和一个TXT文件security,bin ---start.bat启动Tomcat服务器

package internet.lesson05;

import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

public class UrlDown {
   public static void main(String[] args) throws Exception {
       //1、下载地址
       URL url = new URL("http://localhost:8080/zhangsan/SecurityFile.txt");
       //2、连接到这个资源 转换成HTTP类型
       HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
       //3、得到这个资源的输入流,网络上的一切资源都是流
       InputStream inputStream = urlConnection.getInputStream();

       FileOutputStream fileOutputStream = new FileOutputStream("SecurityFile.txt");   //建一个管道,变成可写出来的
       byte[] buffer = new byte[1024];
       int len;
       if((len=inputStream.read(buffer))!=-1){   //接收中,输入流中读出来没有了
           fileOutputStream.write(buffer,0, len);   //就把它写进去fileOutputStream
      }
       fileOutputStream.close();
       inputStream.close();
       urlConnection.disconnect();   //断开连接
  }
}

 

posted @ 2021-03-13 22:58  爱罗翔的张三  阅读(45)  评论(0编辑  收藏  举报