TCP协议

1、什么是TCP协议呢?

1.TCP是一种面向连接,安全、可靠的传输数据的协议。

2.传输前,采用“三次握手”方式,点对点通信,是可靠的 。

3.在连接中可进行大数据量的传输。

注意: 如果在java中只要是使用java.net.Socket类实现通信,底层即是使用了TCP协议。

Socket

构造器

说明

public Socket(String host , int port)​

创建发送端的Socket对象与服务端连接,参数为服务端程序的ip和端口。

Socket类成员方法

方法

说明

OutputStream getOutputStream()

获得字节输出流对象(发)

InputStream getInputStream()

获得字节输入流对象(收)

需求:服务端实现

实现步骤:

 1.创建ServerSocket对象,注册服务端端口。

2.调用ServerSocket对象的accept()方法,等待客户端的连接,并得到Socket管道对象。

3.通过Socket对象调用getInputStream()方法得到字节输入流、完成数据的接收。

4.释放资源:关闭socket管道

ServerSocket代码实现服务端:

服务端代码:

public static void main(String[] args) {
        try {
            System.out.println("===========服务端启动了==========");
            //1、注册端口
            ServerSocket serverSocket = new ServerSocket(7777);
            //2、必须调用accept方法,等待接受用户的Socket链接请求,建立Socket通信管道
            Socket socket = serverSocket.accept();
            //3、从socket通信管道中得到一个字节输入流
            InputStream is = socket.getInputStream();
            //4、把字节输入流包装缓冲字符输入流进行消息接收
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
            //5、按照行读取消息
            String msg;
            if ((msg = br.readLine()) != null){
                System.out.println(socket.getRemoteSocketAddress()+"说了:"+msg);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

需求:客户端实现

实现步骤:

1.创建客户端的Socket对象,请求与服务端的连接。

2.使用socket对象调用getOutputStream()方法得到字节输出流。

3.使用字节输出流完成数据的发送。

4.释放资源:关闭socket管道。

客户端代码:

 public static void main(String[] args) {
        try {
            System.out.println("==========客户端启动了============");
            //1、创建Socket通信管道请求与服务端链接
            //public Socket (String host , int port)
            //参数一:服务端的IP地址
            //参数二:服务端的端口
            Socket socket = new Socket("127.0.0.1",7777);

            //2、从socket通信管道中得到一个字节输出流  负责发送数据
            OutputStream os = socket.getOutputStream();

            //3、把低级的字节流打包成打印流
            PrintStream ps = new PrintStream(os);

            //4、发送消息
            ps.println("我是TCP的客户端,我已经与你对接,并发出邀请:约吗?");
            ps.flush();

            //关闭资源(不建议关闭)
            socket.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

实现结果:

TCP协议_第1张图片TCP协议_第2张图片

 注意:一定要服务端先启动,客户端才可以启动,否则就会报错。

TCP协议_第3张图片

 ServerSocket(服务端)

构造器

说明

public ServerSocket(int port)

注册服务端端口

 ServerSocket类成员方法

方法

说明

public Socket accept()

等待接收客户端的Socket通信连接

连接成功返回Socket对象与客户端建立端到端通信

 注意:如果客户端和服务端一方出了问题,另一方也会失效或者出错。

需求:使用TCP通信实现:多发多收消息、

实现步骤:

1.可以使用死循环控制服务端收完消息继续等待接收下一个消息。

2.客户端也可以使用死循环等待用户不断输入消息。

3.客户端一旦输入了exit,则关闭客户端程序,并释放资源。

服务端代码:

public static void main(String[] args) {
        try {
            System.out.println("===========服务端启动了==========");
            //1、注册端口
            ServerSocket serverSocket = new ServerSocket(7777);
            //2、必须调用accept方法,等待接受用户的Socket链接请求,建立Socket通信管道
            Socket socket = serverSocket.accept();
            //3、从socket通信管道中得到一个字节输入流
            InputStream is = socket.getInputStream();
            //4、把字节输入流包装缓冲字符输入流进行消息接收
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
            //5、按照行读取消息
            String msg;
            while ((msg = br.readLine()) != null){
                System.out.println(socket.getRemoteSocketAddress()+"说了:"+msg);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

客户端代码:

public static void main(String[] args) {
        try {
            System.out.println("==========客户端启动了============");
            //1、创建Socket通信管道请求与服务端链接
            //public Socket (String host , int port)
            //参数一:服务端的IP地址
            //参数二:服务端的端口
            Socket socket = new Socket("127.0.0.1",7777);

            //2、从socket通信管道中得到一个字节输出流  负责发送数据
            OutputStream os = socket.getOutputStream();

            //3、把低级的字节流打包成打印流
            PrintStream ps = new PrintStream(os);


            Scanner sc = new Scanner(System.in);
            while (true) {
                System.out.println("请说:");
                String msg = sc.nextLine();
                //4、发送消息
                ps.println(msg);
                ps.flush();
            }

            //关闭资源(不建议关闭)
//            socket.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

实现结果:

TCP协议_第4张图片TCP协议_第5张图片

这次多发多收是如何实现的呢?

通过客户端使用循环反复地发送消息,服务端使用循环反复地接收消息。从而实现多发多收。

注意:目前服务端是单线程的,每次只能处理一个客户端的消息。


 

使用多线程——同时接受多个客户端消息(优化多发多收)

客户端代码:

package com.wfl.d7_socket3;

import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;

/**
    目标: 多发多收
 */
public class ClientDemo1 {
    public static void main(String[] args) {
        try {
            System.out.println("==========客户端启动了============");
            //1、创建Socket通信管道请求与服务端链接
            //public Socket (String host , int port)
            //参数一:服务端的IP地址
            //参数二:服务端的端口
            Socket socket = new Socket("127.0.0.1",7777);

            //2、从socket通信管道中得到一个字节输出流  负责发送数据
            OutputStream os = socket.getOutputStream();

            //3、把低级的字节流打包成打印流
            PrintStream ps = new PrintStream(os);


            Scanner sc = new Scanner(System.in);
            while (true) {
                System.out.println("请说:");
                String msg = sc.nextLine();
                //4、发送消息
                ps.println(msg);
                ps.flush();
            }


        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

服务端启动代码:

package com.wfl.d7_socket3;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

/**
    目标: 实现服务端可以同时处理多个客户端消息
 */
public class ServerDemo2 {
    public static void main(String[] args) {
        try {
            System.out.println("===========服务端启动了==========");
            //1、注册端口
            ServerSocket serverSocket = new ServerSocket(7777);
            while (true) {
                //2、必须调用accept方法,等待接受用户的Socket链接请求,建立Socket通信管道
                Socket socket = serverSocket.accept();
                System.out.println(socket.getRemoteSocketAddress()+"上线了~~");
                //3、开始创建独立的线程处理socket
                new ServerReaderThread(socket).start();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

服务端线程代码:

package com.wfl.d7_socket3;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

public class ServerReaderThread extends Thread{
    private Socket socket;
    public ServerReaderThread (Socket socket){
        this.socket = socket;
    }
    @Override
    public void run() {
        try {
            //3、从socket通信管道中得到一个字节输入流
            InputStream is = socket.getInputStream();
            //4、把字节输入流包装缓冲字符输入流进行消息接收
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
            //5、按照行读取消息
            String msg;
            while ((msg = br.readLine()) != null){
                System.out.println(socket.getRemoteSocketAddress()+"说了:"+msg);
            }
        } catch (Exception e) {
            System.out.println(socket.getRemoteSocketAddress()+"下线了~~");
        }
    }
}

重点:

1.主线程定义了循环负责接收客户端Socket管道连接

2.每接收到一个Socket通信管道后分配一个独立的线程负责处理它。

 

使用线程池优化(引入线程池)

客户端代码:

package com.wfl.d8_socket4;

import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;

/**
    拓展: 使用线程池优化:实现通信。
 */
public class ClientDemo1 {
    public static void main(String[] args) {
        try {
            System.out.println("==========客户端启动了============");
            //1、创建Socket通信管道请求与服务端链接
            //public Socket (String host , int port)
            //参数一:服务端的IP地址
            //参数二:服务端的端口
            Socket socket = new Socket("127.0.0.1",7777);

            //2、从socket通信管道中得到一个字节输出流  负责发送数据
            OutputStream os = socket.getOutputStream();

            //3、把低级的字节流打包成打印流
            PrintStream ps = new PrintStream(os);


            Scanner sc = new Scanner(System.in);
            while (true) {
                System.out.println("请说:");
                String msg = sc.nextLine();
                //4、发送消息
                ps.println(msg);
                ps.flush();
            }

            //关闭资源(不建议关闭)
//            socket.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

服务端代码:采用线程池。

package com.wfl.d8_socket4;

import com.wfl.d7_socket3.ServerReaderThread;

import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.*;

/**
    目标: 实现服务端可以同时处理多个客户端消息
 */
public class ServerDemo2 {
    //使用静态变量记住一个线程池对象
    private static ExecutorService pool = new ThreadPoolExecutor(3,
            5,6, TimeUnit.SECONDS,new ArrayBlockingQueue<>(2),
            Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
    public static void main(String[] args) {
        try {
            System.out.println("===========服务端启动了==========");
            //1、注册端口
            ServerSocket serverSocket = new ServerSocket(7777);
            while (true) {
                //2、必须调用accept方法,等待接受用户的Socket链接请求,建立Socket通信管道
                Socket socket = serverSocket.accept();
                System.out.println(socket.getRemoteSocketAddress()+"上线了~~");
               //任务对象负责读取消息
                Runnable srr = new ServerReaderRunnable(socket);
                pool.execute(srr);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
package com.wfl.d8_socket4;


import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;

public class ServerReaderRunnable implements Runnable {
    private Socket socket;
    public ServerReaderRunnable(Socket socket){
        this.socket = socket;
    }
    @Override
    public void run() {
        try {
            //3、从socket通信管道中得到一个字节输入流
            InputStream is = socket.getInputStream();
            //4、把字节输入流包装缓冲字符输入流进行消息接收
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
            //5、按照行读取消息
            String msg;
            while ((msg = br.readLine()) != null){
                System.out.println(socket.getRemoteSocketAddress()+"说了:"+msg);
            }
        } catch (Exception e) {
            System.out.println(socket.getRemoteSocketAddress()+"下线了~~");
        }
    }
}

使用线程池的优势:

服务端可以复用线程处理多个客户端,可以避免系统瘫痪。

适合客户端通信时长较短的场景。

即时通信是啥?

即时通信,是指一个客户端的消息发出去,其他客户端可以接收到。

要咋设计?

1.即时通信需要进行端口转发的设计思想。

2.服务端需要把在线的Socket管道存储起来

3.一旦收到一个消息要推送给其他管道

实例:微信群发,就是即时通信的思想!

TCP通信如何实现BS请求网页信息回来呢?

1.客户端使用浏览器发起请求(不需要开发客户端)

2.服务端必须按照浏览器的协议规则响应数据。

你可能感兴趣的:(网络通信,Java,tcp/ip,网络,服务器)