基于java实现的一个hello/hi的简单的网络聊天程序

瘦欲@ 提交于 2020-11-24 06:28:43

1、  Socket的工作流程

Socket实质上提供了进程通信的端点。进程通信之前,双方首先必须各自创建一个端点,否则是没有办法建立联系并相互通信的。正如打电话之前,双方必须各自拥有一台电话机一样。

对于一个功能齐全的Socket,都要包含以下结构,其工作流程包含以下四个基本步骤:

(1)    创建Socket

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

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

(4)    关闭Socket

 

 

 

 

 

2、  java中的Socket

java中的socket通信主要通过两个已经封装好的类ServerSocket和Socket

socket通常开发的是客户端,用来让客户端通过端口号和IP地址连接到远程服务器。而ServerSocket实现的则是一个服务器应用,ServerSocket会一直等待客户端的请求,一旦获得了一个连接请求,就创建一个socket示例来与客户端进行通信。ServerSocket会绑定一个固定的端口,知晓此端口和远程服务器的客服端可以通过IP地址和端口号来与远程客户端通信,而服务端的端口则是随机的,在socket的应用中,我们并不关心客户端的端口号。

 

3、  实现

Java中的socket在底层实现时最终是通过调用系统的socket来实现的,下面是java中的socket类的继承关系

 

 

 

 

从上图中可以看出ServerSocket就是针对SocketImpl的一个外观类。SocketImple是一个抽象类,并且这个抽象类SocketImpl实现了SocketOptions接口。

 

 

                               SocketOptions接口中声明的final属性

 

 

 

                                                     SocketImpl中的属性

 

 

4、  windows和Linux中的socket

windows在windows中socket方法返回的是SOCKET 一个宏定义

如下图所示

 

 

 

 

从上方代码看出他是u_int类型的别名,一个文件描述符为什么是int呢?因为在windows编程下主要使用的是句柄,而句柄就是一串int数字这个数字标识这一个内存中的类(这里可以理解在系统中是键值对而这个int则是key,而他获取的对象则就是我们使用accept或bind所需要的),暂时可以这么理解因为即使是内存也是键值对的map,各位读者暂时将这个int当做一个key值而调用其他socket方法都是需要这个key值的没有这个key是操作不了的。



linux在linux中socket方法比windows要直接,他直接返回的是int类型

如下图所示

 

 

上面都是针对linux和windows的讲解那么在java中是怎么描述的呢?

 

 

 

 

上面的代码是从SocketImpl中的bind方法的jni实现中的,可以看出他先获取了FileDescriptor对象也就是SocketImpl的fd,然后再根据FileDescriptor获取到他的fd我们最终使用的文件描述符,而文件描述符也是int类型的,所以在java中的socket方法返回的也是int类型。



5、java中的socket和Linux的socket的区别

Socket是winsock里的原始套接字开发接口API,java是一门开发语言,而socket是一种通讯标准的简称。Linux中的socket指的就是系统底层的socket,而像java这些开发语言,因为java本身是不带有socket通讯底层实现的,所以他们所使用的socket只不过是对系统底层的Socket API进行了二次封装,面向开发人员,其本质上仍然和Linux底层的socket是同一个东西

 

6、java中的Socket用法

(1)  Socket的构造

Socket() 通过系统默认类型的 SocketImpl 创建未连接套接字

Socket(InetAddress address, int port) 创建一个流套接字并将其连接到指定 IP 地址的指定端口号。

Socket(String host, int port)

Socket(String host, int port, InetAddress localAddr, int localPort) 创建一个套接字并将其连接到指定远程主机上的指定远程端口。

 

(2)  方法摘要

 void

bind(SocketAddress bindpoint)
          将套接字绑定到本地地址。

 void

close()
          关闭此套接字。

 void

connect(SocketAddress endpoint)
          将此套接字连接到服务器。

 void

connect(SocketAddress endpoint, int timeout)
          将此套接字连接到服务器,并指定一个超时值。

 SocketChannel

getChannel()
          返回与此数据报套接字关联的唯一 SocketChannel 对象(如果有)。

 InetAddress

getInetAddress()
          返回套接字连接的地址。

 InputStream

getInputStream()
          返回此套接字的输入流。

 boolean

getKeepAlive()
          测试是否启用 SO_KEEPALIVE。

 InetAddress

getLocalAddress()
          获取套接字绑定的本地地址。

 int

