TCP协议

TCP协议

TCP协议概述
        * TCP英语全称:transfer control protocol 中文名称:传输控制协议。
     
 TCP协议的特点(记忆)
    * 面向连接的协议,速度慢但是可靠。
    * 通过三次握手建立连接,形成数据传输通道。
    * 通过四次挥手断开连接
    * 客户端主动发送消息给服务器端,服务器接收到消息之后,可以反馈数据给客户端。
    * 基于IO流进行数据传输
    * 数据传输大小没有限制。

TCP协议使用场景
    * 文件上传和下载
    * 邮件发送
    * 远程登录

TCP协议相关的类
    * Socket:代表客户端程序,一个该类的对象就表示一个客户端程序。
    * ServerSocket:代表服务器端,一个该类的对象就表示开启了一个服务器。

TCP协议发送和接收数据

发送:
   创建TCP客户端的Socket对象
   获取输出流,写数据
   释放资源
   
接收:
   创建TCP服务器端的Socket对象
   监听客户端连接
   获取输入流,读取数据
   释放资源

Socket机制

  A:通信两端都应该有Socket对象
  B:所有的通信都是通过Socket间的IO进行操作的

TCP通信之客户端

127.0.0.1:本机ip
TCP客户端代码实现
Socket:代表客户端程序,一个该类的对象就表示一个客户端程序。
Socket类构造方法
    * Socket(String host, int port) 
        * 根据ip字符串和端口创建客户端Socket对象 
        * 立即连接指定的服务器,如果服务器没有开启,则直接抛出异常。

Socket类常用方法
    * OutputStream getOutputStream(); 获得字节输出流,通过输出流往服务端输出数据。
    * InputStream getInputStream(); 获得字节输入流,通过输入流读取服务器端返回的数据。

TCP客户端实现步骤
    * 创建Socket对象:启动一个客户端程序
    * 调用Socket对象的getOutputStream方法获得字节输出流对象
    * 通过字节输出流对象往服务器输出数据
    * 调用Socket对象的getInputStream方法获得字节输入流对象
    * 通过字节输入流对象读取服务器返回的数据
    * 关闭Socket

连接被拒绝。TCP协议一定要先看服务器。

  • java.net.ConnectException: Connection refused: connect

public static void main(String[] args) throws IOException {
    // 创建发送端的Socket对象
    // Socket(InetAddress address, int port)
    // Socket(String host, int port)
    Socket s = new Socket("192.168.77.92", 8888);

    // 获取输出流,写数据
    // public OutputStream getOutputStream()
    OutputStream os = s.getOutputStream();

    os.write("hello,tcp,我来了".getBytes());

    // 释放资源
    s.close();
}

TCP通信之服务器端

TCP服务器端代码实现
 
ServerSocket:代表服务器端,一个该类的对象就表示开启了一个服务器。
ServerSocket类构造方法
    * ServerSocket(int port) 根据端口号创建服务器端程序
ServerSocket类成员方法
    * Socketaccept(); 等待客户端连接并获得一个与客户端相关的Socket对象
TCP服务器端开发步骤
    * 创建ServerSocket对象,开启服务器
    * 调用accept方法等待客户端连接并获得Socket对象
    * 调用Socket对象getInputStream方法获得字节输入流对象
    * 通过字节输入流对象读取客户端发送的数据。
    * 调用Socket对象getOutputStream方法获得字节输出流对象
    * 通过字节输出流对象往客户端输出数据
    * 调用close方法关闭Socket对象。

//TCPClient 
    public static void main(String[] args) throws Exception {
        // 创建Socket对象
        Socket socket = new Socket("127.0.0.1",9999);
        // 要发送的内容
        String content = "你好 TCP服务器端,约吗";
        
        // 获得字节输出流
        OutputStream out =  socket.getOutputStream();
        // 往服务器发送数据
        out.write(content.getBytes());
        
        // 接收服务端发送的数据
        InputStream in = socket.getInputStream();
        // 创建字节数组,用来存储服务器返回的数据
        byte[] buf = new byte[1024];
        int length = in.read(buf);      
        System.out.println(new String(buf,0,length));
        
        // 关闭资源
        socket.close();
    }


