网络编程学习笔记(三)TCP协议及客户端与服务端交互Demo

TCP的一些基本概念

TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的、可靠的、基于字节流(当应用层向TCP层发送用于网间传输的、用8位字节表示的数据流)的传输层通信协议。TCP为了保证不发生丢包,就给每个包一个序号,同时序号也保证了传送到接收端实体的包的按序接收。然后接收端实体对已成功收到的包发回一个相应的确认(ACK);如果发送端实体在合理的往返时延(RTT)内未收到确认,那么对应的数据包就被假设为已丢失将会被进行重传。

TCP传输涉及的对象与UDP不同,其需要通过三次握手确定连接的建立,建立连接之后进行数据的传输

连接建立——三次握手

TCP三次握手的过程如下:

  • 客户端发送SYN(SEQ=x)报文给服务器端,进入SYN_SEND状态。
  • 服务器端收到SYN报文,回应一个SYN (SEQ=y)ACK(ACK=x+1)报文,进入SYN_RECV状态。
  • 客户端收到服务器端的SYN报文,回应一个ACK(ACK=y+1)报文,进入Established状态。

三次握手完成,TCP客户端和服务器端成功地建立连接,可以开始传输数据了。

网络编程学习笔记(三)TCP协议及客户端与服务端交互Demo_第1张图片

连接断开——四次挥手
建立一个连接需要三次握手,而终止一个连接要经过四次握手,由于其是全双工,具体过程如下:

  • 某个应用进程首先调用close,称该端执行“主动关闭”(active close)。该端的TCP于是发送一个FIN分节,表示数据发送完毕。
  • 接收到这个FIN的对端执行 “被动关闭”(passive close),这个FIN由TCP确认ACK。
  • 一段时间后,接收到这个文件结束符的应用进程将调用close关闭它的套接字。这导致它的TCP也发送一个FIN。
  • 接收这个最终FIN的原发送端TCP(即执行主动关闭的那一端)确认这个FIN。
    既然每个方向都需要一个FIN和一个ACK,因此通常需要4个分节。

网络编程学习笔记(三)TCP协议及客户端与服务端交互Demo_第2张图片

TCP不同于UDP,是面向连接的,因此必须先开服务端

Socket和ServerSocket

  • 建立客户端和服务器端
  • 建立连接后,通过Socket中的IO流进行数据的传输
  • 关闭Socket

Socket:此类实现客户端套接字,套接字是两台机器间通信的端点。

构造函数:

  • Socket():通过系统默认类型的SocketImpl创建未连接套接字,可以new出对象,通过connect(SocketAddress endpoint)方法将此套接字连接到服务器,SocketAddress实际就是IP套接字地址(IP地址+端口号),也可以是(主机名+端口号),此种情况下会尝试解析主机名,如果解析失败,则改地址将被视为未解析地址。
  • Socket(InetAddress address,int port):创建一个流套接字并将其连接到指定IP地址的指定端口号
  • Socket(String host,int port):创建一个流套接字并将其连接到指定主机上的指定端口号。

ServerSocket:此类实现服务端套接字。服务器套接字等待请求通过网络传入,它基于该请求执行某些操作,然后可能想请求者返回结果。(一个服务端可以同时给多个客户端提供服务)

TCP客户端

TCP传输,客户端建立的过程
1.创建tcp客户端Socket服务,使用的是Socket对象(建议该对象创建时明确目的地,即要连接的主机)
2.如果连接建立成功,说明数据传输通道已经建立(底层帮助建立),这个通道实际就是一个流(输入流,输出流),Socket流(网络IO流)
Socket流是底层建立好的,既然是流,说明这里是既有输入,又有输出,想要输入或者输出流对象,可以找Socket来获取,通过
如下两个方法获得字节流
InputStream getInputStream():返回此套接字(Socket)的输入流
OutputStream getOutputStream():返回此套接字的输出流
3.使用输出流,将数据写出
4.关闭资源

public class TCPClientDemo {
    public static void main(String[] args)throws UnknownHostException,IOException {
        //创建客户端Socket服务(UDP中称为发送端)
        Socket socket = new Socket("127.0.0.1",10002);
        //获取Socket流中的输出流
        OutputStream out = socket.getOutputStream();
        //使用输出流将指定的数据写出去
        out.write("TCP演示例子".getBytes());//因为是字节流
        //关闭资源
        socket.close();//输出流out不用关闭,因为其是socket中的流,关闭了socket即可,关闭资源实际就是将(客户端与服务端)连接断开
    }
}

TCP服务端

建立tcp服务端的思路
1.创建服务端Socket服务,通过ServerSocket对象
2.服务端必须对外提供一个端口(如果不知道端口,即不知道让哪个程序进行解析),否则客户端无法连接
3.获取连接过来的客户端对象
4.通过客户端对象获取socket流,读取客户端发来的数据
5.关闭资源,关闭客户端,关闭服务端

