在学习Java网络编程
之前,我们需要了解关于网络通信的一些概念:计算机网络
、网络通信协议
、网络协议的分层
、数据封装与解封
、IP地址
、端口
、URL
、Socket
;
TCP协议
和UDP协议
是传输层的两种协议。Socket
是传输层供给应用层的编程接口,所以Socket
编程就分为TCP编程
和UDP编程
两类;
Java
为了可移植性,不允许直接调用操作系统,而是由java.net
包来提供网络功能,Java虚拟机
负责与操作系统的实际连接,本文介绍了java.net
包中常用的类。
基本概念 计算机网络
计算机网络是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统。
从其中我们可以提取到以下内容:
计算机网络的作用:资源共享和信息传递。
计算机网络的组成:
计算机网络的多台计算机是具有独立功能的,而不是脱离网络就无法存在的。
网络通信协议
国际标准化组织(ISO
,即International Organization for Standardization
)定义了网络通信协议的基本框架,被称为OSI (Open System Interconnect
,即开放系统互联)模型。要制定通讯规则,内容会很多,比如要考虑A电脑如何找到B电脑,A电脑在发送信息给B电脑时是否需要B电脑进行反馈,A电脑传送给B电脑的数据格式又是怎样的?内容太多太杂,所以OSI
模型将这些通讯标准进行层次划分,每一层次解决一个类别的问题,这样就使得标准的制定没那么复杂。OSI模型制定的七层标准模型,分别是:应用层
,表示层
,会话层
,传输层
,网络层
,数据链路层
,物理层
。
OSI七层协议模型如下图所示:
虽然国际标准化组织制定了这样一个网络通信协议的模型,但是实际上互联网通讯使用最多的网络通信协议是TCP/IP
网络通信协议。
TCP/IP
是一个协议族,也是按照层次划分,共四层:应用层
,传输层
,互连网络层
,网络接口层
(物理
+数据链路层
)。
那么TCP/IP协议
和OSI模型
有什么区别呢?OSI网络通信协议模型
,是一个参考模型,而TCP/IP协议是事实上的标准。TCP/IP协议参考了OSI模型,但是并没有严格按照OSI规定的七层标准去划分,而只划分了四层,这样会更简单点,当划分太多层次时,你很难区分某个协议是属于哪个层次的。TCP/IP协议和OSI模型也并不冲突,TCP/IP协议中的应用层协议
,就对应于OSI中的应用层
,表示层
,会话层
。就像以前有工业部和信息产业部,现在实行大部制后只有工业和信息化部一个部门,但是这个部门还是要做以前两个部门一样多的事情,本质上没有多大的差别。TCP/IP中有两个重要的协议,传输层的TCP协议和互连网络层的IP协议,因此就拿这两个协议做代表,来命名整个协议族了,再说TCP/IP协议时,是指整个协议族。
网络协议的分层
由于网络结点之间联系很复杂,在制定协议时,把复杂成份分解成一些简单的成份,再将它们复合起来。最常用的复合方式是层次方式
,即同层间可以通信、上一层可以调用下一层,而与再下一层不发生关系。
把用户应用程序作为最高层,把物理通信线路作为最低层,将其间的协议处理分为若干层,规定每层处理的任务,也规定每层的接口标准。
ISO模型
与TCP/IP模型
的对应关系如下图所示:
数据封装与解封
由于用户传输的数据一般都比较大,有的可以达到MB字节,一次性发送出去十分困难,于是就需要把数据分成许多片段,再按照一定的次序发送出去。这个过程就需要对数据进行封装
。
数据封装 (Data Encapsulation)是指将协议数据单元
(PDU
)封装在一组协议头和协议尾中的过程。在OSI七层参考模型中,每层主要负责与其它机器上的对等层进行通信。该过程是在协议数据单元(PDU)中实现的,其中每层的PDU一般由本层的协议头、协议尾和数据封装构成。
数据发送处理过程
应用层将数据交给传输层,传输层添加上TCP的控制信息(称为TCP头部),这个数据单元称为段(Segment),加入控制信息的过程称为封装。然后,将段交给网络层。
网络层接收到段,再添加上IP头部,这个数据单元称为包(Packet)。然后,将包交给数据链路层。
数据链路层接收到包,再添加上MAC头部和尾部,这个数据单元称为帧(Frame)。然后,将帧交给物理层。
物理层将接收到的数据转化为比特流,然后在网线中传送。
数据接收处理过程
物理层接收到比特流,经过处理后将数据交给数据链路层。
数据链路层将接收到的数据转化为数据帧,再除去MAC头部和尾部,这个除去控制信息的过程称为解封,然后将包交给网络层。
网络层接收到包,再除去IP头部,然后将段交给传输层。
传输层接收到段,再除去TCP头部,然后将数据交给应用层。
从以上传输过程中,可以总结出以下规则:
发送方数据处理的方式是从高层到底层,逐层进行数据封装。
接收方数据处理的方式是从底层到高层,逐层进行数据解封装。
接收方的每一层只把对该层有意义的数据拿走,或者说每一层只能处理发送方同等层的数据,然后把其余的部分传递给上一层,这就是对等层通信
的概念。
数据封装与解封如下图所示:
IP地址
用来标识网络中的一个通信实体的地址。通信实体可以是计算机、路由器等。 比如互联网的每个服务器都要有自己的IP地址,而每个局域网的计算机要通信也要配置IP地址。路由器是连接两个或多个网络的网络设备。
目前主流使用的IP地址是IPV4
,但是随着网络规模的不断扩大,IPV4面临着枯竭的危险,所以推出了IPV6
。
IPV4 :32位地址,并以8位为一个单位,分成四部分,以点分十进制表示,如192.168.0.1。因为8位二进制的计数范围是00000000—11111111,对应十进制的0-255,所以-4.278.4.1是错误的IPV4地址。
IPV6 :128位(16个字节)写成8个16位的无符号整数,每个整数用四个十六进制位表示,每个数之间用冒号(:)分开,如:3ffe:3201:1401:1280:c8ff:fe4d:db39:1984
注意事项 :
127.0.0.1 本机地址
192.168.0.0–192.168.255.255为私有地址,属于非注册地址,专门为组织机构内部使用。
端口
IP地址用来标识一台计算机,但是一台计算机上可能提供多种网络应用程序,如何来区分这些不同的程序呢?这就要用到端口
。
端口是虚拟的概念,并不是说在主机上真的有若干个端口。通过端口,可以在一个主机上运行多个网络应用程序。 端口的表示是一个16位的二进制整数,对应十进制的0-65535。
Oracle、MySQL、Tomcat、QQ、msn、迅雷、电驴、360等网络程序都有自己的端口。
总结 :
IP地址好比每个人的地址(门牌号),端口好比是房间号。必须同时指定IP地址和端口号才能够正确的发送数据。
IP地址好比为电话号码,而端口号就好比为分机号。
URL
在www
上,每一信息资源都有统一且唯一的地址,该地址就叫URL
(Uniform Resource Locator),它是www的统一资源定位符。URL由4部分组成:协议 、存放资源的主机域名、资源文件名和端口号。如果未指定该端口号,则使用协议默认的端口。例如http协议
的默认端口为 80。 在浏览器中访问网页时,地址栏显示的地址就是URL。
在java.net
包中提供了URL
类,该类封装了大量复杂的涉及从远程站点获取信息的细节。
Socket
我们开发的网络应用程序位于应用层,TCP
和UDP
属于传输层协议,在应用层如何使用传输层的服务呢?在应用层和传输层之间,则是使用套接Socket 来进行分离。
套接字
就像是传输层为应用层开的一个小口,应用程序通过这个小口向远程发送数据,或者接收远程发来的数据;而这个小口以内,也就是数据进入这个口之后,或者数据从这个口出来之前,是不知道也不需要知道的,也不会关心它如何传输,这属于网络其它层次工作。
Socket
实际是传输层供给应用层的编程接口。Socket就是应用层与传输层之间的桥梁。使用Socket编程可以开发客户机和服务器应用程序,可以在本地网络上进行通信,也可通过Internet在全球范围内通信。
TCP/UDP协议 联系与区别
TCP协议
和UDP协议
是传输层的两种协议。Socket
是传输层供给应用层的编程接口,所以Socket编程就分为TCP编程
和UDP编程
两类。
在网络通讯中,TCP
方式就类似于拨打电话,使用该种方式进行网络通讯时,需要建立专门的虚拟连接,然后进行可靠的数据传输,如果数据发送失败,则客户端会自动重发该数据。而UDP方式就类似于发送短信,使用这种方式进行网络通讯时,不需要建立专门的虚拟连接,传输也不是很可靠,如果发送失败则客户端无法获得。
这两种传输方式都在实际的网络编程中使用,重要的数据一般使用TCP方式进行数据传输,而大量的非核心数据则可以通过UDP方式进行传递,在一些程序中甚至结合使用这两种方式进行数据传递。
由于TCP
需要建立专用的虚拟连接以及确认传输是否正确,所以使用TCP
方式的速度稍微慢一些,而且传输时产生的数据量要比UDP
稍微大一些。
总结 :
TCP
是面向连接的,传输数据安全,稳定,效率相对较低。
UDP
是面向无连接的,传输数据不安全,效率较高。
TCP协议
TCP
(Transfer Control Protocol)是面向连接的,所谓面向连接,就是当计算机双方通信时必需经过先建立连接,然后传送数据,最后拆除连接三个过程。
TCP在建立连接时又分三步走:
是请求端(客户端)发送一个包含SYN即同步(Synchronize)标志的TCP报文,SYN同步报文会指明客户端使用的端口以及TCP连接的初始序号。
服务器在收到客户端的SYN报文后,将返回一个SYN+ACK的报文,表示客户端的请求被接受,同时TCP序号被加一,ACK即确认(Acknowledgement)。
客户端也返回一个确认报文ACK给服务器端,同样TCP序列号被加一,到此一个TCP连接完成。然后才开始通信的第二步:数据处理。
这就是所说的TCP的三次握手 (Three-way Handshake)。
UDP协议
基于TCP
协议可以建立稳定连接的点对点的通信。这种通信方式实时、快速、安全性高,但是很占用系统的资源。
在网络传输方式上,还有另一种基于UDP
协议的通信方式,称为数据报通信方式。在这种方式中,每个数据发送单元被统一封装成数据报包的方式,发送方将数据报包发送到网络中,数据报包在网络中去寻找它的目的地。
Java网络编程
Java为了可移植性,不允许直接调用操作系统,而是由java.net
包来提供网络功能。Java虚拟机
负责提供与操作系统的实际连接。下面我们来介绍几个java.net包中的常用的类。
InetAddress
作用:封装计算机的IP地址和DNS(没有端口信息)。
注 :DNS是Domain Name System,域名系统。
特点:这个类没有构造方法。如果要得到对象,只能通过静态方法:getLocalHost()、getByName()、 getAllByName()、 getAddress()、getHostName()。
例1:使用getLocalHost方法创建InetAddress对象
1 2 3 4 5 6 7 8 9 10 11 import java.net.InetAddress;import java.net.UnknownHostException;public class Test1 { public static void main (String[] args) throws UnknownHostException { InetAddress addr = InetAddress.getLocalHost(); System.out.println(addr.getHostAddress()); System.out.println(addr.getHostName()); } }
例2:根据域名得到InetAddress对象
1 2 3 4 5 6 7 8 9 10 11 import java.net.InetAddress;import java.net.UnknownHostException;public class Test2 { public static void main (String[] args) throws UnknownHostException { InetAddress addr = InetAddress.getByName("www.sxt.cn" ); System.out.println(addr.getHostAddress()); System.out.println(addr.getHostName()); } }
例3:根据IP得到InetAddress对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import java.net.InetAddress;import java.net.UnknownHostException;public class Test3 { public static void main (String[] args) throws UnknownHostException { InetAddress addr = InetAddress.getByName("59.110.14.7" ); System.out.println(addr.getHostAddress()); System.out.println(addr.getHostName()); } }
InetSocketAddress
作用:包含IP和端口信息,常用于Socket通信。此类实现 IP 套接字地址(IP 地址 + 端口号),不依赖任何协议。
例:InetSocketAddress的使用
1 2 3 4 5 6 7 8 9 import java.net.InetSocketAddress;public class Test4 { public static void main (String[] args) { InetSocketAddress socketAddress = new InetSocketAddress("127.0.0.1" , 8080 ); InetSocketAddress socketAddress2 = new InetSocketAddress("localhost" , 9000 ); System.out.println(socketAddress.getHostName()); System.out.println(socketAddress2.getAddress()); } }
URL类
IP
地址唯一标识了Internet上的计算机,而URL
则标识了这些计算机上的资源。类URL
代表一个统一资源定位符,它是指向互联网“资源”的指针。资源可以是简单的文件或目录,也可以是对更为复杂的对象的引用,例如对数据库或搜索引擎的查询。
为了方便程序员编程,JDK中提供了URL类,该类的全名是java.net.URL
,有了这样一个类,就可以使用它的各种方法来对URL对象进行分割、合并等处理。
例1:URL类的使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import java.net.MalformedURLException;import java.net.URL;public class Test5 { public static void main (String[] args) throws MalformedURLException { URL u = new URL("http://www.google.cn:80/webhp#aa?canhu=33" ); System.out.println("获取与此url关联的协议的默认端口:" + u.getDefaultPort()); System.out.println("getFile:" + u.getFile()); System.out.println("主机名:" + u.getHost()); System.out.println("路径:" + u.getPath()); System.out.println("端口:" + u.getPort()); System.out.println("协议:" + u.getProtocol()); System.out.println("参数部分:" + u.getQuery()); System.out.println("锚点:" + u.getRef()); URL u1 = new URL("http://www.abc.com/aa/" ); URL u2 = new URL(u, "2.html" ); System.out.println(u2.toString()); } }
例2:最简单的网络爬虫
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.net.MalformedURLException;import java.net.URL; public class Test6 { public static void main (String[] args) { basicSpider(); } static void basicSpider () { URL url = null ; InputStream is = null ; BufferedReader br = null ; StringBuilder sb = new StringBuilder(); String temp = "" ; try { url = new URL("http://www.baidu.com" ); is = url.openStream(); br = new BufferedReader(new InputStreamReader(is)); while ((temp = br.readLine()) != null ) { sb.append(temp); } System.out.println(sb); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { br.close(); } catch (IOException e) { e.printStackTrace(); } try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } }
基于TCP协议的Socket编程和通信
在网络通讯中,第一次主动发起通讯的程序被称作客户端(Client)程序,简称客户端
,而在第一次通讯中等待连接的程序被称作服务器端(Server)程序,简称服务器
。一旦通讯建立,则客户端和服务器端完全一样,没有本质的区别。
“请求-响应”模式 :
Socket类:发送TCP消息。
ServerSocket类:创建服务器。
套接字
是一种进程间的数据交换机制。这些进程既可以在同一机器上,也可以在通过网络连接的不同机器上。换句话说,套接字起到通信端点的作用。单个套接字是一个端点,而一对套接字则构成一个双向通信信道,使非关联进程可以在本地或通过网络进行数据交换。一旦建立套接字连接,数据即可在相同或不同的系统中双向或单向发送,直到其中一个端点关闭连接。套接字与主机地址和端口地址相关联。主机地址就是客户端或服务器程序所在的主机的IP地址。端口地址是指客户端或服务器程序使用的主机的通信端口。
在客户端和服务器中,分别创建独立的Socket,并通过Socket的属性,将两个Socket进行连接,这样,客户端和服务器通过套接字所建立的连接使用输入输出流进行通信。
TCP/IP套接字
是最可靠的双向流协议,使用TCP/IP可以发送任意数量的数据。
实际上,套接字只是计算机上已编号的端口。如果发送方和接收方计算机确定好端口,他们就可以通信了。
如下图所示为客户端与服务器端的通信关系图:
TCP/IP通信连接的简单过程 :
位于A计算机上的TCP/IP
软件向B计算机发送包含端口号的消息,B计算机的TCP/IP
软件接收该消息,并进行检查,查看是否有它知道的程序正在该端口上接收消息。如果有,他就将该消息交给这个程序。
要使程序有效地运行,就必须有一个客户端和一个服务器。
通过Socket的编程顺序 :
创建服务器ServerSocket,在创建时,定义ServerSocket的监听端口(在这个端口接收客户端发来的消息)。
ServerSocket调用accept()方法,使之处于阻塞状态。
创建客户端Socket,并设置服务器的IP及端口。
客户端发出连接请求,建立连接。
分别取得服务器和客户端Socket的InputStream和OutputStream。
利用Socket和ServerSocket进行数据传输。
关闭流及Socket。
TCP:单向通信Socket之服务器端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 import java.io.BufferedWriter;import java.io.IOException;import java.io.OutputStreamWriter;import java.net.ServerSocket;import java.net.Socket; public class BasicSocketServer { public static void main (String[] args) { Socket socket = null ; BufferedWriter bw = null ; try { ServerSocket serverSocket = new ServerSocket(8888 ); System.out.println("服务端建立监听" ); socket = serverSocket.accept(); bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); bw.write("hhhh" ); } catch (IOException e) { e.printStackTrace(); } finally { if (bw != null ) { try { bw.close(); } catch (IOException e) { e.printStackTrace(); } } if (socket != null ) { try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
TCP:单向通信Socket之客户端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.net.InetAddress;import java.net.Socket;public class BasicSocketClient { public static void main (String[] args) { Socket socket = null ; BufferedReader br = null ; try { socket = new Socket(InetAddress.getLocalHost(), 8888 ); br = new BufferedReader(new InputStreamReader(socket.getInputStream())); System.out.println(br.readLine()); } catch (Exception e) { e.printStackTrace(); } finally { if (br != null ) { try { br.close(); } catch (IOException e) { e.printStackTrace(); } } if (socket != null ) { try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
TCP:双向通信Socket之服务器端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 import java.io.BufferedReader;import java.io.BufferedWriter;import java.io.IOException;import java.io.InputStreamReader;import java.io.OutputStreamWriter;import java.net.ServerSocket;import java.net.Socket; public class Server { public static void main (String[] args) { Socket socket = null ; BufferedReader in = null ; BufferedWriter out = null ; BufferedReader br = null ; try { ServerSocket server = new ServerSocket(8888 ); socket = server.accept(); in = new BufferedReader(new InputStreamReader(socket.getInputStream())); out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); br = new BufferedReader(new InputStreamReader(System.in)); while (true ) { String str = in.readLine(); System.out.println("客户端说:" + str); String str2 = "" ; if (str.equals("end" )){ break ; } str2 = br.readLine(); out.write(str2 + "\n" ); out.flush(); } } catch (IOException e) { e.printStackTrace(); } finally { if (in != null ){ try { in.close(); } catch (IOException e) { e.printStackTrace(); } } if (out != null ){ try { out.close(); } catch (IOException e) { e.printStackTrace(); } } if (br != null ){ try { br.close(); } catch (IOException e) { e.printStackTrace(); } } if (socket != null ){ try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
TCP:双向通信Socket之客户端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 import java.io.BufferedReader;import java.io.BufferedWriter;import java.io.IOException;import java.io.InputStreamReader;import java.io.OutputStreamWriter;import java.net.InetAddress;import java.net.Socket;import java.net.UnknownHostException; public class Client { public static void main (String[] args) { Socket socket = null ; BufferedReader in = null ; BufferedWriter out = null ; BufferedReader wt = null ; try { socket = new Socket(InetAddress.getLocalHost(), 8888 ); in = new BufferedReader(new InputStreamReader(socket.getInputStream())); out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); wt = new BufferedReader(new InputStreamReader(System.in)); while (true ) { String str = wt.readLine(); out.write(str + "\n" ); out.flush(); if (str.equals("end" )) { break ; } System.out.println("服务器端说:" + in.readLine()); } } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (out != null ) { try { out.close(); } catch (IOException e) { e.printStackTrace(); } } if (in != null ) { try { in.close(); } catch (IOException e) { e.printStackTrace(); } } if (wt != null ) { try { wt.close(); } catch (IOException e) { e.printStackTrace(); } } if (socket != null ) { try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
执行结果如下图所示:
注意
运行时,要先启动服务器端,再启动客户端,才能得到正常的运行效果。
但是,上面这个程序,必须按照安排好的顺序,服务器和客户端一问一答!不够灵活!!可以使用多线程实现更加灵活的双向通讯!!
TCP:聊天室之服务器端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 import java.io.BufferedReader;import java.io.BufferedWriter;import java.io.IOException;import java.io.InputStreamReader;import java.io.OutputStreamWriter;import java.net.ServerSocket;import java.net.Socket; public class ChatServer { public static void main (String[] args) { ServerSocket server = null ; Socket socket = null ; BufferedReader in = null ; try { server = new ServerSocket(8888 ); socket = server.accept(); new ServerThread(socket).start(); in = new BufferedReader(new InputStreamReader(socket.getInputStream())); while (true ) { String str = in.readLine(); System.out.println("客户端说:" + str); } } catch (IOException e) { e.printStackTrace(); } finally { try { if (in != null ) { in.close(); } } catch (IOException e) { e.printStackTrace(); } try { if (socket != null ) { socket.close(); } } catch (IOException e) { e.printStackTrace(); } } } } class ServerThread extends Thread { Socket ss; BufferedWriter out; BufferedReader br; public ServerThread (Socket ss) { this .ss = ss; try { out = new BufferedWriter(new OutputStreamWriter(ss.getOutputStream())); br = new BufferedReader(new InputStreamReader(System.in)); } catch (IOException e) { e.printStackTrace(); } } public void run () { try { while (true ) { String str2 = br.readLine(); out.write(str2 + "\n" ); out.flush(); } } catch (IOException e) { e.printStackTrace(); } finally { try { if (out != null ){ out.close(); } } catch (IOException e) { e.printStackTrace(); } try { if (br != null ){ br.close(); } } catch (IOException e) { e.printStackTrace(); } } } }
TCP:聊天室之客户端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 import java.io.BufferedReader;import java.io.BufferedWriter;import java.io.IOException;import java.io.InputStreamReader;import java.io.OutputStreamWriter;import java.net.InetAddress;import java.net.Socket;import java.net.UnknownHostException; public class ChatClient { public static void main (String[] args) { Socket socket = null ; BufferedReader in = null ; try { socket = new Socket(InetAddress.getByName("127.0.1.1" ), 8888 ); new ClientThread(socket).start(); in = new BufferedReader(new InputStreamReader(socket.getInputStream())); while (true ) { System.out.println("服务器说:" + in.readLine()); } } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { if (socket != null ) { socket.close(); } } catch (IOException e) { e.printStackTrace(); } try { if (in != null ) { in.close(); } } catch (IOException e) { e.printStackTrace(); } } } } class ClientThread extends Thread { Socket s; BufferedWriter out; BufferedReader wt; public ClientThread (Socket s) { this .s = s; try { out = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); wt = new BufferedReader(new InputStreamReader(System.in)); } catch (IOException e) { e.printStackTrace(); } } public void run () { try { while (true ) { String str = wt.readLine(); out.write(str + "\n" ); out.flush(); } } catch (IOException e) { e.printStackTrace(); } finally { try { if (wt != null ) { wt.close(); } } catch (IOException e) { e.printStackTrace(); } try { if (out != null ) { out.close(); } } catch (IOException e) { e.printStackTrace(); } } } }
执行结果如下图所示:
UDP通讯的实现 DatagramSocket 用于发送或接收数据报包
当服务器要向客户端发送数据时,需要在服务器端产生一个DatagramSocket
对象,在客户端产生一个DatagramSocket对象。服务器端的DatagramSocket将DatagramPacket发送到网络上,然后被客户端的DatagramSocket接收。
DatagramSocket有两种常用的构造函数
。一种是无需任何参数的,常用于客户端;另一种需要指定端口,常用于服务器端。如下所示:
常用方法 :
DatagramPacket 数据容器(封包)的作用
此类表示数据报包。 数据报包用来实现封包的功能。
常用方法 :
DatagramPacket(byte[] buf, int length) :构造数据报包,用来接收长度为 length 的数据包。
DatagramPacket(byte[] buf, int length, InetAddress address, int port) :构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。
getAddress() :获取发送或接收方计算机的IP地址,此数据报将要发往该机器或者是从该机器接收到的。
getData() :获取发送或接收的数据。
setData(byte[] buf) :设置发送的数据。
UDP通信编程基本步骤:
创建客户端的DatagramSocket,创建时,定义客户端的监听端口。
创建服务器端的DatagramSocket,创建时,定义服务器端的监听端口。
在服务器端定义DatagramPacket对象,封装待发送的数据包。
客户端将数据报包发送出去。
服务器端接收数据报包。
UDP:单向通信之客户端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import java.net.DatagramPacket;import java.net.DatagramSocket;import java.net.InetSocketAddress; public class Client { public static void main (String[] args) throws Exception { byte [] b = "北京尚学堂" .getBytes(); DatagramPacket dp = new DatagramPacket(b,b.length,new InetSocketAddress("localhost" ,8999 )); DatagramSocket ds = new DatagramSocket(9000 ); ds.send(dp); ds.close(); } }
UDP:单向通信之服务器端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import java.net.DatagramPacket;import java.net.DatagramSocket; public class Server { public static void main (String[] args) throws Exception { DatagramSocket ds = new DatagramSocket(8999 ); byte [] b = new byte [1024 ]; DatagramPacket dp = new DatagramPacket(b, b.length); ds.receive(dp); String string = new String(dp.getData(), 0 , dp.getLength()); System.out.println(string); ds.close(); } }
执行结果如下图所示:
通过字节数组流ByteArrayInputStream
、ByteArrayOutputStream
与数据流DataInputStream
、DataOutputStream
联合使用可以传递基本数据类型。
UDP:基本数据类型的传递之客户端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 import java.io.ByteArrayOutputStream;import java.io.DataOutputStream;import java.net.DatagramPacket;import java.net.DatagramSocket;import java.net.InetSocketAddress; public class Client { public static void main (String[] args) throws Exception { long n = 2000L ; ByteArrayOutputStream bos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(bos); dos.writeLong(n); byte [] b = bos.toByteArray(); DatagramPacket dp = new DatagramPacket(b,b.length,new InetSocketAddress("localhost" ,8999 )); DatagramSocket ds = new DatagramSocket(9000 ); ds.send(dp); dos.close(); bos.close(); ds.close(); } }
UDP:基本数据类型的传递之服务器端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import java.io.ByteArrayInputStream;import java.io.DataInputStream;import java.net.DatagramPacket;import java.net.DatagramSocket; public class Server { public static void main (String[] args) throws Exception { DatagramSocket ds = new DatagramSocket(8999 ); byte [] b = new byte [1024 ]; DatagramPacket dp = new DatagramPacket(b, b.length); ds.receive(dp); ByteArrayInputStream bis = new ByteArrayInputStream(dp.getData()); DataInputStream dis = new DataInputStream(bis); System.out.println(dis.readLong()); dis.close(); bis.close(); ds.close(); } }
执行结果如下图所示:
通过字节数组流ByteArrayInputStream
、ByteArrayOutputStream
与数据流ObjectInputStream
、ObjectOutputStream
联合使用可以传递对象。
UDP:对象的传递之Person类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import java.io.Serializable;public class Person implements Serializable { private static final long serialVersionUID = 1L ; int age; String name; public Person (int age, String name) { super (); this .age = age; this .name = name; } @Override public String toString () { return "Person [age=" + age + ", name=" + name + "]" ; } }
UDP:对象的传递之客户端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import java.io.ByteArrayOutputStream;import java.io.ObjectOutputStream;import java.net.DatagramPacket;import java.net.DatagramSocket;import java.net.InetSocketAddress; public class Client { public static void main (String[] args) throws Exception { Person person = new Person(18 , "高淇" ); ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(person); byte [] b = bos.toByteArray(); DatagramPacket dp = new DatagramPacket(b,b.length,new InetSocketAddress("localhost" ,8999 )); DatagramSocket ds = new DatagramSocket(9000 ); ds.send(dp); oos.close(); bos.close(); ds.close(); } }
UDP:对象的传递之服务器端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import java.io.ByteArrayInputStream;import java.io.ObjectInputStream;import java.net.DatagramPacket;import java.net.DatagramSocket; public class Server { public static void main (String[] args) throws Exception { DatagramSocket ds = new DatagramSocket(8999 ); byte [] b = new byte [1024 ]; DatagramPacket dp = new DatagramPacket(b, b.length); ds.receive(dp); ByteArrayInputStream bis = new ByteArrayInputStream(dp.getData()); ObjectInputStream ois = new ObjectInputStream(bis); System.out.println(ois.readObject()); ois.close(); bis.close(); ds.close(); } }
执行结果如下图所示: