tcp 实现 java

socket 网络编程

写在前面

  • socket 是默认阻塞的,可以把他作为 位于网络层和传输层的之间的 一个抽象层

案例一、UDP 通信

  • UDP 是面向无连接的 并且传送的是数据包 利用io 流 传输数据。将其封装成包

1.发送端

  • 创建发送/接受 对象 datagramsocket,用于发送数据 (创建的对象的空参的)

  • 定义要发送的 数据 以字符串的形式给出

  • 将字符串 转成byte 数组 (封包对象的构造方法里面是的参数是byte[])

  • 将流过来的数据封装成包(datagrampacket)满参构造,指定byte[] 和byte数组的长度,IP地址对象,端口号

  • socket 对象 调用send()方法,传入封装的数据包对象 packet 进行发包

  • 关流

 package com.itheima.demo1udp;
 import java.net.DatagramPacket;
 import java.net.DatagramSocket;
 import java.net.InetAddress;
 ​
 public class Client
 {
     public static void main(String[] args) throws Exception
     {
         // 创建datagramsocket 对象 用发送数据
         DatagramSocket socket = new DatagramSocket();
         // 定义要发生的数据
         String str = "Hello World";
         // 将数据转成byte 数组
         byte[] bys = str.getBytes();
         // 创建数据包对象,进行封包 udp 面向无连接 需要指定端口号
         DatagramPacket packet = new DatagramPacket(bys,bys.length, InetAddress.getLocalHost(),6666);
         // 发送数据包
         socket.send(packet);
         // 关流
         socket.close();
 ​
     }
 }
 ​

接收端:

  • 创建 发送/接受 数据对象 (datagramsocket)作为 udp 的接受端要指定 端口号

  • 创建byte[ ]数组,用于存放的 发送端发发送的数据 (因为封包的构造方法里面是byte[])

  • 将发送方发来的数据 封装成包(datagrampakcet)构造方法的参数是bys 和bys.length

  • 调用socket方法 接受数据包

  • 打印数据(数据存放在byte[] 中,数组的长度=数据的长度=packet.getlength)

  • new String(bys,0,length)== new一个String类型的对象 取值是从第0个 长度为数组的长度 取的是bys数组

 package com.itheima.demo1udp;
 ​
 import java.net.DatagramPacket;
 import java.net.DatagramSocket;
 ​
 public class Server
 {
     public static void main(String[] args) throws Exception
     {
         // 创建接受包数据对象,udp 面向无连接 指定端口
         DatagramSocket socket = new DatagramSocket(6666);
         // 创建byte[]数组 用于存放数据
         byte[] bys = new byte[1000];
         // 创建数据包对象 用于将数据流 封装成包
         DatagramPacket packet = new DatagramPacket(bys,bys.length);
         // 接受数据包
         socket.receive(packet);
 ​
         // 打印数据 返回返回将要发送或接收到的数据的长度。
         int length = packet.getLength();
         System.out.println("接受到的资源: " + new String(bys,0,length));
         // 释放资源
         socket.close();
 ​
 ​
     }
 }

案例二、 tcp 协议的使用 发送端 发送数据给 接收端

写在前面

  • tcp 之间的通信 其实就是 两个socket 之间的通信

  • socket类 一个该类的对象就是代表一个发送端

    1. 构造方法: Public socket (String host,int port);

      ==注意 该方法的执行就会连接指定的服务器,若没有异常 则连接成功(三次握手成功)

    2. 常用方法

      1. OutputStream getOutputStream( )

      2. InputStream getInputStream( )

1.发送端

  • 创建socket对象,指定服务器的ip地址和端口号

  • 创建字节输出流对象,关联连接通道

  • 使用字节流对象将 数据写到 连接通道里去

  • 释放资源

 package com.itheima.demo2tcpo1;
 ​
 import java.io.OutputStream;
 import java.net.Socket;
 public class Client
 {
     public static void main(String[] args) throws Exception
     {
         // 创建socket对象,指定服务器的ip地址和端口号
         Socket socket = new Socket("127.0.0.1",6666);
 ​
         System.out.println(socket);
 ​
         String str = "你好吗?";
         // 创建字节输出流对象,关联连接通道
         OutputStream os = socket.getOutputStream();
         // 使用字节流对象将 数据写到 连接通道里去
         os.write(str.getBytes());
         // 释放资源
         socket.close();
     }
 }
 ​

