java通信其实入门还是很容易的,会ServerSocket、socket和IO流就行了。
一、Java通信的原理、概念
某台设备的进程的端口<------>服务器<------->某台设备的进程的端口
无论什么进程,都会在电脑上开启一个或多个端口。不管是QQ这类交互软件,还是杀毒软件对其他应用的操作,通信的本质就是这台电脑上的进程端口与另一台(或本台)电脑上进程端口之间的交互。
此外,手机也可以与电脑进行交互。比如说我后来实现的手机控制电脑,本质上就是手机上的一个进程对电脑的一个进程进行通信。
那么如何精准定位到是这个手机(电脑)和这台电脑(其他设备)呢?
IP地址和端口。可以说,一个ip地址能确定一台电脑,端口则能确定这台电脑上的一个进程。而IP地址又有局域网ip和公网ip,区别就是局域网ip只能在同个局域网里相互访问,而公网ip则不受此限制。
查看局域网ip:Windows系统cmd中输入“ipconfig”,
Linux或Mac系统输入“ifconfig”。
查看公网ip:浏览器地址输入” http://ip.chinaz.com/getip.aspx”。
当然,方法还有许多。
其实,一台电脑是可以直接与另一台电脑进行通信(不需要服务端),但我们这里不提,我们要用服务端来作为中转站。
二、实现:
1、 服务端开启一个(或多个)端口号,来供客户端连接。
ServerSocket ss1 = new ServerSocket(7779);
ServerSocket ss2 = new ServerSocket(7778)
注意:括号里是端口号(port),在同一台设备上不能被同时使用。这里可以用1024到 65535,一定要确保你使用的端口号没被其他进程占用。可以在cmd下输入netstat查看端口的使用情况。
2、 接着使用socket阻塞,来等待客户端连接。
Socket conn1=ss1.accept();
Socket conn2=ss2.accept();
思考一下:
(1) Socket conn1= ss2.accept();
Socket conn2= ss1.accept();
(2) Socket conn1= ss1.accept();
Socket conn2= ss1.accept();
(3) Socket conn1= ss2.accept();
(4) Socket conn1= ss1.accept();
Socket conn2= ss2.accept();
Socket conn3= ss1.accept();
上述都是可行的,ServerSocket与Socket之间并没有数量限制,根据情况来使用。只是 要注意服务端要与客户端socket连接一一对应好。
3、 服务端并不只是为一个客户端服务的,它应该能接受许多客户端的连接。那要怎么做呢?我们可以用多线程来实现。
while(true){
Socket conn1= ss1.accept();
Socket conn2= ss2.accept();
Client client=new Client(conn1,conn2);
Thread th=new Thread(client);
th.start();
}
4、 当一个客户端连接到服务端的时候,socket将它们进行了连接,那要怎么成功通信呢?我们要用到IO流。IO的相关类有很多,我建议用DataInputStream和DataOutputStream。其实别的也很不错,建议选择一两个精用就行了。
DataInputStream dis1 = new DataInputStream(conn1.getInputStream());
DataInputStream dis2 = new DataInputStream(conn2.getInputStream());
DataOutputStream dos1 = new DataOutputStream(conn1.getOutputStream());
DataOutputStream dos2 = new DataOutputStream(conn2.getOutputStream());
它们提供的方法,主要是传递相应的基本类型和string,还是挺好用的。
DataInputStream提供的方法: write(); writeChar(); writeInt(); writeFloat(); writeDouble(); writeUTF();等等
DataOutputStream提供的方法: read(); readChar(); readInt(); readFloat(); readDouble(); readUTF();等等
这些write方法要注意的是,最好在写入完成后加上flush()方法。
示例:
dis.writeInt(size);
dis.write(数组);
dis.writeUTF(“Success!”);
dis.flush();
而各种read()方法跟ServerSocket的accept()方法相似,都是堵塞状态,只有当读取到数据时,才会往下执行代码。
通信的内容无非是 数字、字符、字符串、图片、音乐、视频等等。前边的基础类型直接传就行了,而后边的多媒体类型先转化为基础类型数组,再进行传输。转化并不难,几行代码就解决了。因为情况太多种,这里就不赘述了,自行百度吧。
最后,说一下IO流的关闭方法。对象名.close(); 就行了。
5、我们在实现以上代码的时候,基本都要用try()和catch()或throw来捕获异常,这部分代码加上就是了。
当客户端意外退出或正常退出时,假如你用的软件拼命报错,你可以看一下catch语句中是否有e.printfstack();之类的代码。去掉就行了。
但这肯定不是治本的,你要根据具体情况(即你的需求)来关闭IO流、关闭socket、甚至直接close对应的线程。常用的方法是通过改变某个参数+判断来回避一些代码的执行。
6、 有那么多的客户端,服务端要如何来辨识并且精准地建立两者或多者之间的联系呢?
每个客户端在连接到服务端后可以output一条独特的消息,或者服务端通过客户端的ip地址来辨别。
在不用数据库的情况下,可以采用数组或ArrayList来存储接入的客户端。要特别注意一下,当客户端退出时,要在相应的数组或队列中移除该客户端。
7、 在代码、技术之外,更重要的是要知道目的是什么,思路是什么,实现的步骤或措施是什么,什么时候要output、input,顺序如何弄,通信的步骤是否对应。要想弄出能服务于千万甚至上亿级别的程序,一定要想好各种异常情况,并进行处理。除了提高程序的容错性外,还要在封装上多注意,学习设计模式,加强代码的可读性和易改性。
8、socket在多种语言上都是可用的,IO流可能是有问题的(滑稽),比如InputStream提供的方法write()、read()中,传参的是int,但真正传的是int的后八位。又如读取字符串的一些方法,也是可能有问题的,在Android上,问题更多了。所以,提醒一下,类提供的方法不总是可靠的,别太相信了。想实现java通信也不是很难的事,别怂!
9、java通信能实现什么产品,我最后来抛砖引玉一下。产品一定要动手做,不然就不算学。只要是程序与程序之间的交互都能用到java通信。比如在线游戏、仿聊天室,又比如我后来写的手机控制电脑。(其实我就是想给我下篇技术文章打广告,嘎嘎)