//TCPServer
public static void main(String[] args) throws IOException {

    // 创建服务端的Socket对象
    // ServerSocket(int port)
    ServerSocket ss = new ServerSocket(55555);

    // 监听客户端连接。返回一个对应的Socket对象
    // public Socket accept()
    Socket s = ss.accept(); // 侦听并接受到此套接字的连接。此方法在连接传入之前一直阻塞。

    // 获取输入流,读取数据显示在控制台
    InputStream is = s.getInputStream();

    byte[] bys = new byte[1024];
    int len = is.read(bys); // 阻塞式方法
    String str = new String(bys, 0, len);

    String ip = s.getInetAddress().getHostAddress();

    System.out.println(ip + "---" + str);

    // 释放资源
    s.close();
    // ss.close(); //这个不应该关闭
}

  • 客户端键盘录入,服务器输出到控制台


public static void main(String[] args) throws Exception {
    // 创建客户端Socket对象

    Socket s = new Socket("192.168.77.1", 22222);

    // 键盘录入数据

    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

    // 把通道内的流给包装一下

    BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));

    String line = null;

    while ((line = br.readLine()) != null) {

        // 键盘录入数据要自定义结束标记

        if ("886".equals(line)){

            break;

        }

        bw.write(line);
        //如果服务器读取用.readLine,就必须加上.newLine();不然服务器会一直等待换行符
        bw.newLine();
        
        //加上.flush()服务器才能读取到数据
        bw.flush();

    }

    // 释放资源

    bw.close();

    br.close();

    s.close();

}


public static void main(String[] args) throws Exception {

    // 创建服务器Socket对象

    ServerSocket ss = new ServerSocket(22222);

    // 监听客户端连接

    Socket s = ss.accept();

    // 包装通道内容的流

    BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));

    String line =null;

    while ((line = br.readLine()) != null){

        System.out.println(line);

    }

    br.close();

    s.close();

    ss.close();

}

  • 告诉服务器,读取到这条数据说明我就结束,你也结束吧。

  • public void shutdownOutput()

    public static void main(String[] args) throws 
    IOException {
      // 创建客户端Socket对象
      Socket s = new Socket("192.168.12.92", 11111);
    
      // 封装文本文件
      BufferedReader br = new BufferedReader(new FileReader("InetAddressDemo.java"));
      // 封装通道内流
      BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
    
      String line = null;
      while ((line = br.readLine()) != null) {
          // 阻塞
       bw.write(line);
       bw.newLine();
       bw.flush();
      }
    
      //Socket提供了一个终止,它会通知服务器你别等了,我没有数据过来了,终止后输出流不可再使用,用来传输文件等数据
      s.shutdownOutput();
    
      // 接收反馈
      BufferedReader brClient = new BufferedReader(new InputStreamReader( s.getInputStream()));
      String client = brClient.readLine(); // 阻塞
      System.out.println(client);
    
      // 释放资源
      br.close();
      s.close();
     }
    }

TCP文件上传

  • 需求:将文件上传到指定IP的主机

    • TCP的服务端可以接受多个客户端的连接

    • TCP的客户端上传文件到服务器端

    • 文件保存到c:/java/file文件下,文件名随机生成,只要不出现文件覆盖即可。

    • 服务器端需要反馈上传状态(成功或失败)给客户端。

  • 示例代码

/**
 * 上传线程
 * @author pkxing
 *
 */
public class UploadThread extends Thread{

    // 声明Socket对象
    private Socket socket = null;
    // 构造方法
    public UploadThread(Socket socket) {
        super();
        this.socket = socket;
    }
    
