网络编程—TCP编程

1. 概述:计算机网络是指两台或更多的计算机组成的网络,在同一个网络中,任意两台议计算机都可以直接通信,因此所有计算机都需要遵循同一种网络协议。为了把计算机网络接入互联网,就必须使用TCP/IP协议。

TCP/IP协议泛指互联网协议,其中最重要的两个协议是 TCP协议和 IP协议。只有使用 TCP/IP协议的计算机才能够联入互联网,使用其他网络协议(例如NetBIOS、AppleTalk协议等)。

IP地址:在互联网中,一个 IP地址用于准一标识一个网络接口(NetworkInterface )。一台联入互联网的计算机肯定有一个 IP地址,但也可能有多个IP地址。

IP地址分为 IPv4和 IPv6两种。IPv4采用32位地址,类似101.202.99.12,而 IPv6采用 128位地址,类似2001:0DA8:1OOA:0OO0:0OO0:1020:F2F3:1428。 IPv4地址总共有232个(大约42亿),而 IPv6地址则总共有21z8个(大约340万亿亿亿亿),IPv4 的地址目前已耗尽,而 IPv6的地址是根本用不完的。

IP地址又分为公网IP地址和内网IP地址。公网IP地址可以直接被访问,内网IP地址只能在内网访问。内网 IP地址类似于:

192 . 168 .x.次

10 .X.X.X

(注:有一个特殊的P地址,称之为本机地址,它总是127.0.0.1。)

2. 网络模型:由于计算机网络从底层的传输到高层的软件设计十分复杂,要合理地设计计算机网络模型,必须采用分层模型,每一层负责处理自己的操作。 OSI(open SystemInterconect)网络模型是 Iso组织定义的一个计算机互联的标准模型,注意它只是一个定义,目的是为了简化网络各层的操作,提供标准接口便于实现和维护。这个模型从上到下依次是:

应用层:提供应用程序之间的通信;

表示层:处理数据格式,加解密等等;

会话层:负责建立和维护会话;

传输层:负责提供端到端的可靠传输;

网络层:负责根据目标地址选择路由来传输数据;

数据链路层和物理层:负责把数据进行分片并且真正通过物理网络传输,例如:无线网、光纤等。

3. 常用协议:IP协议是一个分组交换协议,它不保证可靠传输。而 TCP协议是传输控制协议,它是面向连接的协议,支持可靠传输和双向通信。TCP协议是建立在IP协议之上的,简单地说,IP协议只负责发数据包,不保证顺序和正确性,而TCP协协负责控制数据包传输,它在传输数据之前需要先建立连接,建立连接后才能传输数据,传输完后还需要断开连接。TCP协议之所以能保证数据的可靠传输,是通过接収确认、超时重传这些机制实现的。并且, TCP协议允许双向通信, 即通信双方可以同时发送和接收数据。

TCP协议也是应用最广泛的协议,许多高级协议都是建立在TCP协议之上的,例如HTTP、SMTP等。

UDP协议(User Datagram Protocol)是一种数据报文协议,它是无连接协议,不保证可靠传输。因为UDP协议在通信前不需要建立连接,因此它的传输效率比 TCP高,而且 UDP协议比 TCP协议要简单得多。选择 UDP协议时,传输的数据通常是能容忍丢失的,例如,一些语音视频通信的应用会选择 UDP协议。

4. 什么是Socket?

Socket是一个抽象概念,一个应用程序通过一个Socket来建立一个远程连接,而Socket内部通过TCP/IP协议把数据传输到网络。

5. 服务器端:要使用Socket编程,首先要编写服务器端程序。Java标准库提供了ServerSocket来实现对指定IP和端口的监听。ServerSocket的典型实现代码如下:

public class Server {
    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(6666); // 监听指定端口
        System.out.println("server is running...");
        while (true) {
            Socket sock = ss.accept();
            
            // 使用Socket流进行网络通信
            // ...
            
            System.out.println("connected from " + sock.getRemoteSocketAddress());
        }
    }
}

服务器端通过下面代码,在指定端口6666监听。这里我们我们没有指定IP地址,表示在计算机的所有网络接口上进行监听。

ServerSocket ss = new ServerSocket(6666);

如果ServerSocket监听成功,我们写一个死循环来处理客户端的连接

  while (true) {
      Socket sock = ss.accept();
      System.out.println("connected from " + sock.getRemoteSocketAddress());
  }

(注意:代码as.accept()表示每当有新的客户端连接进来后,就返回一个Socket实例,这个Socket实例就是用来和刚连接的客户端进行通信的)

如果没有客户端连接进来,accept()方法会阻塞并一直等待。如果有多个客户端同时连接进来,ServerSocket会把连接进来的客户端扔到队列里,一个一个处理。

6. 客户端:相比服务器端,客户端程序就简单很多

public class Client {
    public static void main(String[] args) throws IOException {
        // 连接指定服务器和端口
        Socket sock = new Socket("localhost", 6666); 
        
       // 使用Socket流进行网络通信
       // ...
        
        // 关闭
        sock.close();
        System.out.println("disconnected.");
    }
}

客户端通过下述代码,连接到服务器端,注意上述代码的服务器地址是"localhost",表示本机地址。端口号是6666。如果连接成功,将返回一个Socket实例,用于后续通信。

Socket sock = new Socket("localhost", 6666);

7. Socket流:当Socket连接成功后,无论是服务器端,还是客户端,我们都使用Socket实例进行网络通信,因为TCP是一种基于流的协议,因此,Java标准库使用InputStream和OutputStream来封装Socket的数据流,这样使用Socket的流就和普通流类似:

// 用于读取网络数据:
InputStream in = sock.getInputStream();

// 用于写入网络数据:
OutputStream out = sock.getOutputStream();

写入网络数据时,必须要调用flush()方法,如果不调用flush(),我们可能会发现,客户端和服务器端都收不到数据,这是因为我们以流的形式写入数据的时候,并不是一写就立刻发送到网络,而是先写入内存缓冲区,直到缓冲区满了以后,才会一次性真正发送到网络,这样设计的目的是为了提高传输效率。如果缓冲区的数据较少,而我们又想强制把这些数据发送到网络,就必须调用flush()强制把缓冲区数据发送出去。

8. 总结:使用Java进行TCP编程时,需要使用Socket模型

服务器端用ServerSocket监听指定端接口;

客户端使用Socket(InetAdrdress,port)连接服务器;

服务器端用accept()接收并返回Socket实例;

双方通过Socket打开InputStream/OutputStream读写数据;

flush()方法用于强制输出到网络;

服务器通常使用多线程同时处理多个客户端连接,利用线程池可大幅提升效率。

你可能感兴趣的:(网络,tcp/ip,网络协议)