TCP链接简介

TCP在真正的读写操作之前,server与client之间必须建立一个连接,当读写操作完成后,双方不再需要这个连接时它们可以释放这个连接,连接的建立通过三次握手,释放则需要四次握手,所以说每个连接的建立都是需要资源消耗和时间消耗的。

长连接

模拟一种长连接的情况:

  1. client 向 server 发起连接
  2. server 接到请求,双方建立连接
  3. client 向 server 发送消息
  4. server 回应 client
  5. 一次读写完成,连接不关闭
  6. 后续读写操作…
  7. 长时间操作之后client发起关闭请求

长连接的操作步骤是:
建立连接——数据传输…(保持连接)…数据传输——关闭连接

服务器

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Hashtable;

public class TCPTransport {
    private static final int SERVER_PORT=8883;

    private static Hashtable<String, Socket> ht = new Hashtable();

    public static void main(String[] args) throws IOException {
        // TODO Auto-generated method stub
        System.out.println("VNTCenter.main()===TCP SERVER==============");
        //新建
        ServerSocket vntServer = null;
        try{
            //监听本机的端口的地址是否有链接
            vntServer= new ServerSocket(SERVER_PORT);

            System.out.println("监听的端口 "+vntServer.getLocalPort()+"...");
            while(true){
                //在服务端调用accept()方法接受客户端的连接对象
                Socket vntClient=vntServer.accept();
                System.out.println("客户IP "+vntClient.getInetAddress()+"...");
                System.out.println("客户端口 "+vntClient.getPort()+"...");
                //新建线程
                new GetGpsThreadFun(vntClient).start();
                //进客户端ip转为String字符串,存到map中
                ht.put(vntClient.getInetAddress().toString(),vntClient);
            }
        }catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
            vntServer.close();
        }
    }
}



import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Date;

public class GetGpsThreadFun extends Thread{
    private Socket vntThreadClient;

    public GetGpsThreadFun(Socket vntThreadSocket){
        vntThreadClient=vntThreadSocket;
    }
    public void run(){
        try{
            //显示时间
            Date day=new Date();
            SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            System.out.println(df.format(day));
            byte[] data = new byte[1024];
            while(true){
                int len = vntThreadClient.getInputStream().read(data);
                if (len > 0){
                    //打印客户端IP:端口:接收的数据(字符串的转化方法)
                    System.out.println(vntThreadClient.getInetAddress()+":"+vntThreadClient.getPort()+":"+new String(data,0,len));
                    //给客户端返回TCP
                    vntThreadClient.getOutputStream().write("TCP".getBytes());
                    if("shutdown".equals(vntThreadClient)){
                        //停止本次循环
                        break;
                    }
                }
            }
        }catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        }
    }
}

短链接

模拟一种TCP短连接的情况:

  1. client 向 server 发起连接请求
  2. server 接到请求,双方建立连接
  3. client 向 server 发送消息
  4. server 回应 client
  5. 一次读写完成,此时双方任何一个都可以发起 close 操作

短连接的操作步骤是:
建立连接——数据传输——关闭连接…建立连接——数据传输——关闭连接

服务器

import java.net.ServerSocket;
import java.net.Socket;
import java.sql.Connection;

public class TCPThreadServer {
    private static final int PORT = 8883;

    public static void main(String[] args) {
        int count = 1;
        try {
            /**
             * 创建一个ServerSocket对象,并给它制定一个端口号,
             * 通过这个端口号来监听客户端的连接,服务端接受客户端连接的请求是
             * 不间断地接受的,所以服务端的编程一般都永无休止的运行
             */
            ServerSocket ss = new ServerSocket(PORT);
            System.out.println("服务器已经启动。。。");
            while (true) {
                /**
                 * 在服务端调用accept()方法接受客户端的连接对象,accept()方法是
                 * 一个阻塞式的方法,一直傻傻地等待着是否有客户端申请连接
                 */
                Socket s = ss.accept();
                System.out.println("共" + count + "次链接");
                System.out.println("现连接IP地址是:"+s.getInetAddress());
                count++;
                /** 获取当前的系统时间,与初始时间相减就是程序运行的毫秒数,除以1000就是秒数*/
                /**
                 * 服务端使用多线程方便多客户端的连接
                 * 这里将服务端的socket传给内部类,方便每个客户端都创建一个线程
                 */
                Thread t = new Thread(new TCPThreadServerSocket(s));
                t.start();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}



import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.sql.Connection;
import java.text.SimpleDateFormat;
import java.util.Date;

class TCPThreadServerSocket implements Runnable {
    private Socket socket = null;
    private InputStream in = null;
    private OutputStream out = null;
    public TCPThreadServerSocket(Socket s) {
        this.socket = s;
    }

    @Override
    public void run() {
        try {
            //获取服务端输入的消息
            in = socket.getInputStream();
            //显示时间
            Date day=new Date();
            SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            System.out.println(df.format(day));
            //服务端返回的消息
            out = socket.getOutputStream();
            //用一个字节数字来存放消息,提高效率
            byte[] recData = new byte[1024];
            int length = in.read(recData);
            String hc = new String(recData, 0, length);
            System.out.println("读取到客户端发送来的数据:" + hc);
            //返回给客户端的消息
            out.write("REG".getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                //关闭资源
                in.close();
                out.close();
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

客户端

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

public class Test {

    public static void main(String[] args) throws IOException {
        // 建立tcp服务
        Socket socket = new Socket(InetAddress.getLocalHost(), 8883);
        //Socket socket = new Socket("120.53.236.19", 8883);

        // 获取socket输出流对象
        OutputStream outputStream = socket.getOutputStream();

        // 写数据
        String data = "HY:456,-123,000";
        outputStream.write(data.getBytes());

        // 与服务器端交互
        InputStream inputStream = socket.getInputStream();
        byte[] buf = new byte[1024];
        int length = inputStream.read(buf);
        System.out.println("客户端收到" + new String(buf, 0, length));

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