网络编程之TCP

什么是七层协议?

IOS(国际标准委员会组织)将数据的传递从逻辑上划分了以下七层
应用层、表示层、会话层、传输层、网络层、数据链据层、物理层

当发送消息时,需要按照上述从前往后的次序对发送的内容进行层层加包,最后发送出去
当接收消息时,需要按照上述相反的次序对发送的内容进行层层拆包,最后解析出来

常见协议

协议:就是一种约定/规则,是通信双方需要遵循的一种机制

http:超文本传输协议,浏览网站时使用该协议
ftp:文件传输协议,上传下载文件时候使用该协议
tcp:传输控制协议,是一种面向连接的协议
udp:用户数据报协议,是一种非面向连接的协议
ip:互联网协议,是上述协议的底层协议

端口号

IP地址可以定位到具体的一台设备
端口号可以定位到设备上具体的进程
在网络编程中需要提供:IP地址,端口号

端口号:本质上是由16位二进制组成的整数,范围是:0~65535,其中0~1024之间的端口以及被系统占用,因此编程端口号从1025开始

Socket

Socket套接字:
网络上具有唯一标识的IP地址和端口号组合在一起才能构成唯一能识别的标识符套接字

Socket原理机制:
通信的两端都有Socket
网络通信其实就是Socket间的通信
数据在两个Socket间通过IO传输

InetAddress

在JDK中提供了一个与IP地址相关的InetAddress类,该类用于封装一个IP地址,并提供了一系列与IP地址相关的方法

 

 

 

其中,前两个方法用于获得该类的实例对象,第一个方法用于获得表示指定主机的InetAddress对象,第2个方法用于获得表示本地的InetAddress对象。通过InetAddress对象便可获取指定主机名、IP地址等。

接下来通过一个案例来演示InetAddress常用方法的使用

import java.net.InetAddress;
public class Example01 {
    public static void main(String[] args) throws Exception {
        InetAddress host = InetAddress.getLocalHost();
        InetAddress byName = InetAddress.getByName("www.baidu.com");
        System.out.println("本机的IP地址:"+host.getHostAddress());
        System.out.println("百度的IP地址:"+byName.getHostAddress());
        System.out.println("3秒内是否可达:"+byName.isReachable(3000));
        System.out.println("本机的主机名是:"+host.getHostName());
        System.out.println("百度的主机名是:"+byName.getHostName());
    }
}

TCP通信

面向连接、安全可靠,效率稍低,通过三次握手建立连接

在JDK中提供了两个用于实现TCP程序的类,一个是ServerSocket类,用于表示服务器端,一个Socket类,用于表示客户端。通信时,首先要创建代表服务器端ServerSocket对象,创建该对象相当于开启了一个服务,此服务会等待客户端的连接;然后创建代表客户端的Socket对象,使用该对象向服务器端发出连接请求,服务器端响应请求后,两者才建立连接,开始通信。

ServerSocket(int port)

使用该构造方法在创建Serversocket对象时,可以将其绑定到一个指定的端口号上(参数port就是端口号)。端口号可以指定为0,此时系统就会分配一个还没被其他网络程序所使用的的端口号。由于客户端需要指定的端口号来访问服务器端程序,因此端口号随机分配的情况并不常用,通常都会让服务器端程序监听一个指定的端口号。

 

 

ServerSocket对象负责监听某台计算机的某个端口号,在创建ServerSocket对象后,需要继续调用该对象的accept()方法,接收来自客户端的请求。当执行了accept()方法之后,服务器端程序会发生阻塞,直到客户端发出连接请求时,accept()方法才会返回一个Socket对象,用于和客户端实现通信

Socket(InetAddress address,int port)

参数address用于接收一个InetAddress 类型的对象,该对象用于封装一个IP地址

 

 

 

Socket类的常用方法,其中,getInputStream()和getOutputStream()方法分别用于获取输入流和输出流。当客户端和服务端建立连接后,数据是以IO流的形式进行交互的,从而实现通信。

接下来通过一张图来描述服务器端和客户端的数据传输

 

 

 

简单的TCP网络程序

