- JAVAOOP-20 网络编程
-
- 一. 网络编程概念
-
- OSI参考模型
- 二. UDP,TCP通讯协议
- 三.TCP网络编程
- 四.UDP网络编程
- 五. URL网络编程
JAVAOOP-20 网络编程
一. 网络编程概念
计算机网络是通过传输介质、通信设施和网络通信协议,把分散在不同地点的计算机设备互连起来的,实现资源共享和数据传输的系统。网络编程就是编写程序使互联网的两个(或多个)设备(如计算机)之间进行数据传输。Java语言对网络编程提供了良好的支持。通过其提供的接口我们可以很方便地进行网络编程。
网络体系结构:
通过网络发送数据是一项复杂的操作,必须仔细地协调网络的物理特性以及所发送数据的逻辑特征。通过网络将数据从一台主机发送到另外的主机,这个过程是通过计算机网络通信来完成。
OSI参考模型
这里首先介绍OSI参考模型。OSI模型把网络通信的工作分为7层,分别是物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。
基础层:物理层(physical)、数据链路层(Datalink)、网络层(network).。
传输层(Transport):TCP-UDP协议层、Socket。
高级层::会话层(Session)、表示层(Presentation)、应用层(Application)
网络通信的不同方面被分解为多个层,层与层之间用接口连接。通信的双方具有相同的层次,层次实现的功能由协议数据单元(PDU)来描述。不同系统中的同一层构成对等层,对等层之间通过对等层协议进行通信,理解批次定义好的规则和约定。每一层表示为物理硬件(即线缆和电流)与所传输信息之间的不同抽象层次。在理论上,每一层只与紧挨其上和其下的层对话。将网络分层,这样就可以修改甚至替换某一层的软件,只要层与层之间的接口保持不变,就不会影响到其他层。
Java的网络编程主要涉及到的内容是Socket编程。Socket,套接字,就是两台主机之间逻辑连接的端点。TCP/IP协议是传输层协议,主要解决数据如何在网络中传输,而HTTP是应用层协议,主要解决如何包装数据。Socket是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元。它是网络通信过程中端点的抽象表示,包含进行网络通信必须的五种信息:连接使用的协议、本地主机的IP地址、本地进程的协议端口、远程主机的IP地址、远程进程的协议端口。
应用层通过传输层进行数据通信时,TCP会遇到同时为多个应用程序进程提供并发服务的问题。多个TCP连接或多个应用程序进程可能需要通过同一个TCP协议端口传输数据。为了区别不同的应用程序进程和连接,许多计算机操作系统为应用程序与TCP/IP协议交互提供了套接字(Socket)接口。应用层可以和传输层通过Socket接口,区分来自不同应用程序进程或网络连接的通信,实现数据传输的并发服务。
Socket,实际上是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API),通过Socket,我们才能使用TCP/IP协议。实际上,Socket跟TCP/IP协议没有必然的关系,Socket编程接口在设计的时候,就希望也能适应其他的网络协议。所以说,Socket的出现,只是使得程序员更方便地使用TCP/IP协议栈而已,是对TCP/IP协议的抽象,从而形成了我们知道的一些最基本的函数接口,比如create、listen、accept、send、read和write等等。网络有一段关于socket和TCP/IP协议关系的说法比较容易理解:
“TCP/IP只是一个协议栈,就像操作系统的运行机制一样,必须要具体实现,同时还要提供对外的操作接口。这个就像操作系统会提供标准的编程接口,比如win32编程接口一样,TCP/IP也要提供可供程序员做网络开发所用的接口,这就是Socket编程接口。”
二. UDP,TCP通讯协议
如同人与人之间相互交流是需要遵循一定的规则(如语言)一样,计算机之间能够进行相互通信是因为它们都共同遵守一定的规则,即网络协议。
**TCP协议(Transmission Control Protocol) ** 传输控制协议 比如打电话
TCP是面向连接的协议,因此每个TCP连接都有3个阶段:连接建立、数据传送和连接释放。连接建立经历三个步骤,通常称为“三次握手”。
? 1.第一次握手(客户端发送请求)
- 第二次握手(服务端回传确认)
- ? 第三次握手(客户端回传确认)
**UDP协议(User Datagram Protocol)**用户数据报协议 比如 快递 发短信
UDP,用户数据报协议,它是TCP/IP协议簇中无连接的运输层协议。
-
UDP是一个非连接的协议,传输数据之前源端和终端不建立连接,当它想传送时就简单地去抓取来自应用程序的数据,并尽可能快地把它扔到网络上。在发送端,UDP传送数据的速度仅仅是受应用程序生成数据的速度、计算机的能力和传输带宽的限制;在接收端,UDP把每个消息段放在队列中,应用程序每次从队列中读一个消息段。
-
由于传输数据不建立连接,因此也就不需要维护连接状态,包括收发状态等,因此一台服务器可同时向多个客户端传输相同的消息。
-
UDP是面向报文的。
为 了方便理解这两种方式,还是先来看一个例子。大家使用手机时,向别人传递信息时有两种方式:拨打电话和发送短信。使用拨打电话的方式可以保证将信息传递给 别人,因为别人接听电话时本身就确认接收到了该信息。而发送短信的方式价格低廉,使用方便,但是接收人有可能接收不到。
在网络通讯中,TCP方式就类似于拨打电话,使用该种方式进行网络通讯时,需要建立专门的虚拟连接,然后进行可靠的数据传输,如果数据发送失败,则客户端会自动重发该数据。而UDP方式就类似于发送短信,使用这种方式进行网络通讯时,不需要建立专门的虚拟连接,传输也不是很可靠,如果发送失败则客户端无法获得。
这两种传输方式都是实际的网络编程中进行使用,重要的数据一般使用TCP方式进行数据传输,而大量的非核心数据则都通过UDP方式进行传递,在一些程序中甚至结合使用这两种方式进行数据的传递。
由于TCP需要建立专用的虚拟连接以及确认传输是否正确,所以使用TCP方式的速度稍微慢一些,而且传输时产生的数据量要比UDP稍微大一些。
如何表示网络上的某一台计算机:
? 通过IP地址来表示,每台主机都有一个唯一的IP地址
如何 表示某台计算机上面的某个应用程序:
? 通过端口号来表示
通过IP地址+端口号 直接点位到网络上某台计算机上面的某个应用程序
三.TCP网络编程
服务器端(Server)是指在网络编程中被动等待连接的程序,服务器端一般实现程序的核心逻辑以及数据存储等核心功能。服务器端的编程步骤和客户端不同,是由四个步骤实现,依次是:
1、 监听端口
服务器端属于被动等待连接,所以服务器端启动以后,不需要发起连接,而只需要监听本地计算机的某个固定端口即可。
这个端口就是服务器端开放给客户端的端口,服务器端程序运行的本地计算机的IP地址就是服务器端程序的IP地址。
2、 获得连接
当客户端连接到服务器端时,服务器端就可以获得一个连接,这个连接包含客户端的信息,例如客户端IP地址等等,服务器端和客户端也通过该连接进行数据交换。
一般在服务器端编程中,当获得连接时,需要开启专门的线程处理该连接,每个连接都由独立的线程实现。
3、 交换数据
服务器端通过获得的连接进行数据交换。服务器端的数据交换步骤是首先接收客户端发送过来的数据,然后进行逻辑处理,再把处理以后的结果数据发送给客户端。简单来说,就是先接收再发送,这个和客户端的数据交换数序不同。
其实,服务器端获得的连接和客户端连接是一样的,只是数据交换的步骤不同。
当然,服务器端的数据交换也是可以多次进行的。
在数据交换完成以后,关闭和客户端的连接。
4、 关闭连接
当服务器程序关闭时,需要关闭服务器端,通过关闭服务器端使得服务器监听的端口以及占用的内存可以释放出来,实现了连接的关闭。
? 其实服务器端编程的模型和呼叫中心的实现是类似的,例如移动的客服电话10086就是典型的呼叫中心,当一个用户拨打10086时,转接给一个专门的客服人员,由该客服实现和该用户的问题解决,当另外一个用户拨打10086时,则转接给另一个客服,实现问题解决,依次类推。
? 在服务器端编程时,10086这个电话号码就类似于服务器端的端口号码,每个用户就相当于一个客户端程序,每个客服人员就相当于服务器端启动的专门和客户端连接的线程,每个线程都是独立进行交互的。
? 这就是服务器端编程的模型,只是TCP方式是需要建立连接的,对于服务器端的压力比较大,而UDP是不需要建立连接的,对于服务器端的压力比较小罢了。
-
**Socket**
:此类实现客户端套接字(也可以就叫“套接字”)。套接字是两台机器间通信的端点。
构造方法 | 说明 |
---|---|
[Socket](…/…/java/net/Socket.html#Socket(java.lang.String, int))(String host, int port) | 创建一个流套接字并将其连接到指定 IP 地址的指定端口号。 |
[Socket](…/…/java/net/Socket.html#Socket(java.net.InetAddress, int))(InetAddress address, int port) | 创建一个流套接字并将其连接到指定 IP 地址的指定端口号。 |
普通方法 | 说明 |
---|---|
void close() | 关闭此套接字。 |
InputStream getInputStream() | 返回此套接字的输入流。 |
InetAddress getInetAddress() | 返回套接字连接的地址。 |
OutputStream getOutputStream() | 返回此套接字的输出流。 |
int getPort() | 返回此套接字连接到的远程端口。 |
InetAddress getLocalAddress() | 获取套接字绑定的本地地址。 |
int getLocalPort() | 返回此套接字绑定到的本地端口。 |
ServerSocket:
? 此类实现服务器套接字。服务器套接字等待请求通过网络传入。它基于该请求执行某些操作,然后可能向请求者返回结果。
构造方法 | 说明 |
---|---|
ServerSocket(int port) | 创建绑定到特定端口的服务器套接字。 |
普通方法 | 说明 |
---|---|
Socket accept() | 侦听并接受到此套接字的连接。 |
void close() | 关闭此套接字。 |
InetAddress getInetAddress() | 返回此服务器套接字的本地地址。 |
int getLocalPort() | 返回此套接字在其上侦听的端口。 |
SocketAddress getLocalSocketAddress() | 返回此套接字绑定的端点的地址,如果尚未绑定则返回 null。 |
注意: 需要先启动服务器端,再启动客户端
客户端:
import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;public class HelloClient {
public static void main(String[] args) throws UnknownHostException, IOException {
//1.建立连接Socket client=new Socket("127.0.0.1",8888);//2.传输数据PrintWriter pw=new PrintWriter(client.getOutputStream());pw.print("hello! Server!!!");pw.flush();//3.关闭连接if(pw!=null){
pw.close();}if(client!=null){
client.close();}}}
服务器端:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;public class HelloServer {
public static void main(String[] args) throws IOException {
//1.监听连接ServerSocket server=new ServerSocket(8888);//2.接受并建立连接Socket s=server.accept();//3.交换数据BufferedReader reader=new BufferedReader(new InputStreamReader(s.getInputStream()));String str=reader.readLine();System.out.println("来自客户端的消息是: "+str);//4.关闭连接if(reader!=null){
reader.close();}if(s!=null){
s.close();}}}
demo02:
客户端:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;public class TestTCPClient {
public static void main(String[] args) throws UnknownHostException, IOException {
Socket client=new Socket("127.0.0.1",8889);//表示本机ip的表示方式: 1.实际IP 2. localhost 3.127.0.0.1//向服务器发消息PrintWriter pw=new PrintWriter(client.getOutputStream());pw.println("How are you !!! Server..");pw.flush();//接收来自服务器的消息BufferedReader br=new BufferedReader(new InputStreamReader(client.getInputStream()));System.out.println("来自服务器的消息是: "+br.readLine());if(pw!=null){
pw.close();}if(br!=null){
br.close();}if(client!=null){
client.close();}}}
服务器端:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;public class TestTCPServer {
public static void main(String[] args) throws IOException {
ServerSocket server=new ServerSocket(8889);Socket s=server.accept();//接收来自客户端的消息BufferedReader br=new BufferedReader(new InputStreamReader(s.getInputStream()));String str=br.readLine();System.out.println("来自客户端的消息是:"+str);System.out.println("客户端的IP地址是: "+s.getInetAddress()+"端口号是: "+s.getPort());//向客户端回消息PrintWriter pw=new PrintWriter(s.getOutputStream());pw.println("I'm fine !Thank you ! client........");pw.flush();if(br!=null){
br.close();}if(pw!=null){
pw.close();}if(s!=null){
s.close();}}}
四.UDP网络编程
DatagramSocket:
? 此类表示用来发送和接收数据报包的套接字。
? 数据报套接字是包投递服务的发送或接收点。
构造方法 | 说明 |
---|---|
DatagramSocket() | 构造数据报套接字并将其绑定到本地主机上任何可用的端口。 |
DatagramSocket(int port) | 创建数据报套接字并将其绑定到本地主机上的指定端口。 |
普通方法 | 说明 |
---|---|
void close() | 关闭此数据报套接字。 |
void send(DatagramPacket p) | 从此套接字发送数据报包。 |
void receive(DatagramPacket p) | 从此套接字接收数据报包。 |
DatagramPacket:
构造方法 | 说明 |
---|---|
DatagramPacket(byte[] buf, int length) | 构造 DatagramPacket,用来接收长度为 length 的数据包。 |
DatagramPacket(byte[] buf, int length, InetAddress address, int port) | 构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。 |
注意: 需要先启动接收方,再启动发送方
接收方:
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;public class TestUDPServer {
public static void main(String[] args) throws IOException {
DatagramSocket socket=new DatagramSocket(8883);//构造一个接收的快递站,指明哪个快递站(带端口)byte[] buff=new byte[100];DatagramPacket dp=new DatagramPacket(buff,buff.length);//构造一个快递包,用来存放接受来的数据socket.receive(dp);//接收快递String str=new String(buff);//拆包,把数据组合成成品System.out.println("来自发送方的消息: "+str);if(socket!=null){
socket.close();}//关闭连接}}
发送方:
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;public class TestUDPClient {
//UDP协议编程的发送方public static void main(String[] args) throws IOException {
DatagramSocket socket=new DatagramSocket();//构造快递站String str="Hello !!! receiver.....";byte[] buff=str.getBytes();DatagramPacket dp=new DatagramPacket(buff,buff.length,new InetSocketAddress("localhost",8883));//将快递打包,贴上快递单socket.send(dp);//投递if(socket!=null){
socket.close();}}}
demo02:
发送方:
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;public class TestUDPSender {
public static void main(String[] args) throws IOException {
//发送消息DatagramSocket socket=new DatagramSocket();String str="Hello!!! receiver.........";byte[] buff=str.getBytes();DatagramPacket dp=new DatagramPacket(buff,buff.length,new InetSocketAddress("localhost",8874));socket.send(dp);//接收消息byte[] buff2=new byte[30];DatagramPacket dp2=new DatagramPacket(buff2,buff2.length);socket.receive(dp2);String str2=new String(buff2);System.out.println("来自接收方的消息是: "+str2);if(socket!=null){
socket.close();}}}
接收方:
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;public class TestUDPReceiver {
public static void main(String[] args) throws IOException {
//接收数据DatagramSocket socket =new DatagramSocket(8874);byte[] buff=new byte[30];DatagramPacket dp=new DatagramPacket(buff,buff.length);socket.receive(dp);String str=new String(buff);System.out.println("来自发送方的消息是: "+str);//发送消息String str2="Hello!!! sender..........";byte[] buff2=str2.getBytes();DatagramPacket dp2=new DatagramPacket(buff2,buff2.length,dp.getSocketAddress());socket.send(dp2);if(socket!=null){
socket.close();}}}
五. URL网络编程
URL:
类 URL
代表一个统一资源定位符,它是指向互联网“资源”的指针。资源可以是简单的文件或目录,也可以是对更为复杂的对象的引用,例如对数据库或搜索引擎的查询
方法 | 说明 |
---|---|
URL(String spec) | 根据 String 表示形式创建 URL 对象。 |
URLConnection openConnection() | 返回一个 URLConnection 对象,它表示到 URL 所引用的远程对象的连接。 |
URLConnection:
抽象类 URLConnection
是所有类的超类,它代表应用程序和 URL 之间的通信链接。
此类的实例可用于读取和写入此 URL 引用的资源
方法 | 说明 |
---|---|
InputStream getInputStream() | 返回从此打开的连接读取的输入流。 |
OutputStream getOutputStream() | 返回写入到此连接的输出流。 |
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;public class TestURL {
public static void main(String[] args) throws IOException {
URL url=new URL("http://www.baidu.com");//定义一个网站URLConnection conn=url.openConnection();//建立连接BufferedReader br=new BufferedReader(new InputStreamReader(conn.getInputStream()));String line=null;while((line=br.readLine())!=null){
System.out.println(line);}//读取网站内容if(br!=null){
br.close();}//关闭连接/*BufferedInputStream in=new BufferedInputStream(conn.getInputStream());BufferedOutputStream out=new BufferedOutputStream(new FileOutputStream("D:\\day22\\day22\\a.html"));int data=-1;while((data=in.read())!=-1){out.write(data);}if(in!=null){in.close();}if(out!=null){out.close();}*/}}
练习:
设服务器端程序监听端口为8629,当收到客户端信息后,首先判断是否是“BYE”,
若是,则立即向对方发送“BYE”,然后关闭监听,结束程序。
若不是,则在屏幕上输出收到的信息,并由键盘上输入发送到对方的应答信息。
请编写程序完成此功能。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;/*设服务器端程序监听端口为8629,当收到客户端信息后,首先判断是否是“BYE”, 若是,则立即向对方发送“BYE”,然后关闭监听,结束程序。 若不是,则在屏幕上输出收到的信息,并由键盘上输入发送到对方的应答信息。 请编写程序完成此功能。*/public class Ex_Client {
public static void main(String[] args) throws UnknownHostException, IOException {
Socket s=new Socket("localhost",8629);Scanner sc=new Scanner(System.in);BufferedReader br=new BufferedReader(new InputStreamReader(s.getInputStream()));PrintWriter pw=new PrintWriter(s.getOutputStream());String line=null;while(!"bye".equalsIgnoreCase(line)){
System.out.println("发送: ");String datas=sc.nextLine();pw.println(datas);pw.flush();line=br.readLine();System.out.println("接收: "+line);}if(br!=null){
br.close();}if(pw!=null){
pw.close();}if(s!=null){
s.close();}}}
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;public class Ex_Server {
public static void main(String[] args) throws IOException {
ServerSocket ss=new ServerSocket(8629);Socket s=ss.accept();Scanner sc=new Scanner(System.in);BufferedReader br=new BufferedReader(new InputStreamReader(s.getInputStream()));PrintWriter pw=new PrintWriter(s.getOutputStream());String line=null;while(!"bye".equalsIgnoreCase(line)){
line=br.readLine();System.out.println("接收: "+line);System.out.println("发送:");String msg=sc.nextLine();pw.println(msg);pw.flush();}pw.println("bye");pw.flush();if(br!=null){
br.close();}if(pw!=null){
pw.close();}if(s!=null){
s.close();}}}