基于JAVA套接字的简单网络聊天程序

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于JAVA套接字的简单网络聊天程序相关的知识,希望对你有一定的参考价值。

网络中进程之间如何通信

本地的进程间通信(IPC)有很多种方式,但可以总结为下面4类:

  • 消息传递(管道、FIFO、消息队列)
  • 同步(互斥量、条件变量、读写锁、文件和写记录锁、信号量)
  • 共享内存(匿名的和具名的)
  • 远程过程调用(Solaris门和Sun RPC)

但这些都不是本文的主题!我们要讨论的是网络中进程之间如何通信?首要解决的问题是如何唯一标识一个进程,否则通信无从谈起!在本地可以通过进程PID来唯一标识一个进程,但是在网络中这是行不通的。其实TCP/IP协议族已经帮我们解决了这个问题,网络层的“ip地址”可以唯一标识网络中的主机,而传输层的“协议+端口”可以唯一标识主机中的应用程序(进程)。这样利用三元组(ip地址,协议,端口)就可以标识网络的进程了,网络中的进程通信就可以利用这个标志与其它进程进行交互。

使用TCP/IP协议的应用程序通常采用应用编程接口:UNIX  BSD的套接字(socket)和UNIX System V的TLI(已经被淘汰),来实现网络进程之间的通信。就目前而言,几乎所有的应用程序都是采用socket,而现在又是网络时代,网络中进程通信是无处不在,这就是为什么说“一切皆socket”。

TCP简介

  1.TCP介绍

    a>TCP协议:TCP协议,传输控制协议(英语:Transmission Control Protocol,缩写为:TCP)是一种面向连接的、可靠的、基于字节流的通信协议

    1.面向连接:先连接,再通信,好比打电话模型

    2.可靠的,相对于UDP,TCP传输更可靠,TCP通过一序列的机制(面向连接机制、发送应答机制)来保障传输的可靠性

    3.基于字节流的,UDP创建UDP socket——DGRAM:基于数据报通信方式,每一次发送的数据都是一个独立的整体,包含目标主机的ip地址、端口号及发送数据的内容

    b>TCP通信的三个步骤

    基于面向连接的:1.与服务端建立连接

            2.收发数据

  

 

 

          3.关闭连接

  2.TCP特点

    a>面向连接

    1.先建立连接,再进行通信

    2.TCP连接是一对一的,而UDP可以一对一或一对多,UDP适合做广播程序

    a>可靠传输:通过一序列机制来保障TCP传输数据比UDP更可靠

    1.传送应答机制

    2.超时重传机制

    3.错误校验

    4.流量控制/阻塞管理

JavaSocket编程

        在Java环境下,Socket编程主要是指基于 TCP/IP协议 的网络编程。

 

        Socket工作过程包含以下四个步骤:

  (1) 创建Socket;

  (2) 打开连接到Socket的输入/输出流;

  (3) 按照一定的协议对Socket进行读/写操作;

  (4) 关闭Socket

 

java中的Socket类主要包括两个:Socket(客户端)和 ServerSocket(服务器端)

1)Socket 类

    Socket 类:该类实现客户端套接字,套接字指的是两台设备之间通讯的端点。 构造方法public Socket(String host, int port) :创建套接字对象并将其连接到指定主机上的指定端口号。如果指 定的host是null ,则相当于指定地址为回送地址。
    成员方法 :

         public InputStream getInputStream() : 返回此套接字的输入流。如果此Scoket具有相关联的通道,则生成的InputStream 的所有操作也关联该通道。 关闭生成的InputStream也将关闭相关的Socket。

    public OutputStream getOutputStream() : 返回此套接字的输出流。 如果此Scoket具有相关联的通道,则生成的OutputStream 的所有操作也关联该通道。 关闭生成的OutputStream也将关闭相关的Socket。
    public void close() :关闭此套接字。 一旦一个socket被关闭,它不可再使用。 关闭此socket也将关闭相关的InputStream和OutputStream 。
    public void shutdownOutput() : 禁用此套接字的输出流。 任何先前写出的数据将被发送,随后终止输出流。

 

 


  2)ServerSocket类
  ServerSocket 类:这个类实现了服务器套接字,该对象等待通过网络的请求。

  构造方法

 

 

     public ServerSocket(int port) :使用该构造方法在创建ServerSocket对象时,就可以将其绑定到一个指定的端口号上,参数port就是端口号。

    构造举例,代码如下: ServerSocket server = new ServerSocket(6666);

  成员方法

    public Socket accept() :侦听并接受连接,返回一个新的Socket对象,用于和客户端实现通信。该方法
会一直阻塞直到建立连接。

socket中TCP的三次握手建立连接详解