public class TCPServerDemo {
    public static void main(String[] args) throws IOException{
        //创建服务端对象
        ServerSocket ss = new ServerSocket(10002);
        //获取连接过来的客户端对象
        Socket s = ss.accept();
        String ip = s.getInetAddress().getHostAddress();
        //通过Socket对象获取输入流,要读取客户端发来的数据
        InputStream in = s.getInputStream();
        byte[] buf = new byte[1024];
        int len = in.read(buf);
        String text = new String(buf,0,len);
        System.out.println(ip+":"+text);
        s.close();
        ss.close();//注意,服务端通常是不关闭的,因为在不断的提供服务
    }
}

其中Socket s = ss.accept()是阻塞式,如果没有客户端对象,会一直等待

TCP客户端与服务端交互

上述的Demo例子实现了客户端向服务端发送数据,服务端进行接收,但是实际上是双向的,本质都是利用Socket对象,利用OutputStream进行数据输出,利用InputStream进行数据读取:

网络编程学习笔记(三)TCP协议及客户端与服务端交互Demo_第3张图片

也就是说在服务端通过accept()方法获取客户端Socket对象后,可以使用OutputStream对客户端进行回写:

客户端:

public class TCPClientDemo {
    public static void main(String[] args)throws UnknownHostException,IOException {

        Socket socket = new Socket("127.0.0.1",10002);

        OutputStream out = socket.getOutputStream();

        out.write("TCP演示例子".getBytes());//因为是字节流

        //读取服务端返回的数据,使用Socket读取流
        InputStream in = socket.getInputStream();
        byte[] buf = new byte[1024];
        int len = in.read(buf);
        String text = new String(buf,0,len);
        System.out.println(text);

        socket.close();
    }
}

服务端:

public class TCPServerDemo {
    public static void main(String[] args) throws IOException{
        ServerSocket ss = new ServerSocket(10002);

        Socket s = ss.accept();//阻塞式,如果没有客户端对象,会一直等待
        String ip = s.getInetAddress().getHostAddress();

        InputStream in = s.getInputStream();
        byte[] buf = new byte[1024];
        int len = in.read(buf);
        String text = new String(buf,0,len);
        System.out.println(ip+":"+text);

        //使用客户端Socket对象的输出流给客户端返回数据
        OutputStream out = s.getOutputStream();
        out.write("收到".getBytes());

        s.close();
        ss.close();
    }
}

运行结果:

网络编程学习笔记(三)TCP协议及客户端与服务端交互Demo_第4张图片

网络编程学习笔记(三)TCP协议及客户端与服务端交互Demo_第5张图片

文本转换

需求:客户端输入字母数据,发送给服务端,服务端收到后显示在控制台,并将该数据转成大写返回给客户端,知道客户端输入“over”转换结束,即创建一个英文大写转换服务器

分析:上述提及客户端与服务端,因此使用TCP传输

客户端

public class TransClient {
    public static void main(String[] args) throws IOException {

        //创建客户端对象
        Socket s = new Socket("127.0.0.1",10004);

        //获取键盘录入
        BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));


        //将录入的信息发送给Socket输出流
        //new BufferedWriter(new OutputStreamWriter(s.getOutputStream));
        //PrintWriter是一个非常实用的输出流,PrintWriter(OutputStream out, boolean autoFlush)  通过现有的 OutputStream 创建新的 PrintWriter
        PrintWriter out = new PrintWriter(s.getOutputStream(),true);//打印流中true表示自动刷新

        String line = null;
        //Socket输入流,读取服务端返回的大写数据
        //InputStreamReader可以把InputStream转换为Reader,即是一个适配器类(adapter)
        BufferedReader burIn = new BufferedReader(new InputStreamReader(s.getInputStream()));//为了操作字符方便,使用BuffererReader


        while ((line=bufr.readLine())!=null){
            if ("over".equals(line))
                break;
            out.println(line);

            //读取服务端发回的大写数据
            String upperStr = burIn.readLine();
            System.out.println(upperStr);
        }

        s.close();

    }
}

服务端

public class TransServer {
    /*
    1.ServerSocket服务
    2.获取Socket对象
    3.源:Socket,读取客户端发过来的需要转换的数据
    4.显示在控制台上
    5.将数据转为大写并返回
     */
    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(10004);
        Socket s = ss.accept();
        //获取ip
        String ip = s.getInetAddress().getHostAddress();
        System.out.println(ip+"......connected");
        //获取Socket读取流并装饰
        BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
        //获取Socket的输出流并装饰
        PrintWriter out = new PrintWriter(s.getOutputStream(),true);
        String line =  null;
        while ((line=bufIn.readLine())!=null){
            System.out.println(line);
            out.println(line.toUpperCase());
        }
        s.close();
        ss.close();
    }
}

网络编程学习笔记(三)TCP协议及客户端与服务端交互Demo_第6张图片

网络编程学习笔记(三)TCP协议及客户端与服务端交互Demo_第7张图片

你可能感兴趣的:(网络编程)