通过前面两个小节了解到ServerSocket、Socket类的基本用法。为了让初学者更好地掌握这两个类的使用,接下来通过一个TCP通信的案例来进一步学习这两个类的用法

要实现TCP通信需要创建一个服务器端程序和一个客户端程序,为了保证数据传输的安全,首先需要实现服务端程序。

建立TCP服务端的思路

  1. 创建ServerSocket类的想,并提供端口号
  2. 等待客户端连接,使用accept()方法
  3. 等Socket对象,并使用输入输出流进行通信
  4. 关闭相互资源

public class Server {
    private static final int PORT=8888;        //定义一个端口号
    public static void main(String[] args) throws Exception {
        //创建ServerSocket对象
        ServerSocket ss = new ServerSocket(PORT);  
        //调用accpet()等待客户端的连接
        Socket s = ss.accept();        
        //获取客户端的输出流
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));  
        System.out.println("开始与客户端交互数据");    
        //当客户端接入后,向客户端输出数据
        bw.write("我是服务器,你好客户端!"); 
        //模拟置信其他功能占用的时间
        Thread.sleep(5000);         
        System.out.println("结束与客户端交互数据");
        bw.flush();
        s.close();
        bw.close();
    }
}

在创建ServerSocket对象时指定了端口号,并调用该对象的accept()方法。从运行结果可以看出,控制台中的光标一直在闪动,这是因为accept()方法发生阻塞,程序暂时停止运行,直到客户端来访问时才会结束这种阻塞状态。这时该方法会返回一个Socket类型的对象用于表示客户端,通过该对象获取与客户端关联的输出流,并向客户端发送消息。最后调用Socket对象的close()方法将通信关闭。

建立TCP客户端的思路

  1. 创建Socket类型的对象,并制定服务器的IP地址和端口号
  2. 使用输入输出流进行通信
  3. 关闭相互资源

接下来编写与客户端的程序。

public class Client {
    private static final int PORT=8888;        //定义一个端口号
    public static void main(String[] args) throws Exception {
        //创建一个Socket并连接到给出的地址和端口号的计算机
        Socket s = new Socket("192.168.96.1", PORT);
        //得到接收数据的流
        BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
        //读取数据
        String r = br.readLine();
        //打印读取到的数据
        System.out.println(r);
        //关闭资源
        br.close();
    }
}

在客户端创建Socket对象与服务器端建立连接后,通过Socket对象获得输入流读取服务端发来的数据,并打印结果,结束本次通信。

多线程的TCP网络程序

实际上,很多服务器端程序都是允许被多个应用程序访问的。例如门户网站可以被多个用户访问,因此服务器都是多线程的。下面通过一个图例来表示多个用户访问同一个服务器。

 

 图中代表的是多个客户端访问同一个服务器端,服务器端为每个客户端创建一个对象的Socket,并且开启一个新的线程使两个Socket建立专线进行通信,接下来对TCP通信的服务端进行改进。

public class Server {
    private static final int PORT=8888;        //定义一个端口号
    public static void main(String[] args) throws Exception{
        //创建ServerSocket对象
        ServerSocket ss = new ServerSocket(PORT);  
        //使用while()循环不停的接收客户端发送的请求
        while(true) {
            //调用accpet()等待客户端的连接
            Socket s = ss.accept();        
            //下面用代码开启一个新线程
            new Thread() {
                public void run() {
                    try {
                        //获取客户端的输出流
                        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));  
                        System.out.println("开始与客户端交互数据");    
                        //当客户端接入后,向客户端输出数据
                        bw.write("我是服务器,你好客户端!"); 
                        //模拟置信其他功能占用的时间
                        Thread.sleep(5000);         
                        System.out.println("结束与客户端交互数据");
                        bw.flush();
                        s.close();
                        bw.close();
                    } catch (Exception e) { 
                        e.printStackTrace();
                    }
                }
            }.start();
        }
    }
}

使用多线程的方式创建了一个服务器端程序。通过在while循环中调用accept()方法,不停地接收客户端发送的请求,而主线程仍然处于等待状态。

posted @ 2019-10-15 21:21  tunan96  阅读(215)  评论(0编辑  收藏  举报