我们知道tcp建立连接要进行“三次握手”,即交换三个分组。大致流程如下:

  • 客户端向服务器发送一个SYN J
  • 服务器向客户端响应一个SYN K,并对SYN J进行确认ACK J+1
  • 客户端再想服务器发一个确认ACK K+1

只有就完了三次握手,但是这个三次握手发生在socket的那几个函数中呢?请看下图:

image

 

从图中可以看出,当客户端调用connect时,触发了连接请求,向服务器发送了SYN J包,这时connect进入阻塞状态;服务器监听到连接请求,即收到SYN J包,调用accept函数接收请求向客户端发送SYN K ,ACK J+1,这时accept进入阻塞状态;客户端收到服务器的SYN K ,ACK J+1之后,这时connect返回,并对SYN K进行确认;服务器收到ACK K+1时,accept返回,至此三次握手完毕,连接建立。

客户端的connect在三次握手的第二个次返回,而服务器端的accept在三次握手的第三次返回。

 

socket中TCP的四次握手释放连接详解

上面介绍了socket中TCP的三次握手建立过程,及其涉及的socket函数。现在我们介绍socket中的四次握手释放连接的过程,请看下图:

image

 

图示过程如下:

  • 某个应用进程首先调用close主动关闭连接,这时TCP发送一个FIN M;
  • 另一端接收到FIN M之后,执行被动关闭,对这个FIN进行确认。它的接收也作为文件结束符传递给应用进程,因为FIN的接收意味着应用进程在相应的连接上再也接收不到额外数据;
  • 一段时间之后,接收到文件结束符的应用进程调用close关闭它的socket。这导致它的TCP也发送一个FIN N;
  • 接收到这个FIN的源发送端TCP对它进行确认。

这样每个方向上都有一个FIN和ACK。

Java的Socket库函数与Linux的Socket系统调用之间的区别:

Java通过JVM实现了优良的跨平台性,同一段写好的代码,移植到不同的操作系统上可直接执行,不受操作系统的限制,一处编译,到处执行,JAVA的SOCKET编程的底层原理是JVM将JAVA程序解析出来的参数传递给所对应的C++程序,由C++执行完之后回传给JAVA。

而LINUX自带的SOCKET编程只能再LINUX操作系统下使用,同时JAVA与LINUX的API也不一致。

一个小例子:

服务端代码:

package ray;
import java.io.*;
import java.net.*;
public class Server {
    private static ServerSocket server;

    public static void main(String[] args) throws IOException{
        server = new ServerSocket(1100);
        System.out.println("服务器启动成功,等待用户接入...");
        //等待用户接入,直到有用户接入为止,Socket对象表示客户端
        Socket client = server.accept();
        //得到接入客户端的IP地址
        System.out.println("有客户端接入,客户IP:" + client.getInetAddress());
        InputStream in = client.getInputStream();//从客户端生成网络输入流,用于接收来自网络的数据
        
        OutputStream out = client.getOutputStream();//从客户端生成网络输出流,用来把数据发送到网络上

        byte[] bt = new byte[1024];//定义一个字节数组,用来存储网络数据
        int len = in.read(bt);//将网络数据写入字节数组
        String data = new String(bt, 0 , len);//将网络数据转换为字符串数据
        System.out.println("来自客户端的消息:" + data);
        out.write("我是服务器,欢迎光临".getBytes());//服务器端数据发送(以字节数组形式)
        client.close();//关闭套接字
    }
}

客户端代码:

package ray;
import java.io.*;
import java.net.*;
public class Client {
    private static Socket client;

    public static void main(String[] args) throws UnknownHostException, IOException {
        client = new Socket("127.0.0.1", 1100);
        System.out.println("连接服务器成功");
        InputStream in = client.getInputStream();//从客户端生成网络输入流,用于接收网络数据
        OutputStream out = client.getOutputStream();//从客户端生成网络输出流,用来发送数据
        out.write("我是客户端,欢迎光临".getBytes());//客户端数据发送
        byte[] bt = new byte[1024];//定义一个字节数组,用来存储网络数据
        int len = in.read(bt);//将网络数据写入字节数组
        String data = new String(bt, 0 ,len);//将网络数据转换为字符串数据
        System.out.println("来自服务器的消息:" + data);
        client.close();//关闭套接字
    }
}

运行结果展示:

服务器端:

 

 

 

 

 

 

 

  

以上是关于基于JAVA套接字的简单网络聊天程序的主要内容,如果未能解决你的问题,请参考以下文章

Java网络编程基础-- 基于TCP/IP的Socket编程

基于Python完成一个hello/hi的简单的网络聊天程序

基于java的socket简单聊天编程

利用java的Socket实现一个简单hello/hi聊天程序

udp网络程序-发送接收数据

udp网络程序-发送接收数据