基于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的那几个函数中呢?请看下图:
从图中可以看出,当客户端调用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中的四次握手释放连接的过程,请看下图:
图示过程如下:
-
某个应用进程首先调用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的简单的网络聊天程序