getLocalPort()
          返回此套接字绑定到的本地端口。

 SocketAddress

getLocalSocketAddress()
          返回此套接字绑定的端点的地址,如果尚未绑定则返回 null。

 boolean

getOOBInline()
          测试是否启用 OOBINLINE。

 OutputStream

getOutputStream()
          返回此套接字的输出流。

 int

getPort()
          返回此套接字连接到的远程端口。

 int

getReceiveBufferSize()
          获取此 Socket 的 SO_RCVBUF 选项的值,该值是平台在 Socket 上输入时使用的缓冲区大小。

 SocketAddress

getRemoteSocketAddress()
          返回此套接字连接的端点的地址,如果未连接则返回 null。

 boolean

getReuseAddress()
          测试是否启用 SO_REUSEADDR。

 int

getSendBufferSize()
          获取此 Socket 的 SO_SNDBUF 选项的值,该值是平台在 Socket 上输出时使用的缓冲区大小。

 int

getSoLinger()
          返回 SO_LINGER 的设置。

 int

getSoTimeout()
          返回 SO_TIMEOUT 的设置。

 boolean

getTcpNoDelay()
          测试是否启用 TCP_NODELAY。

 int

getTrafficClass()
          为从此 Socket 上发送的包获取 IP 头中的流量类别或服务类型。

 boolean

isBound()
          返回套接字的绑定状态。

 boolean

isClosed()
          返回套接字的关闭状态。

 boolean

isConnected()
          返回套接字的连接状态。

 boolean

isInputShutdown()
          返回是否关闭套接字连接的半读状态 (read-half)。

 boolean

isOutputShutdown()
          返回是否关闭套接字连接的半写状态 (write-half)。

 void

sendUrgentData(int data)
          在套接字上发送一个紧急数据字节。

 void

setKeepAlive(boolean on)
          启用/禁用 SO_KEEPALIVE。

 void

setOOBInline(boolean on)
          启用/禁用 OOBINLINE(TCP 紧急数据的接收者) 默认情况下,此选项是禁用的,即在套接字上接收的 TCP 紧急数据被静默丢弃。

 void

setPerformancePreferences(int connectionTime, int latency, int bandwidth)
          设置此套接字的性能偏好。

 void

setReceiveBufferSize(int size)
          将此 Socket 的 SO_RCVBUF 选项设置为指定的值。

 void

setReuseAddress(boolean on)
          启用/禁用 SO_REUSEADDR 套接字选项。

 void

setSendBufferSize(int size)
          将此 Socket 的 SO_SNDBUF 选项设置为指定的值。

static void

setSocketImplFactory(SocketImplFactory fac)
          为应用程序设置客户端套接字实现工厂。

 void

setSoLinger(boolean on, int linger)
          启用/禁用具有指定逗留时间(以秒为单位)的 SO_LINGER。

 void

setSoTimeout(int timeout)
          启用/禁用带有指定超时值的 SO_TIMEOUT,以毫秒为单位。

 void

setTcpNoDelay(boolean on)
          启用/禁用 TCP_NODELAY(启用/禁用 Nagle 算法)。

 void

setTrafficClass(int tc)
          为从此 Socket 上发送的包在 IP 头中设置流量类别 (traffic class) 或服务类型八位组 (type-of-service octet)。

 void

shutdownInput()
          此套接字的输入流置于“流的末尾”。

 void

shutdownOutput()
          禁用此套接字的输出流。

 

7、javasocket编程-简单的hello/hi实现

基于服务器端实现的一个简单的聊天机器人,可支持多人同时聊天

效果如下:

                                       左边的是聊天服务器,右边两个是客户端

 

 

                                                            聊天恢复,第二个客户端下线

 

 

代码实现:

Demo_Receive.java:

 

 

 

Demo_Send.java:

 

 

 

 

Java中实现多线程主要有两种方法:

1、  继承Thread类,重写run()方法

2、  实现Runnable接口,重写Run()方法

这里使用的是第二种方法

 

 

作者:

SA19225475

 

 

 

 

 

参考:

https://www.jianshu.com/p/83d5598b9d3b   java中socket的实现

https://blog.csdn.net/weixin_34220834/article/details/85747263  

JAVA Socket编程和C++ Socket编程有什么不同

https://blog.csdn.net/weixin_33829657/article/details/92413466  

JAVA中的socket的继承关系

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!