2.接受端

  • 创建ServerSocket对象,指定服务器的端口号

  • 调用ServerSocket对象的 accept 方法 获取到Socket对象

  • 通过接收端的 socket对象 调用输入流方法

  • 使用字节输入流 读取 关联通道里的数据

  • 创建数组 因为关联通道在写的时候 构造方法指定值存在数组中里的

  • 获得 读取的字符长度

  • 打印 将字符强转成字符串

  • 释放资源

 package com.itheima.demo2tcpo1;
 ​
 import java.io.InputStream;
 import java.net.ServerSocket;
 import java.net.Socket;
 ​
 public class Server
 {
     public static void main(String[] args) throws Exception
     {
         // 创建ServerSocket对象,指定服务器的端口号
         ServerSocket serverSocket = new ServerSocket(6666);
         // 调用ServerSocket对象的 accept 方法 获取到Socket对象
         Socket socket = serverSocket.accept();
 ​
         System.out.println(socket);
 ​
         // 通过接收端的 socket对象 调用输入流方法
         InputStream is = socket.getInputStream();
         // 使用字节输入流 读取 关联通道里的数据
           // 创建数组 因为关联通道在写的时候 构造方法指定值存在数组中里的
         byte[] bys = new byte[8192];
           // 获得  读取的字符长度
         int len = is.read(bys);
         // 打印 将字符强转成字符串
         System.out.println(new String(bys,0,len));
         // 释放资源
         socket.close();
 ​
 ​
     }
 }
 ​

案例三、拓展 接受方 回话给发送端

接受端(额外增加的功能)

  • 在关闭之间 搞个输出流

  • 将 要回复的字符串 写到数组中去(已经关联了通道)

   // 回话
         String str = "我很好你呢?";
         socket.getOutputStream().write(str.getBytes());
         // 释放资源
         socket.close();

发送端(额外增加的代码)

  • 在关闭资源之前去 读取 接受端写入在数组中的数据

  • 打印(byte[] 强转成字符串)

  // 从字节输入流中读取
         byte[] bys = new byte[8192];
         // 读取
         int len = socket.getInputStream().read(bys);
         // 打印
         System.out.println(new String(bys,0,len));
         // 释放资源
         socket.close();

案例四:对话内容不限 对话次数不限(但问一次只能回答一次)

发送端和接受端:

  • 在连接建立之后 进入循环

  • 不能释放资源

  • 对话 内容键盘录入

注意,发送方在创建socket 对象时候 回自动进行三次握手

接受方 在调用accept( ) 返回 socket 对象时,服务器回一直处于阻塞状态 直到发送方连接进来

案例四,文件的上传

客服端 借助(输入流) 将文件从 硬盘写道 程序A里 ,程序A 借助文件(输出流)将文件输出 (在借助输入流)将文件 写道到关联通道里

服务器端,从关联通道中 借助(输出流)读取文件到 本地

客服端

  • 创建Socket 对象,指定要连接的服务器的ip地址和端口号

  • 创建文件输入流对象(读),关联数据源文件路径

  • 通过socket获得字节输出流对象,关联连接通道(输出就是要写 在 循环读中 用到写 写就得有容器 存放 byte数组)

  • 定义byte[ ] 数组,用来存储读取到得字节数据 (下一步就是常规的套路,在读循环中 写)

  • 定义int 类型变量,记录读取到 字符对应的 十进制数(字节)

  • 循环读(不为-1)

  • 注意 while ((b=fis.read(bys))!=-1) 从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中,判断得是,字节数组中是否还有元素

  • 写字节数据到连接通道中去 ==一气呵成write(bys,0,int b);

  • 释放资源

 package com.itheima.demo3fileup;
 ​
 import java.io.FileInputStream;
 import java.io.OutputStream;
 import java.net.InetAddress;
 import java.net.Socket;
 ​
 public class Client
 {
     public static void main(String[] args) throws Exception
     {
         // 创建socket对象,指定要连接服务器的IP地址和端口
         Socket socket = new Socket(InetAddress.getLocalHost().getHostAddress(),6667);
         // 创建文件字节输入流对象,关联数据源文件文件
         FileInputStream fis = new FileInputStream("day12-code\\aaa\\jobs.jpg");
 ​
         // 通过Socket获得文件输出流对象,这样输出流对象就也关联了连接通道
         OutputStream os = socket.getOutputStream();
         
         // 定义byte[] 数组 用来存储读取到的字节数
         byte[] bys = new byte[8192];
         // 创建int 类型的变量 用来记录读出来的字符 对应的
         int b;
         // 对文件输出流循环读
         while ((b=fis.read(bys))!=-1)
         {
             // 将读出来的字节写到 数组中 去实现关联流通道
             os.write(bys,0,b);
         }
         // 释放资源
         socket.close();
 ​
     }
 }
 ​

