首先先介绍一下InetAddress和URL类
URL类:
类 URL 代表一个统一资源定位符,它是指向互联网“资源”的指针。资源可以是简单的文件或目录,也可以是对更为复杂的对象的引用,例如对数据库或搜索引擎的查询。其实,它就是为我们提供切割URL字符串,为我们获取相应的URL信息。
通过下面一段代码简单的说明其常用的功能:
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
public class URLDemo {
public static void main(String[] args)throws Exception{
// TODO Auto-generated method stub
//目标是我自己写的一个server
URL url=new URL("Http://127.0.0.1:10000");
//获取此 URL 的文件名。
System.out.println("File:"+url.getFile());
//获取此 URL 的主机名(如果适用)。
System.out.println("getHost:"+url.getHost());
//获取此 URL 的路径部分。
System.out.println("getPath:"+url.getPath());
//获取此 URL 的端口号。 如果为默认端口,则返回-1,需要自己指定
System.out.println("getPort:"+url.getPort());
//获取此 URL 的协议名称。
System.out.println("getProtocol:"+url.getProtocol());
//获取此 URL 的查询部分。
System.out.println("getQuery:"+url.getQuery());
//建立连接,功能相当于socket,但是不同的是同工作在应用层,不用关闭链接
URLConnection connection=url.openConnection();
InputStream is=connection.getInputStream();
int len=0;
byte[]bt=new byte[1024];
while((len=is.read(bt))!=-1){
System.out.println(new String(bt,0,len));
}
}
}
运行结果如下:
File:/name.html?name=chu
getHost:127.0.0.1
getPath:/name.html
getPort:10000
getProtocol:http
getQuery:name=chu
上述程序有两个特殊的方法:
openConnection(),此方法返回的是一个URLConnection类,表示和目标服务器建立连接,相当于完成Socket的三次握手,即连接工作;但是和Socket不同的是,Socket工作在传输层,而它工作在用于层;它把Socket封装在自己类中;组装出自己的功能
另一个就是openStream()方法,此方法返回的是一个InputStream流对象,查看JDK文档,可以知道它就是URLConnection connection=url.openConnection();
InputStream is=connection.getInputStream();两者的组合;即url.openConnection().getInputStream();
InetAddress类
此类表示互联网协议 (IP) 地址。 简单的说它就代表IP地址对象,用来操作IP地址的,此类没有构造函数,只有通过自身的静态方法获取该类的对象;
一般而言,其方法为getbyXXX()基本就是返回其自身对象的。比如说:getAllByName();此方法返回的是一个InetAddress的数组;因为同一个域名可能有多个IP地址;其它,就是getByAddress(),getByName();但有一个特殊的方法也是返回InetAddress,即getLocalHost();返回本地主机。
接下来看段代码:
import java.net.InetAddress;
public class URLDemo {
public static void main(String[] args) throws Exception{
// TODO Auto-generated method stub
//因为InetAddress没有构造函数,只能通过调用自身静态方法产生
InetAddress ip=InetAddress.getByName("127.0.0.1");
//获取主机地址
System.out.println("address="+ip.getHostAddress());
//获取主机名,由于Host文件中配置了 127.0.0.1 activate.adobe.com
System.out.println("name="+ip.getHostName());
}
}
运行结果如下:
address=127.0.0.1 //Host文件中配置了 127.0.0.1 activate.adobe.com name=activate.adobe.com
因为我在Host文件中配置了 127.0.0.1 activate.adobe.com,所以,name为activate.adobe.com
还有一个常用的用法
String ip=dp.getAddress().getHostAddress();即InetAddress.getHostAddress()方法获取Ip字符串。
注:dp.getAddress()返回的是一个InetAddress类。
下面举一个实例:通过UDP传输方式,将一段文字数据发送出去。
发送端设计思路如下:
1、建立UDPSocket服务;
2、提供数据,并将数据封装到数据包中,注意:最终发送的都是字节流;
3、通过Socket服务的发送功能,将数据包发送出去;
4、关闭资源。
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class UDPSend {
public static void main(String[] args) throws Exception{
// TODO Auto-generated method stub
//创建udp服务,通过DatagramSocket()对象
DatagramSocket ds=new DatagramSocket();
//确定存储数据的字节数组
byte[]buf=new byte[1024];
buf="正在学习黑马视频教程!".getBytes();
//将数据封装为数据包,并指定目标Ip以及端口号
DatagramPacket dp=new DatagramPacket(buf, buf.length, InetAddress.getByName("127.0.0.1"),10000);
//发送数据
ds.send(dp);
//关闭资源
ds.close();
}
}
接受端设计思路如下:
1、定义udpsocket服务,并指定监听端口;
2、定义一个数据包,因为要存储接收到的字节数据,因为数据包对象中有更多功能可以提取字节数据中 的不同数据信息;
3、通过socket服务的receive方法接收到的数据存入已经定义好的数据包中;
4、通过数据包对象的特有功能,将这些不同的数据取出,打印在控制台上;
5、关闭资源。
用于接收udp协议传输的数据并进行处理
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class UDPReceive {
public static void main(String[] args)throws Exception {
// TODO Auto-generated method stub
//创建udp socket,建立端点
//指定监听端口号
DatagramSocket ds=new DatagramSocket(10000);
//定义数据包,存储接收到的数据
byte[]buf=new byte[1024];
//此构造方法用来接收数据,一共有两种构造方法用来接收,另一种DatagramPacket(byte[]by,ing offset,int length)
DatagramPacket dp=new DatagramPacket(buf,buf.length);
//通过receive方法接收到的数据存入数据包中,Receive方法为阻塞式方法
ds.receive(dp);
//获取数据
String data=new String(dp.getData(),0,dp.getLength());
String data1=new String(buf,0,buf.length);
//获取源ip
String ip=dp.getAddress().getHostAddress();
//获取源端口号
int port=dp.getPort();
System.out.println("data:"+data);
System.out.println("ip:"+ip);
System.out.println("port:"+port);
System.out.println("data1:"+data1);
//关闭资源
ds.close();
}
}
测试要先运行接收程序,再运行发送程序.如果接收程序没有接收到数据,则会一直阻塞,接收到数据后才会关闭程序。如果网络上没有数据发送过来,接收程序也没有阻塞,通常都是使用了一个已经被占用的端口。
运行结果如下:
data:正在学习黑马视频教程! ip:127.0.0.1 port:61045 data1:正在学习黑马视频教程!
有关TCP的应用
应用举例:建立一个文本转换服务器
客户端给服务端发送文本,服务端将文本转换成大写在返回给客户端,而且客户端可以不断的进行文本转换,当客户端输入over时,转换结束。
分析
客户端:
既然是操作设备上的数据,那么就可以用Io技术,并且按照IO操作规律来思考
源:键盘录入
目的:网络设备,网络输出流,而且操作的是文本数据,可以选择字符流
步骤
1.建立服务
2.获取键盘录入
3.将数据发给服务器
4.返回服务端的大写数据
服务端代码如下:
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class TCPServer {
public static void main(String[] args) throws Exception{
// TODO Auto-generated method stub
try{
ServerSocket st=new ServerSocket(10000);
Socket s=st.accept();
String ip=s.getInetAddress().getHostAddress();
System.out.println("ip..."+ip+"....connection........");
//读取socket读取流中的数据
BufferedReader br=new BufferedReader(new InputStreamReader(s.getInputStream()));//第一种写法
//目的,socket输出流,将大写数据写入到socket输出流,并发送到客户端
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
//PrintWriter bs=new PrintWriter(s.getOutputStream(),true);第二种写法
String line=null;
while((line=br.readLine())!=null){
System.out.println(line);
bw.write(line.toUpperCase());
bw.newLine();
bw.flush();
//bs.println(line.toUpperCase());第二种写法
}
}catch(Exception e){
e.printStackTrace();
}
}
}
在这个程序里,创建了一个在10000端口上等待连接的ServerSocket对象,当接收到一个客户的连接请求后,程序从与这个客户建立了连接的Socket对象中获得输入输出流对象,通过输出流首先向客户端发送一串字符,然后通过输入流读取客户端发送过来的信息,并将这些信息打印,然后关闭所有资源。
要先运行服务器程序,然后才能运行客户端程序,当TCP服务器程序运行到Socket.accpet()方法等待客户连接时,accept方法将阻塞,一直到有客户连接请求到来,该方法才会返回,如果又没有请求到来,又没有发生阻塞,通常都是使用了一个已经被占用的端口.。
客户端代码如下:
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class TCPClient {
public static void main(String[] args)throws Exception {
// TODO Auto-generated method stub
try{
Socket s=new Socket("127.0.0.1",1000);
//定义读取键盘数据流对象
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
//定义目的,将数据写入socket输出流,发给服务器端
//BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
//第一种写法
BufferedReader buf=new BufferedReader(new InputStreamReader(s.getInputStream()));
//定义一个socket读取流,读取服务端返回的大写信息
//第二种写法
PrintWriter pw=new PrintWriter(s.getOutputStream(),true);
String line=null;
while((line=br.readLine())!=null){
if("over".equals(line))break;
//bw.write(line);第一种写法
//bw.newLine();因为readline()的结束标记是以换行为标记的,所以必须得加
//bw.flush();
String str=buf.readLine();
System.out.println("data="+str);
//第二种写法
pw.println(line);
}
br.close();
s.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
下面举一个多线程上传图片的应用例子
服务端程序:
import java.io.*;
import java.net.*;
public class FilesServerDemo {
public static void main(String[] args) {
// TODO Auto-generated method stub
try{
ServerSocket ss=new ServerSocket(10000);
while(true){
Socket s=ss.accept();
new Thread(new PicThread(s)).start();
}
}catch(Exception e){
throw new RuntimeException("上传失败");
}
}
}
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class UploadPicture implements Runnable {
private Socket s;
UploadPicture(Socket s){
this.s=s;
}
@Override
public void run(){
// TODO Auto-generated method stub
int count=1;
String ip=s.getInetAddress().getHostAddress();
System.out.println(ip+"......connection");
try{
File file=new File(ip+"("+count+")"+".jpg");
while(file.exists())
file=new File(ip+"("+(count++)+")"+".jpg");
FileOutputStream fos=new FileOutputStream(file);
InputStream is=s.getInputStream();
OutputStream out=s.getOutputStream();
int len=0;
byte[]bt=new byte[1024];
while((len=is.read(bt))!=-1){
fos.write(bt,0,len);
}
out.write("图片上传成功".getBytes());
s.close();
fos.close();
}
catch(Exception e){
throw new RuntimeException("图片上传失败!");
}
}
}
客户端程序:
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class PictureClient {
public static void main(String[] args) {
// TODO Auto-generated method stub
if(args.length!=1){
System.out.println(" please choose a pic");
return;
}
if(!args[0].endsWith(".jpg")){
System.out.println("请上传一个jpg格式的图片!");
return;
}
File f=new File(args[0]);
if(!(f.exists()&&f.isFile())){
System.out.println("请上传一个存在的文件!");
return;
}
if(f.length()>1024*1024*5){
System.out.println("请上传一个文件大小不超过6M的文件!");
return;
}
try{
Socket s=new Socket("127.0.0.1",10000);
FileInputStream fs=new FileInputStream(f);
OutputStream out=s.getOutputStream();
InputStream in=s.getInputStream();
byte[]bt=new byte[1024];
int len=0;
while((len=fs.read(bt))!=-1){
out.write(bt,0,len);
}
s.shutdownOutput();
int l=0;
byte[]bb=new byte[1024];
while((l=in.read(bb))!=-1){
System.out.println(new String(bb,0,l));
}
s.close();
fs.close();
}catch(Exception e){
throw new RuntimeException("图片上传文件失败");
}
}
}
模仿Tomcat服务器的一个应用举例
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class TomcatDemo {
public static void main(String[] args)throws Exception {
// TODO Auto-generated method stub
ServerSocket st=new ServerSocket(10004);
//获取客户端的Socket
Socket sk=st.accept();
//获取输入流对象
InputStream is=sk.getInputStream();
byte[]bs=new byte[1024];
//读取数据
int len=is.read(bs);
String data=new String(bs,0,len);
String ip=sk.getInetAddress().getHostAddress();
System.out.println("ip......."+ip+'\n'+"data="+data);
//BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(sk.getOutputStream(),"gbk"));
OutputStream bw=sk.getOutputStream();
bw.write("黑马欢迎您!".getBytes());
System.out.println("黑马欢迎您!");
//关闭客户端Socket流
sk.close();
st.close();
}
}
其实,Tomcat服务器就是一个服务端的ServerSocket
采用了多线程
需求分析:
客户端通过键盘录入用户名
服务器对这个用户名进行校验
如果该用户名存在,在服务器端显示xxx已经登录
并在客户端显示xxx,欢迎光临
如果该用户名不存在,在服务器端显示xxx尝试登录
并在客户端显示xxx,该用户名不存在
做多就登录三次
服务端程序:
import java.net.ServerSocket;
import java.net.Socket;
public class UserLoginServerDemo {
public static void main(String[] args)throws Exception {
// TODO Auto-generated method stub
ServerSocket ss=new ServerSocket(10008);
while(true){
Socket s=ss.accept();
new Thread(new UserLoginThread(s)).start();
}
}
}
客户端端程序:
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
public class UserLoginThread implements Runnable {
private Socket s;
UserLoginThread(Socket s){
this.s=s;
}
@Override
public void run() {
// TODO Auto-generated method stub
try{
String ip=s.getInetAddress().getHostAddress();
System.out.println(ip+".......connnection");
for(int i=0;i<3;i++){
BufferedReader br=new BufferedReader(new InputStreamReader(s.getInputStream()));
String name=br.readLine();
BufferedReader buf=new BufferedReader(new FileReader("user.txt"));
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
String line=null;
boolean flag=false;
while((line=buf.readLine())!=null){
if(line.equals(name))
{
flag=true;
break;
}
}
if(flag){
System.out.println(name+"欢迎,登录成功");
bw.write(name+"欢迎,登录成功");
bw.flush();
break;
}else{
System.out.println(name+"登录未成功");
bw.write(name+"登录未成功");
bw.newLine();
bw.flush();
}
}
s.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class UserLoginClientDemo {
public static void main(String[]args)throws Exception{
Socket s=new Socket("127.0.0.1",10008);
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
PrintWriter pw=new PrintWriter(s.getOutputStream(),true);
BufferedReader bufr=new BufferedReader(new InputStreamReader(s.getInputStream()));
for(int i=0;i<3;i++){
String line=br.readLine();
if(line==null)break;
pw.println(line);
String le=bufr.readLine();
System.out.println(le);
if(le.contains("欢迎您的登陆!")){
break;
}
}
s.close();
br.close();
}
}
模拟简单聊天程序(采用了多线程编程思想)
服务端端程序:
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class ReceiveServer implements Runnable {
private DatagramSocket ds;
ReceiveServer(DatagramSocket ds){
this.ds=ds;
}
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
try{
byte[]b=new byte[1024*64];
DatagramPacket dp=new DatagramPacket(b,0,b.length);
ds.receive(dp);
String data=new String(dp.getData(),0,dp.getLength());
String ip=dp.getAddress().getHostAddress();
System.out.println("data:"+data);
System.out.println("ip:"+ip);
}catch(Exception e){
throw new RuntimeException("运行失败");
}
}
}
}
客户端端程序:
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class SendClient implements Runnable{
private DatagramSocket ds;
SendClient(DatagramSocket ds){
this.ds=ds;
}
public void run(){
try{
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
String line=null;
while((line=br.readLine())!=null){
if("byby".equals(line))break;
byte[]b=line.getBytes();
DatagramPacket dp=new DatagramPacket(b,0,b.length,InetAddress.getByName("127.0.0.1"),10006);
ds.send(dp);
}
ds.close();
}catch(Exception e){
throw new RuntimeException("运行出错!");
}
}
}
主程序:用于测试
import java.net.DatagramSocket;
public class LiaotianMain {
public static void main(String[] args) throws Exception{
// TODO Auto-generated method stub
DatagramSocket ds=new DatagramSocket();
DatagramSocket dt=new DatagramSocket(10006);
new Thread(new SendClient(ds)).start();
new Thread(new ReceiveServer(dt)).start();
}
}
运行结果:
您好!
data:您好!
ip:127.0.0.1
欢迎来到黑马学习
data:欢迎来到黑马学习
ip:127.0.0.1
努力中
data:努力中
ip:127.0.0.1
通过以上应用举例,可以总结出使用JAVA网络编程的一般设计步骤如下:
1、建立客户端的Socket对象,客户端连接到服务器;
2、服务器端:
a. 建立ServerSocket对象;
b. 指定端口;
c. 获取客户端Socket,通过accept()方法,注意此方法为阻塞式方法;
d. 服务端关闭端口。其实,现实应用中,很多大型的公司都不会关闭服务端端口;
e. 服务端没有专门的流对象,通过获取客户端的流对象进行操作;
f. 一般用到网络编程,都会用到IO流操作TCP和UDP基本情况就是这样;注意,Socket服务于传输层,建立连接,实际上是建立Socket流,而Socket流包括输入,输出流。