时间有些紧迫了,一个星期的悠闲时光也就这么结束了,开始巩固新东西,今天来说一说Java中的网络编程
学习网络编程首先需要知道一些计算机网络的基本知识
TCP/IP
ip地址划分
注意: A类地址中的127开头的地址都是保留地址,例如最常见的127.0.0.1代表localhost,C类地址中的私有地址:192.168.X.X表明不是公网地址。
端口
URL
TCP三次握手
TCP四次挥手
InetAddress类
InetAddress类有两个子类Inet4Address、Inet6Address,分别表示IPv4和IPv6。InetAddress没有提供构造方法,但可以通过类中的静态方法来获取InetAddress类的实例。
例子
package homework;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Scanner;
public class InetAddressDemo {
public static void main(String []args){
Scanner sc=new Scanner(System.in);
System.out.println("input the domain:");
String dnsname=sc.next();
InetAddress local =null;
try{
local =InetAddress.getByName(dnsname);
System.out.println("dsnmain IP:"+local.getHostAddress());
System.out.println("domainName:"+local.getHostName());
local=InetAddress.getLocalHost();
System.out.println("host ip:"+local.getHostAddress());
System.out.println("host name:"+local.getHostName());
}catch (UnknownHostException e){
e.printStackTrace();
}
}
}
URL类
例子
package homework;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.Scanner;
public class UrlDemo {
public static void main(String []args){
System.out.println("input the URL:");
Scanner sc=new Scanner(System.in);
String urlstr=sc.next();
URL url=null;
try{
url=new URL(urlstr);
System.out.println("host:"+url.getHost());
System.out.println("path:"+url.getPath());
System.out.println("port:"+url.getPort());
System.out.println("protocol:"+url.getProtocol());
System.out.println("information:"+url.getUserInfo());
System.out.println("content:"+url.getQuery());
}catch (Exception e){
e.printStackTrace();
}
try{
InputStream in=url.openStream();
BufferedReader bin=new BufferedReader(new InputStreamReader(in));
String strline=null;
while ((strline=bin.readLine())!=null)
System.out.println(strline);
bin.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
URLConnection类
URLConnection类的对象可以与指定的URL建立动态的连接,同时使用URLConnection类的对象可以实现向服务器发送请求,将数据送回服务器并检查远程资源的一些属性。 一般创建URLConnection类对象使用的是URL的openConnection()方法,URLConnection类的常用方法如下:
下载文件代码
package homework;
import java.text.DateFormat;
import java.io.BufferedInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
public class URLConDemo {
public static void main(String []args){
URLConnection urlCon=null;
try{
URL url=new URL("http://baobab.kaiyanapp.com/api/v1/playUrl?vid=63613&editionType=default&source=aliyun");
urlCon =url.openConnection();
}catch (Exception e){
e.printStackTrace();
}
DateFormat d=DateFormat.getDateTimeInstance();
long t1=System.currentTimeMillis();
System.out.println("start time: "+d.format(t1));
long length=urlCon.getContentLength()/1024;
System.out.println("the filelen: "+length+"KB");
System.out.println("filetype: "+urlCon.getContentType());
try{
InputStream in=urlCon.getInputStream();
BufferedInputStream bin=new BufferedInputStream(in);
FileOutputStream fout=new FileOutputStream("one.mp4");
int len=0;
float sum=0;
byte b[]=new byte[1024*1024];
System.out.println("==Start Download==");
while((len=bin.read(b,0,1024*1024))!=-1){
fout.write(b,0,len);
sum+=len/1024;
System.out.print("\rhave down: "+sum/length*100+"%");
}
System.out.println();
fout.close();
bin.close();
in.close();
long t2=System.currentTimeMillis();
System.out.println("End time: "+d.format(t2));
System.out.println("the go through time:"+(t2-t1)+"ms");
System.out.println("the speed: "+(float)length*1000/1024/(t2-t1)+"MB/s");
System.out.println("==End Download==");
}catch (Exception e){
e.printStackTrace();
}
}
}
附赠几个MP4下载链接
http://baobab.kaiyanapp.com/api/v1/playUrl?vid=11111&editionType=default&source=aliyun
http://baobab.kaiyanapp.com/api/v1/playUrl?vid=23456&editionType=default&source=aliyun
http://baobab.kaiyanapp.com/api/v1/playUrl?vid=63613&editionType=default&source=aliyun
http://baobab.kaiyanapp.com/api/v1/playUrl?vid=54321&editionType=default&source=aliyun
TCP通信
进行TCP网络通信需要使用两个类,一个是服务器套接字ServerSocket类,一个是客户端套接字Socket类,服务器套接字一次只能与一个客户端套接字连接,若多态客户端同时请求连接则可以采用多线程,服务器会将请求连接的客户端放入对列中,然后从中取出一个套接字与服务器的套接字相连,最大容纳数默认为50.
ServerSocket类
当ServerSocket类对象创建后,调用accept()方法接收来自客户端发来的连接请求,accept()方法采用的是“阻塞方式监听,直到客户端连接成功后程序才会向下继续执行并返回一个与客户端Socket对象相连的Socket对象。
服务器端的Socket对象使用getOutputStream()方法获取的输出流将指向客户端Socket对象使用getInputStream()方法获取的那个输入流;
同样的服务器端的Socket对象使用的getInputStream()方法获取的输入流将指向客户端Socket对象使用getOutputSream()方法获取的输出流。
Socket类
在Java中,有专门的Socket类来处理用户的请求和响应,利用Socket类的方法,就可以实现两台计算机之间的通信。
客户端创建Socket对象之后,会向指定的IP地址即端口尝试连接,服务器端的Socket接到连接请求后创建新的Socket与客户端的Socket进行连接。
若服务器的Socket与客户端的Socket连接成功后,就可以获取Socket中的输入流与输出流进行通信了。
PS:在Socket类的诸多方法中,最常见的getInputStream和getOutputStream是一定要使用的方法。
一对一通信实例
服务器端
package homework;
import java.io.*;
import java.net.*;
import java.util.*;
import java.text.DateFormat;
public class MyserverDemo {
public static void main(String []args){
ServerSocket server=null;
Socket socket=null;
DataInputStream din=null;
DataOutputStream dout=null;
DateFormat dd=DateFormat.getDateTimeInstance();
try{
server =new ServerSocket(8000);
System.out.println(dd.format(System.currentTimeMillis())+"\twait the client.....");
socket =server.accept();
System.out.println(dd.format(System.currentTimeMillis())+"\tclient have connect....\nclient from\t"+socket.getInetAddress().getHostAddress());
din=new DataInputStream(socket.getInputStream());
dout=new DataOutputStream(socket.getOutputStream());
String strMessage=null;
Scanner sc=new Scanner(System.in);
boolean flag=true;
while(flag){
strMessage=din.readUTF();
if(!strMessage.equals("bye")){
System.out.println(dd.format(System.currentTimeMillis())+"\tclient message:\n"+strMessage);
System.out.println(dd.format(System.currentTimeMillis())+"\treply: ");
strMessage=sc.next();
dout.writeUTF(strMessage);
dout.flush();
}else {
din.close();
dout.close();
flag=false;
}
}
server.close();
System.out.println(dd.format(System.currentTimeMillis())+"\tserver exit()...");
}catch (Exception e){
e.printStackTrace();
}
}
}
客户机端
package homework;
import java.io.*;
import java.net.*;
import java.text.DateFormat;
import java.util.*;
public class MyclientDemo {
public static void main(String []args){
Socket socket=null;
DataInputStream din=null;
DataOutputStream dout=null;
DateFormat df=DateFormat.getDateTimeInstance();
try{
System.out.println(df.format(System.currentTimeMillis())+"\ttry connect server.....");
socket =new Socket("127.0.0.1",8000);
System.out.println(df.format(System.currentTimeMillis())+"\tconnect successful");
din=new DataInputStream(socket.getInputStream());
dout=new DataOutputStream(socket.getOutputStream());
String strMassage=null;
Scanner sc=new Scanner(System.in);
boolean flag=true;
while (flag){
System.out.println(df.format(System.currentTimeMillis())+"\tsent message:");
strMassage =sc.next();
dout.writeUTF(strMassage);
dout.flush();
strMassage=din.readUTF();
if(!strMassage.equals("bye")){
System.out.println(df.format(System.currentTimeMillis())+"\treceive message:\n"+strMassage);
}else {
din.close();
dout.close();
flag=false;
}
}
socket.close();
System.out.println(df.format(System.currentTimeMillis())+"\tserver exit()...");
}catch (Exception e){
e.printStackTrace();
}
}
}
修改服务器端,实现一对多
客户端就是前面的示例无需任何修改,只需修改服务器端的示例如下即可
(方法就是简单的多线程实现方法,即实现Runnable抽象类)
package homework;
import java.io.*;
import java.net.*;
import java.util.*;
public class MyServerDemo2 {
public static void main(String []arg){
ServerSocket server=null;
Socket socket=null;
try{
server =new ServerSocket(8000);
System.out.println("wait client.....");
while(true){
socket=server.accept();
new Thread(new ServerThread(socket)).start();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
class ServerThread implements Runnable{
private Socket socket;
public ServerThread(Socket socket){
this.socket=socket;
}
public void run(){
DataInputStream din=null;
DataOutputStream dout=null;
try{
System.out.println("client connect....\nclient from\t"+socket.getInetAddress().getHostAddress());
din=new DataInputStream(socket.getInputStream());
dout=new DataOutputStream(socket.getOutputStream());
String strMassage=null;
Scanner sc=new Scanner(System.in);
boolean flag=true;
while(flag){
strMassage=din.readUTF();
if(!strMassage.equals("bye")){
System.out.println("client message:\n"+strMassage);
System.out.println("reply client:");
strMassage=sc.next();
dout.writeUTF(strMassage);
dout.flush();
}else {
din.close();
dout.close();
flag=false;
}
}
socket.close();
System.out.println("server exit()");
}catch (Exception e){
e.printStackTrace();
}
}
}
UDP
在Java中一般使用DatagramPacket和DatagramSocket类实现UDP通信。
DatagramSocket类用于创建接受或发送数据包的Socket对象,在C/S程序中使用UDP协议时,实际上并没有明显的服务器端和客户端的区别,因为通信双方都需要先建立一个DatagramSocket对象,用来接收或发送数据报,然后使用DatagramPacket对象作为传输数据的载体。
简单示例
- UDP客户端
package homework;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class UDPClientDemo {
public static void main(String []args){
DatagramSocket dsocket=null;
DatagramPacket dPack=null;
byte b[]=new byte[512];
try{
dsocket =new DatagramSocket(8000);
dPack=new DatagramPacket(b,512);
System.out.println("wait digital....");
while(true){
dsocket.receive(dPack);
String message=new String(dPack.getData(),0,dPack.getLength());
System.out.println("remote ip: "+dPack.getAddress().getHostAddress());
System.out.println("remote port: "+dPack.getPort());
System.out.println("receive message:"+message);
}
}catch (Exception e){
e.printStackTrace();
}
}
}
- UDP服务器端
package homework;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;
public class UDPServerDemo {
public static void main(String []args){
DatagramSocket dSock=null;
DatagramPacket dpack=null;
Scanner sc=new Scanner(System.in);
try{
InetAddress local=InetAddress.getLocalHost();
dSock=new DatagramSocket(8001);
String mssage=null;
while(true){
System.out.println("sent message");
mssage=sc.next();
dpack=new DatagramPacket(mssage.getBytes(),mssage.length(),local,8000);
dSock.send(dpack);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
首先开启客户端,然后开启服务器端发送数据
注意: 上述TCP的ServerSocket只是监听8000端口,绑定端口的只有客户端的Socket,即服务器端的Socket并没有绑定端口。而UDP的DatagramSocket的客户端和服务器端口都会绑定,但绑定是具有唯一性的,所以UDP客户端和服务器绑定的端口是不一样的,也就是上面的客户端绑定端口8000,服务器绑定端口8001。
小结
Java网络编程就是舒服,比起C/C++需要自己来实现协议来说,Java只需要熟悉如何进行操作就行了,在一方面Java比之C/C++又更胜一筹了,不过原理还是或多或少的需要去钻研一下的,毕竟需要知道自己面对的是什么才对。