服务器端

  • 创建ServeSocket 对象,绑定端口号

  • 调用accept 方法 ,让服务器处于阻塞状态,接受客户端的请求,建立连接,得到socket对象

  • (上一步中得到的 socket对象)通过socket对象,会的字节输入流(读),关联连接通道

  • 创建文件输出流对象 关联文件目的路径(下一步就是常规的套路,在读循环中 写)

  • 定义int 类型变量,记录读取到 字符对应的 十进制数(字节)循环读(不为-1)

  • 写字节数据到连接通道中去 ==一气呵成write(bys,0,int b);

  • 释放资源

 package com.itheima.demo3fileup;
 ​
 import java.io.FileOutputStream;
 import java.io.InputStream;
 import java.net.ServerSocket;
 import java.net.Socket;
 ​
 public class Server
 {
     public static void main(String[] args) throws Exception
     {
         // 创建ServersSocket对象,绑定端口号
         ServerSocket ss = new ServerSocket(6667);
         // 调用accept方法,接受请求,建立连接,得到Socket对象
         Socket socket = ss.accept();
         // 通过socket对象 获得字节文件输入流,关联连接通道
         InputStream is = socket.getInputStream();
         
         // 创建文件字节输出流对象,关联目的文件路径
         FileOutputStream fos = new FileOutputStream("day12-code\\bbb\\jobs1.jpg");
         // 定义byte[],用来存放读取到的字节数据
         byte[] bys = new byte[8192];
         // 循环读写数据
         int b;
         while ((b=is.read(bys))!=-1)
         {
             // 写
             fos.write(bys,0,b);
         }
         // 释放资源
         socket.close();
     }
 }
 ​

文件上传的总结

  • 客户端设备

    1. 先创建socket 对象 没话说 确定服务器的ip 和port

    2. cpu 将内存的文件 读到内存中去(用输入流)

    3. 通过socket 对象 获取到 输出流对象(这样输出流就关联了 连接通道)

    4. 定义byte [ ] 、int 变量、实现读循环

    5. 在读循环中 将读出来的数据 写到byte[ ] 中去(这样服务器端 读 byte[ ]的时候j就关联了 连接通道 )

    6. 释放资源

  • 服务器端

    1. 创建ServeSocket 对象,绑定端口号(没得说)

    2. 调用accept 方法 ,让服务器处于阻塞状态,接受客户端的请求,建立连接,得到socket对象(没得说)

    3. 通过socket对象 获得字节文件输入流(要读byte[ ]),关联连接通道

    4. 创建文件字节输出流对象,关联目的文件路径

    5. 定义byte [ ] 、int 变量、实现读循环

    6. 在读循环中 将读出来的数据 写到byte[ ] 中去

案例五,文件上传2.0

  • 新增 上传后的 文件名自动生成

  • 服务器可以接受 上传文件

  • 加入线程,可以使多个客户端上传文件

客服端1

 package com.itheima.demo5fileuplusplus;
 ​
 import java.io.FileInputStream;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.InetAddress;
 import java.net.Socket;
 ​
 public class Client1
 {
     public static void main(String[] args) throws Exception
     {
         // 创建socket 对象,指定服务器的 ip 和 port
         Socket socket = new Socket("127.0.0.1",6668);
         // 创建文件字节输入流 管关联数据源路径
         FileInputStream fis = new FileInputStream("day12-code\\aaa\\11.jpg");
         // 获取字节输出流对象,关联连接通道
         OutputStream os = socket.getOutputStream();
         // 定义byte数组
         byte[] bys = new byte[8192];
 ​
         // 定义int 类型变量
         int b;
         /* 循环读 */
         while((b=fis.read(bys))!=-1)
         {
             // 写
             os.write(bys,0,b);
         }
         // 通知服务器 已经读完
         socket.shutdownOutput();
         // 读取 服务器的回话
         // 读取 服务器的回话
         // 再次管关联 连接通道
         InputStream aIs = socket.getInputStream();
         int ab;
         while ((ab = aIs.read(bys))!=-1)
         {
             System.out.println(new String(bys,0,ab));
         }
         socket.close();
 ​
     }
 ​
 }
 ​

