通过基于java实现的网络聊天程序分析java中网络API和Linux Socket API关系

Posted zhouz

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了通过基于java实现的网络聊天程序分析java中网络API和Linux Socket API关系相关的知识,希望对你有一定的参考价值。

1. 引言

  socket网络编程,可以指定不同的通信协议,在这里,我们使用TCP协议实现基于java的C/S模式下“hello/hi”网络聊天程序

2. 目标

  1). 通过该网络聊天程序,了解java Socket API接口的基本用法

  2). java Socket API简要介绍

  3). linux socket API 简单分析

  4). tcp协议的连接和终止

  5). 探究java socket API 和 linux socket api之间的关系

3. linux socket API简单分析

   技术图片

   1). 简要介绍一下 tcp协议

    tcp协议提供了可靠的面向连接的字节流运输层服务协议, 它将用户数据打包生成一个报文段发送出去,同时启动一个定时器;接收端则会对数据进行确认,重排失序的数据,并丢弃重复数据。

    tcp面向连接,意味着每次发送数据要先在两端间建立连接; 终止发送也要关闭连接

    a). 建立连接

      客户端: 请求发送方;  服务端: 数据接收方

      tcp通过三次握手来建立连接

      客户端先发送一个报文段,执行主动打开 -->  服务器发回一个SYN报文段作为应答 --> 客户端再发送一个报文段对服务器的应答进行确认

    b). 终止连接

      tcp需要四次握手来关闭连接,因为tcp连接时全双工,两个方向上的都要关闭,而每次的关闭都需要确认,由此需要四次握手

  2). API 分析

      a). 建立和终止

      客户端在调用connect()方法时候,会进行三次握手来建立连接,只有连接建立成功/失败的时候才会返回

      在此之前,服务端会一直阻塞等待客户端的请求

      当连接建立成功的时候,客户端和服务端进行数据传输

      传输结束后,客户端调用close()方法,

    b). bind

      会将本地协议地址赋予一个套接口

    c). listen

      当服务端调用socket(),会假定这是一个主动连接(即将调用connect发起连接的客户套接口),而listen()则将其转为被动连接,让内核接收指向该套接口的连接请求

      内核维护两个队列:未完成连接队列,已完成连接队列

    d). accept

      当客户端connect的时候,会在服务端的未完成连接队列中创建一个条目,当连接完成时候,将该条目移动到已完成连接队列中

      accept则从已完成连接队列中拿到一个已完成连接

4. 基于java的“hello/hi”网络聊天程序的简单实现

 1 import java.io.*;
 2 import java.net.ServerSocket;
 3 import java.net.Socket;
 4 
 5 public class Server {
 6     public static void main(String[] args) throws Exception {
 7         System.out.println("服务端启动 , 等待连接 .... ");
 8         // 1. 创建 ServerSocket对象,绑定端口,开始等待连接
 9         ServerSocket server = new ServerSocket(6666);
10         // 2. 接收连接 accept 方法, 返回 socket 对象; 此方法在连接传入之前一直阻塞,返回新的套接字
11         Socket socket = server.accept();
12         // 3. 获取输入流
13         InputStream is = socket.getInputStream();
14         // 4. 一次性读取数据
15         byte[] b = new byte[1024];
16         int len = is.read(b);
17         // 解析数组
18         String msg = new String(b, 0, len);
19         System.out.println("From Client-> "+msg);
20 
21         // 5. 获取输出流, 向客户端写入数据
22         OutputStream out = socket.getOutputStream();
23         out.write("Hi".getBytes());
24         // 关闭资源
25         out.close();
26         is.close();
27         server.close();
28     }
29 }
 1 import java.io.*;
 2 import java.net.Socket;
 3 
 4 public class Client {
 5     public static void main(String[] args) throws Exception {
 6         System.out.println("客户端 发送数据");
 7         // 1. 创建 Socket , 确定连接到哪里
 8         Socket client = new Socket("localhost", 6666);
 9         // 2. 获取输出流
10         OutputStream os = client.getOutputStream();
11         // 3. 写出数据
12         os.write("Hello".getBytes());
13         // 4. 获取输入流,获取服务端返回的信息
14         InputStream in = client.getInputStream();
15 
16         // 5.读取服务端返回的数据
17         byte[] b = new byte[100];
18         int len = in.read(b);
19         System.out.println("From Sever-> "+new String(b, 0, len));
20         // 5. 关闭资源
21         in.close();
22         os.close();
23         client.close();
24     }
25 }

5. java socket API 和 linux socket api 关系探究

  由上面的程序,我们可以观察到java中 server端相比linux下少了bind()和listen()两个方法, 客户端也没有connect()方法

  我们看一下Java和Linux Sever端API 对应关系,下面是网上的一张图,网络来源(https://blog.csdn.net/vipshop_fin_dev/article/details/102966081)

  技术图片

 

 

     当运行在linux系统上的时候,jvm会调用linux底层socket()函数,我们比较一下服务端的对应API

  a. ServerSocket server = new ServerSocket(6666);  会先创建一个socket,而其底层(对应的linux api)是实现了下面三个过程:

    1. 创建socket

      若要执行网络I/O,进程首先就要调用一个socket函数,并会指定一个期望的协议类型和套接口类型

      socket函数成功的时候会返回一个小的非负整数, 因在Linux中一切皆文件,所以这个整数表示一个文件描述符

      此时会创建socket对应的结构,并将其和一个已经打开的文件对应

    2. 将对应socket和本地协议地址对应(bind函数)

      对于TCP协议,bind函数可以和一个IP和一个端口绑定;此时该套接口处于TCP_CLOSE状态

      如果不绑定端口号,则内核会为该套接口绑定一个临时的端口

      如果不绑定IP地址,对于服务端而言,内核就会将客户端发送的SYN的目的IP地址作为服务器的源IP地址

    3. listen()监听

      由上面可以知道,listen会将主动转为被动连接,会将套接口由closed状态转换为linsten状态;并且维护了两个队列

      此时会和客户端进行三次握手来建立连接

      技术图片

  b. Socket socket = server.accept();

 

 

     linux中由TCP服务器调用,从已完成的连接队列的队头返回下一个已完成连接;成功则会返回由内核创建的新的描述符;

    在并发服务器中,accept()返回后,服务器会调用fork函数,创建一个子进程,由该子进程来和客户端进行通讯,此时套接口对应文件描述符的引用计数会增加

    同时父进程会关闭该已连接套接字,即会将引用计数减少。然后在原有的监听套接口上,继续监听有无其他连接,当由连接的时候,再次accept处理连接

  c. server.close();

    当待关闭的socket的描述符的引用计数为0的时候才会真正的关闭连接

 

参考来源:

  1. https://blog.csdn.net/vipshop_fin_dev/article/details/102966081

  2. 《UNIX网络编程》

以上是关于通过基于java实现的网络聊天程序分析java中网络API和Linux Socket API关系的主要内容,如果未能解决你的问题,请参考以下文章

基于Vue+Java实现的在线聊天APP系统设计与实现

java网络聊天软件开发,要基于UDP的

深度学习面试题18:网中网结构(Network in Network)

深度学习面试题18:网中网结构(Network in Network)

Java爬虫原理分析

项目日志之基于Java socket的网络通讯