    @Override
    public void run() {
        FileOutputStream fos = null;
        try {
            // 目标文件夹
            File destDir = new File("/aaaa");
            // 随机生成一个文件名
            String fileName = System.currentTimeMillis()+ new Random().nextInt(100000000)+".png";
            // fileName = 1234567424.png
            fos = new FileOutputStream(new File(destDir,fileName));
            // 获得字节输入流对象
            InputStream in = socket.getInputStream();
            // 定义一个字节数组:用来存储文件数据
            byte[] buf = new byte[1024];
            // 定义整型变量用来记录实际读取到的字节
            int length = -1;
            // 使用循环读取客户端发送的文件数据
            while((length = in.read(buf)) != -1) {
                // 将文件数据输出到目标文件中
                fos.write(buf, 0, length);
            }
            // 获得字节输出流对象
            OutputStream out = socket.getOutputStream();
            // 返回上传成功给客户端
            out.write("上传成功".getBytes());
            
            // 获得客户端ip地址
            String clientIP = socket.getInetAddress().getHostAddress();
            System.out.println("收到 "+clientIP+" 上传的文件");
        } catch (Exception e) {
            try {
                if(socket == null) return;
                // 获得字节输出流对象
                OutputStream out = socket.getOutputStream();
                // 返回上传失败给客户端
                out.write("上传失败".getBytes());
            } catch (IOException e1) {
            }
        } finally {
            
            try {
                if(socket != null)
                    socket.close();
            } catch (IOException e) {
            }
            try {
                if(fos != null)
                    fos.close();
            } catch (IOException e) {
            }
        }
    }
}

// 客户端程序
public class TCPClient {
    public static void main(String[] args) throws Exception {
        // 创建客户端Socket对象
        Socket socket = new Socket("192.168.77.132",8888);
        // 获得字节输出流对象:往服务器输出数据
        OutputStream out = socket.getOutputStream();
        // 创建字节输入流
        FileInputStream fis = new FileInputStream("/aaaaa.png");
        // 定义字节数组存储读取到文件数据
        byte[] buf = new byte[1024];
        // 定义整型变量用来记录实际读取到字节个数
        int length = -1;
        // 循环读取文件数据
        while((length = fis.read(buf)) != -1) {
            // 将数据输出到服务器端
            out.write(buf,0,length);
        }
        // 告诉服务器端数据已经传输完毕
        // 输出一个结束标记给服务器
        socket.shutdownOutput();
        System.out.println("333333333333");
        // 获得字节输入流对象
        InputStream in = socket.getInputStream();
        // 读取服务器返回的数据
        length = in.read(buf);
        System.out.println(new String(buf,0,length));
        
        // 关闭流和socket
        fis.close();
        socket.close();
    }
}


/**
 * 文件上传服务器端
 * @author pkxing
 *
 */
public class TCPServer {
    public static void main(String[] args) throws Exception{
        // 开启服务端
        ServerSocket serverSocket = new ServerSocket(8888);
        // 使用死循环保证服务器不退出
        while(true) {
            // 等待客户端连接获得获得Socket
            Socket socket = serverSocket.accept();
            // 创建线程对象
            new UploadThread(socket).start();
        }       
    }
}

小结

  • UDP相关的两个类

DatagramPacket概述
    * 数据包对象。用来封装要发送的数据。
DatagramPacket构造方法
    * 发送端数据包对象:DatagramPacket(byte[] buf,int length, InetAddress address,int port);
    * 接收端数据包对象:DatagramPacket(byte[] buf,int length);
    * 获得发送端的地址:InetAddress getAddress();
    * 获得实际接收的字节长度:int getLength();
    * 获得发送端的端口号:int getPort();

DatagramSocket概述
    * 发送对象,负责接收和发送数据包对象。
DatagramSocket构造方法
    * DatagramSocket() 创建发送端Socket对象
    * DatagramSocket(int port) 创建接收端Socket对象
DatagramSocket成员方法
    * void send(DatagramPacket dp); 发送数据包
    * void receive(DatagramPacket dp); 接收数据包
    * void close() 关闭资源释放端口号。
  • TCP相关的两个类

Socket概述
    * 客户端Socket对象,代表一个客户端程序。
Socket构造方法
    * Socket(String host,int port); 创建客户端Socket对象,立即连接指定的服务器,如果服务器没开启,则直接抛出异常。
Socket成员方法
    * InputStream getInputStream() 获得字节输入流对象
    * OutputStream getOutputStream() 获得字节输出流对象
    * void shutdownOutput() 关闭字节输出流。
    * void close() 关闭Socket对象,释放端口。
    
ServerSocket概述
    * 服务器端Socket对象,代表一个服务器程序
ServerSocket构造方法
    * ServerSocket(int port); 根据指定的端口创建服务器端程序
ServerSocket成员方法
    * Socket accept(); 等待客户端连接并获得Socket对象。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值