客户端2

 package com.itheima.demo5fileuplusplus;
 ​
 import java.io.FileInputStream;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.InetAddress;
 import java.net.Socket;
 ​
 public class Client2
 {
     public static void main(String[] args) throws Exception
     {
         // 创建socket 对象,指定服务器的 ip 和 port
         Socket socket = new Socket("127.0.0.1",6668);
         // 创建文件字节输入流 管关联数据源路径
         FileInputStream fis = new FileInputStream("day12-code\\aaa\\jobs.jpg");
         // 获取字节输出流对象,关联连接通道
         OutputStream os = socket.getOutputStream();
         // 定义byte数组
         byte[] bys = new byte[8192];
 ​
         // 定义int 类型变量
         int b;
         /* 循环读 */
         while((b=fis.read(bys))!=-1)
         {
             // 写
             os.write(bys,0,b);
         }
         // 通知服务器 已经读完
         socket.shutdownOutput();
 ​
         // 读取 服务器的回话
         // 再次管关联 连接通道
         InputStream aIs = socket.getInputStream();
         int ab;
         while ((ab = aIs.read(bys))!=-1)
         {
             System.out.println(new String(bys,0,ab));
         }
 ​
         socket.close();
 ​
     }
 ​
 }
 ​

服务器端

 package com.itheima.demo5fileuplusplus;
 ​
 import java.io.FileOutputStream;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.ServerSocket;
 import java.net.Socket;
 ​
 public class Server
 {
     public static void main(String[] args) throws Exception {
         // 创建serversocket 对象
         ServerSocket ss = new ServerSocket(6668);
         // 服务器一直读
         while (true)
         {   // 获取到socket 对象
             Socket socket = ss.accept();
             // 创建文件输入流文件,关联连接通道
             InputStream is = socket.getInputStream();
             // 创建文件文件输出流文件,关联目的路径 (自动生成文件名)
             FileOutputStream fos = new        
                 FileOutputStream("day12code\\bbb\\"+System.currentTimeMillis()+".jpg");
             byte[] bys = new byte[8192];
             int b;
 ​
             System.out.println("服务器开始接受文件数据");
             while ((b = is.read(bys))!=-1)
             {
                 fos.write(bys,0,b);
             }
             System.out.println("服务器准备回写文件数据");
 ​
             // 服务器回话,再次关联连接通路
             OutputStream os = socket.getOutputStream();
             // 回写上传成功的信息给客户端
             os.write("文件上传成功".getBytes());
 ​
              socket.close();
             //ss.close();
             os.close();
 ​
         }
 ​
 ​
     }
 }
 ​

服务器端(线程版)

 package com.itheima.demo5fileuplusplus;
 ​
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.ServerSocket;
 import java.net.Socket;
 ​
 /**
  * @author ChenY@itheima.com
  * @date 2022/8/6 21:05
  */
 public class Server
 {
     public static void main(String[] args) throws Exception {
         // 创建serversocket 对象
         ServerSocket ss = new ServerSocket(6668);
 ​
         // 服务器一直读
         while (true)
         {   // 获取到socket 对象
             Socket socket = ss.accept();
 ​
 // 在连接完了 上线程
             // 线程版
             new Thread(new Runnable() {
                 @Override
                 public void run() {
 ​
                     try {
                         // 创建文件输入流文件,关联连接通道
                         InputStream is = socket.getInputStream();
                         // 创建文件文件输出流文件,关联目的路径 (自动生成文件名)
                         FileOutputStream fos = new FileOutputStream("day12-code\\bbb\\"+System.currentTimeMillis()+".jpg");
                         byte[] bys = new byte[8192];
                         int b;
 ​
                         System.out.println("服务器开始接受文件数据");
                         while ((b = is.read(bys))!=-1)
                         {
                             fos.write(bys,0,b);
                         }
                         System.out.println("服务器准备回写文件数据");
 ​
                         // 服务器回话,再次关联连接通路
                         OutputStream os = socket.getOutputStream();
                         // 回写上传成功的信息给客户端
                         os.write("文件上传成功".getBytes());
 ​
                         socket.close();
                         //ss.close();
                         os.close();
                     } catch (IOException e) {
                         e.printStackTrace();
                     } finally { 
                     }
 ​
 ​
                 }
             }).start();
 ​
         }
 ​
     }
 }
 ​

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值