前言:
兜兜转转,终于来到了网络编程,接着上次的内容,我们继续学习Java
URL通信:
URL(Uniform Resource Locator,同一资源定位器)表示Internet/Intranet 上的资源位置。这些资源可以是一个文件、一个目录或者一个对象。当我们使用浏览器浏览网络上的资源时,首先要键入URL地址,才可以访问相应的主页。
每一个完整的URL由四个部分组成,如下表:
含义 | |
传输协议 | |
主机名或主机地址 | |
通信端口号 | |
文件名称 |
一般的通信协议都已经规定好了开始联络时的通信端口,例如,HTTP协议的缺省端口号是80,FTP协议的缺省端口号是21等。URL使用协议的缺省端口号时,可以不写出缺省端口号等。
在TCP通信中,端口被规定在一个0~65535之间的16位整数。其中,0~1023被预先定义的服务通信占用(如FTP协议的端口号是21,HTTP协议的端口号是80等)。除非我们需要访问这些特定服务,否则就应该使用1024~65535这些端口的某一个来进行通信,以避免端口冲突。
URL类
要使用URL进行网络编程,就必须创建URL对象。
利用URL类访问网上资源示例程序:
package 测试;
import java.net.MalformedURLException;
import java.net.URL;
public class test{
public static void main(String[] args) {
URL MyURL=null;
try {
MyURL=new URL("http://www.马赛克.cn/index.html");
}catch(MalformedURLException e) {
System.out.println("MalformedURLException"+e);
}
//获取URL对象转化成字符串
System.out.println("URL String:"+MyURL.toString());
//获取协议名
System.out.println("Protocol:"+MyURL.getProtocol());
//获取主机名
System.out.println("Host:"+MyURL.getHost());
//获取端口号
System.out.println("Port:"+MyURL.getPort());
//获取文件名
System.out.println("File:"+MyURL.getFile());
}
}
输出:
URL String:http://www.马赛克.cn/index.html
Protocol:http
Host:www.zjsdxf.cn
Port:-1
File:/index.html
示例:使用URL类的openStream()成员方法获取URL指定的网上信息
package 测试;
import java.beans.Encoder;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
public class test{
public static void main(String[] args) {
String str;
InputStream st1;
//获取远程网上的信息
String ur="http://www.马赛克.cn/index.html";
try {
URL MyURL=new URL(ur);
st1=MyURL.openStream();
InputStreamReader ins=new InputStreamReader(st1);
BufferedReader in=new BufferedReader(ins);
//从URL处获取信息并显示
while((str=in.readLine())!=null) {
System.out.println(str);
}
}catch(MalformedURLException e) {
System.out.println("Can't get URL:"+e.getMessage());
}catch(IOException e) {
System.out.println("Error in I/O:"+e.getMessage());
}
}
}
输出既是网页的页面代码
由于URL的openStream()成员方法返回的是InputStream类的对象,因此只能通过read()方法逐个字节去读URL地址处的资源信息。这里利用BufferedReader对原始信息流进行了包装和处理,以提高I/O效率
使用URLConnection类访问网上资源信息
上面的方法只能读取远程计算机的信息,如果希望在读取远程计算机节点的信息时还可以向它写入信息,则需要使用java.net软件包中的另一个类URLConnection
- 创建URLConnection类的对象
- 建立输入/输出数据流
- 读取远程计算机节点的信息或向其写入信息
URLConnection类是一个抽象类,它是代表程序与URL对象之间建立通信连接的所有类的超类,此类的一个实例可以用来读/写URL对象所代表的资源。处于安全性考虑,Java程序只能对特定的URL进行操作,这种URL就是服务器上的CGI(Common Gateway Interface,公共网关接口)程序。CGI是客户端浏览器与服务器进行通信的接口。
示例:使用URLConnection类从远程主机获取信息
package 测试;
import java.io.*;
import java.net.*;
public class test{
public static void main(String[] args) {
try {
//获取远程网上的信息
String ur="http://www.chd.edu.cn";
URL MyURL=new URL(ur);
String str;
URLConnection con=MyURL.openConnection();
//这里设置了编码集是utf-8来避免中文乱码
InputStreamReader ins=new InputStreamReader(con.getInputStream(),"UTF-8");
BufferedReader in=new BufferedReader(ins);
while((str=in.readLine())!=null) {
System.out.println(str);
}
in.close();
}catch(MalformedURLException mfURLe) {
System.out.println("MalformedURLException:"+mfURLe.getMessage());
}catch(IOException e) {
System.out.println("IOException:"+e);
}
}
}
Socket通信
Socket套接字是应用于网络通信中的重要机制。Java语言中采用的Socket通信是一种流式套接字通信,它采用TCP协议,通过提供面向连接的服务,实现客户/服务器之间双向、可靠的通信。java包中的Socket类与ServerSocket类为流式套接字通信方式提供了充分的支持。
利用Socket进行网络通信分为三个步骤:
- 建立Socket连接。在通信开始之前由通信双方确认身份,建立一条专用的虚拟连接通道。
- 数据通信。利用虚拟连接通道传送数据信息进行通信
- 关闭。通信结束时将所建的虚拟连接拆除
示例:利用InetAddress类的对象来获取计算机主机的信息
package 测试;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class test{
public static void main(String[] args) {
try {
if(args.length==1) {
//调用InetAddress类的静态方法,利用主机名创建对象
InetAddress ipa=InetAddress.getByName(args[0]);
//获取主机名
System.out.println("Host name:"+ipa.getHostName());
//获取IP地址
System.out.println("Host IP Address:"+ipa.toString());
System.out.println("Local Host:"+InetAddress.getLocalHost());
}else {
System.out.println("请输入一个主机名");
}
}catch(UnknownHostException e) {
System.out.println(e.toString());
}
}
}
Host name:localhost
Host IP Address:localhost/127.0.0.1
Local Host:BAT-PC/192.168.17.1
这个例子需要给主函数传参数
下面的示例就牛逼了,是一个完整的实现Socket通信的Java程序,有服务端,也有客户端
由于连个端测试,所以我在txt文本里写,用命令行测试了
我们先看服务端:
//服务端程序
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class test{
public static final int port=7999;
public static void main(String[] args) {
String str;
try {
//在端口port注册服务
//创建当前线程的监听对象
ServerSocket server=new ServerSocket(port);
System.out.println("Started: "+server);
//负责C/S通信的Socket对象
Socket socket=server.accept();
System.out.println("Socket:"+socket);
//获得对应Socket的输入/输出流
InputStream fIn=socket.getInputStream();
OutputStream fOut=socket.getOutputStream();
//建立数据流
InputStreamReader isr=new InputStreamReader(fIn);
BufferedReader in=new BufferedReader(isr);
PrintStream out=new PrintStream(fOut);
InputStreamReader userisr=new InputStreamReader(System.in);
BufferedReader userin=new BufferedReader(userisr);
while(true) {
System.out.println("等待客户端的消息……");
str=in.readLine(); //读客户端传送的字符串
System.out.print("客户端:"+str); //显示字符串
//如果是end,则推出
if(str.equals("end"))
break;
System.out.print("给客户端发送");
str=userin.readLine();
out.println(str); //向客户端发送信息
if(str.equals("end"))
break;
}
socket.close();
server.close();
}catch(Exception e) {
System.out.println("异常:"+e);
}
}
}
客户端
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
public class Client{
public static void main(String[] args) {
String str;
try {
InetAddress addr=InetAddress.getByName("127.0.0.1");
Socket socket=new Socket(addr,7999);
System.out.println("Socket:"+socket);
//获得对应socket的输入/输出流
InputStream fIn=socket.getInputStream();
OutputStream fOut=socket.getOutputStream();
//建立数据流
InputStreamReader isr=new InputStreamReader(fIn);
BufferedReader in=new BufferedReader(isr);
PrintStream out=new PrintStream(fOut);
InputStreamReader userisr=new InputStreamReader(System.in);
BufferedReader userin=new BufferedReader(userisr);
while(true) {
System.out.print("发送字符串:");
str=userin.readLine(); //读取用户输入的字符串
//将字符串传给服务器
out.println(str);
//如果是end,就退出
if(str.equals("end")) {
break;
}
System.out.println("等待服务器端信息……");
//获取服务器发送的字符串
str=in.readLine();
System.out.println("服务器端字符:"+str);
if(str.equals("end")) {
break;
}
}
socket.close();
}catch(Exception e) {
System.out.println("异常"+e);
}
}
}
测试结果:
运行时先运行服务端使其监听端口,再运行客户端即可。
URL通信与Socket通信的区别
URL通信与Socket通信都是面向连接的通信,他们的区别在于:Socket通信方式为主动等地啊客户端的服务请求方式,而URL通信方式则为被动等待客户端的服务请求方式
UDP通信:
URL和Socket通信是一种面向连接的流式套接字通信,采用的协议是TCP协议。在面向连接的通信中,通信的双方首先建立连接再进行通信,这就需要占用资源和时间,当然,这样也会稳定和准确。
UDP通信是一种无连接的数据报通信。采用的协议是数据报通信协议UDP(User Datagram Protocol)。按照这个协议,两个程序进行通信时不用建立连接;数据以独立的包为单位发送,包的容量不能太大;每个数据报需要有完整的收/发地址,可以随时进行收/发数据报,但不保证传送顺序和内容准确;数据包可能会被丢失、延误等。因此,UDP通信是不可靠的通信。由于UDP通信速度较快,因此常常被应用在某些要求实时交互,准确性要求不高,但传输速度较高的场合。
Datagram:数据包
服务器端的程序有一个线程不停地监听客户端发来的数据报,等待客户的请求。服务器只有通过客户发来数据报中的信息才能得到客户端的地址及端口
我们来看个示例:
服务端:
package 测试;
//服务端程序
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
class UDPServerThread extends Thread{
//启动服务器线程的主程序
private DatagramPacket packet;
private DatagramSocket socket;
static final int sport=1777;
UDPServerThread(){
try {
//将socket连接到本机的一个可用端口上
socket=new DatagramSocket(sport);
System.out.println("Listening on port:"+socket.getLocalPort());
}catch(Exception e) {
System.out.println("Error:"+e);
}
}
public void run(){
//线程的主要操作
if(socket==null)
return;
while(true) {
try {
InetAddress address;
int cport;
byte[] buf1=new byte[1000];
byte[] buf2=new byte[1000];
String s="Your packet is received!";
//生成一个接受数据报
packet=new DatagramPacket(buf1,buf1.length);
//接受数据报
socket.receive(packet);
String s1=new String(packet.getData());
//打印数据报的内容
System.out.println("Received from client:"+s1);
//获取数据报的源地址与端口
address=packet.getAddress();
cport=packet.getPort();
buf2=s.getBytes();
//生成发送的数据报
packet=new DatagramPacket(buf2,buf2.length,address,cport);
socket.send(packet); //发送数据给客户
}catch(Exception e) {
System.out.println("Error: "+e);
}
}
}
protected void finaliz() {
if(socket!=null) {
//关闭socket
socket.close();
System.out.println("Socket Closed");
}
}
}
public class test{
public static void main(String[] args) {
UDPServerThread server=new UDPServerThread();
server.start();
}
}
客户端(注意要给客户端传参数——本地端口号、服务器名、服务器端口号):
package 测试;
import java.net.InetAddress;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class Client{
public static void main(String[] args) {
//用于发送/接收UDP
DatagramSocket socket;
//用于保存UDP的内容
DatagramPacket packet;
InetAddress address;
int port;
byte[] buf1=new byte[1000],buf2=new byte[1000];
String s="Hello server!",s2;
if(args.length<3) {
System.out.println("输入本地端口号、服务器名、服务器端口号");
System.exit(0);
}
try {
socket=new DatagramSocket(Integer.parseInt(args[0]));
address=InetAddress.getByName(args[1]);
port=Integer.parseInt(args[2]);
buf1=s.getBytes();
packet=new DatagramPacket(buf1,buf1.length,address,port);
//向服务器发送packet
socket.send(packet);
//生成接收的packet
packet=new DatagramPacket(buf2,buf2.length);
//接受服务器传来的packet
socket.receive(packet);
s2=new String(packet.getData());
//打印packet内容
System.out.println("Received from server:"+s2);
//关闭Socket
socket.close();
}catch(Exception e) {
System.out.println("Error:"+e);
}
}
}
我们在运行=》运行配置中=》新建启动配置,然后先运行服务端,再运行客户端,此时控制台只有一个结果,我们点击右上角的切换控制台就可以看到不同的结果了。
商业转载 请联系作者获得授权,非商业转载 请标明出处,谢谢