黑马程序员---网络编程

[align=center]-------[url]http://www.itheima.com[/url]java培训、android培训期待与您交流!------- [/align]

一、概述
Java可以说是目前使用最为广泛的网络编程语言。自从1995年正式问世以 来。Java的快速发展已经使整个web世界发生了翻天覆地的变化。Java是一种可以简单地建立分布式应用程序(即由网络上的多个计算机执行的程序)的技术;是第一个允许用户将应用程序通过internet从远端服务器传输到本地上执行的一种语言。而随着internet的普及,越来越多的软件项目应用会涉及到网络。
  简单地说,网络编程其实就是让计算机之间相互通信数据。而java语言则提供了一系列的API来完成这些工作。只要我们导入java.net包就可以进行网络编程了。
 在进行网络编程前,还是要首先了解一些基本的知识,
   
网络模型;OSI参考模型、TCP/IP参考模型
网络要素:
IP地址:所谓 IP地址就是每台计算机所在的位置,这个位置用IP来标识。就像人的名字一样,如果我们要实现多台计算机间的数据通信,就要知道每个计算机的位置(名计算机的名称),那么知道了这个位置就可以将数据在计算机间传输。
那么,怎么使用java来获取IP地址呢?可以通过创建InetAddress对象,并通过他的一些方法来获取。java.net包
//通过计算机的名字获取
InetAddress ia2 = InetAddress.getByName("wangtingting-PC");
System.out.println("地址:"+ia2.getHostAddress());
System.out.println("名字:"+ia2.getHostName());
端口号:简单的说就是应用程序所对应的的数字标识。
传输协议:TCP/IP、UDP/IP   
二、Socket编程
网络编程其实就是让计算机之间相互传输数据,网络编程也叫Socket编程。
 在使计算机之间的程序相互传输数据时,这两个计算机的底层必须通过某些介质连接,这个介质就是Socket:套接字。数据在两个Socket间通过IO传输。
1、UDP(UserData Protocol)用户数据报协议
  UDP:用户数据报协议,是一种面向无连接的协议(在向另一端发送数据的时候,不用保证另一端必须也存在,如果,另一端不存在,则将数据包丢失,若存在就将数据接收),且是一种不可靠协议(数据容易丢失)。在传输数据包前不用在两个程序间建立连接,数据包的大小要小于64k,如果要发送的数据很大,就将数据分包发送(每个数据包还是要小于64k)。因此,UDP是的传输速度很快。
UDP一般使用于即时通信,如QQ聊天,视频会议等,这些对于数据的准确性和丢包要求比较低,但速度要求要快。
 数据传输是两台机器间的通信过程,所以,必须创建Socket服务(即要有发送端和接收端)。DatagramSocket可用于创建发送端和接收端的类对象。该对象提供了发送和接收的方法,既能发送数据又能接收数据。DatagramSocket构造函数中可以接收数据报包,DatagramPacket对象中可以接收指定大小的数据。
发送端:
创建步骤:导包,建立UDP Socket服务,即DatagramSocket对象
确定数据并封装成数据包,DatagramPacket
通过socket服务奖已有数据包发送出去,send()
关闭资源
当运行时,数据发出去但没有反应,说明数据丢失了,因为无接收端。
接收端:
创建步骤:创建UDP Socket服务,建立端点,
定义数据包,用于存储数据
通过服务的receive方法将接收到的数据存入已经定义好的数据包中
通过数据包的方法货物其中数据
关闭资源
在接收端创建UDP Socket服务时,接收端会指定监听端口,用于接收发送端(可指定,也可不指定,会用系统随机分配的端口地址)发送的数据包。若接收端不标识端口号,系统会自动分配端口。

import java.net.*;
//都是独立运行程序,有主函数

//发送端
class UdpSend
{
public static void main(String[] args) throws Exception
{
//建立UDP socket服务,
DatagramSocket ds = new DatagramSocket();

//确定数据,并将数据封装成数据包 目的端口号
//DatagramPacket(byte[] buf, int length, InetAddress address, int port)
// 将字符串数据转换成字节数组
byte[] buf = "发送的数据为:".getBytes();
//将要发送的数据、数据长度、接收的机器地址和该机器的应用程序,作为参数传递给DatagramPacket
DatagramPacket dp =
new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.1.100"),10000);

//通过socket服务,将已有数据包发送出去。通过send方法
ds.send(dp);

//关闭资源
ds.close();

//运行后在控制台无数据,因为UDP是面向无连接的,,数据丢失。。接收端未开启
}
}

//接收端

class UdpReceive
{
public static void main(String[] args) throws Exception
{
//创建udp socket ,既能发送又能接收 建立端点
DatagramSocket ds = new DatagramSocket(1000);

//定义数据包,用于存储数据 数据包对象中已经定义了功能提取字节数据中的不同数据信息
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf,buf.length);

//通过socket服务中的receive方法存入数据
ds.receive(dp);//阻塞式方法 没数据 就等待 线程机制

//通过数据包的方法获取其中数据 获取地址并得到字符串(返回的是对象)
String ip = dp.getAddress().getHostAddress();
//截取一部分(0,dp.length)
String data = new String(dp.getData(),0,dp.getLength());
int port = dp.getPort();

System.out.println(ip+"---"+data+"----"+port);

//关闭资源
ds.close();
}
}

class UdpDemo
{
public static void main(String[] args)
{
/*
开启两个cmd命令行,可以将发送端和接收端中的任意一个作为发送或接收方

*/
}
}


---->实例:
(1)键盘录入方式:在控制台录入要发送的内容,并发送给接收端
import java.net.*;
import java.io.*;
class UdpSend2
{
public static void main(String[] args) throws Exception
{
//创建udp socket服务,建立端点
DatagramSocket ds = new DatagramSocket();

//确定要发送的数据
//由于要发送的数据是键盘录入的内容,所以,使用到了IO流
BufferedReader bufr =
new BufferedReader(new InputStreamReader(System.in));
String line = null;
while ((line=bufr.readLine())!=null) //read 也是阻塞式方法 所以 发送端和接收端都等待
{
if ("再聊---".equals(line))
break;
byte[] buf = line.getBytes();

//将键盘录入的数据封装成数据包
DatagramPacket dp =
new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.1.103"),10001);

//将封装好的数据包发送出去
ds.send(dp);
}
ds.close();
}
}

class UdpReceive2
{
public static void main(String[] args) throws Exception
{
//建立udp socket服务
DatagramSocket ds = new DatagramSocket();

//定义数据包,用于存储接收的数据
//接收的是键盘录入的数据,所以发送端发送多少,接收端就接收多少
while (true)
{
//定义字节数组,用于存储数据,若数据较大 大小可 1024x64
byte[] buf = new byte[1024];

//将接收的数据存储到数据包中,
DatagramPacket dp = new DatagramPacket(buf,buf.length);

ds.receive(dp);

//获取ip地址和传送过来的数据
String ip = dp.getAddress().getHostAddress();
String data = new String (dp.getData(),0,dp.getLength());

System.out.println(ip+"----"+data);
}
//不必关闭资源 若关掉 那么其他端口就发送的内容就接收不到

}
}


(2)编写一个简单的聊天小程序,一个负责接收数据,一个负责发送数据,还要保证两个部分同时执行,则使用多线程。一个线程控制接收,一个线程控制发送。接收和发送动作不一致,所以定义两个run方法,用于编写接收端和发送端的代码且这两个方法要封装到两个不同的类中。
import java.io.*;
import java.net.*;
//实现Runnable接口
class Send implements Runnable
{
private DatagramSocket ds;
public Send(DatagramSocket ds)
{
this.ds = ds;
}
//复写run方法
public void run()
{
try
{
//创建字符流缓冲区对象,用于接收键盘录入的数据
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));

String line = null;
//接收键盘录入的数据,如果键盘录入未停止,就将键盘录入的数据先存储在数据包中
while ((line=bufr.readLine())!=null)
{
//如果键盘录入的数据为886,那么跳出循环,不在录入
if ("886".equals(line))
break;

//否则,将键入的数据转换成字节数据存储到字节数组中
byte[] buf = line.getBytes();

//将数据(包括键盘录入的数据、数据的长度接收的地址)封装到DatagramPacket对象中,等待发送
DatagramPacket dp =
new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.1.109"),10009); //,,,103
//调用Socket服务的send方法,将数据报包发送出去
ds.send(dp);
}
}
catch (Exception e)
{
//若发生异常,直接抛出运行时异常,迫使程序停止
throw new RuntimeException("发送失败");
}
}
}

class Receive implements Runnable
{
private DatagramSocket ds;
public Receive(DatagramSocket ds)
{
this.ds = ds;
}
public void run()
{
try
{
//如果
while (true)
{
//定义一个字节数组,用于存储发送端发送过来的数据
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf,buf.length);

ds.receive(dp);

String ip = dp.getAddress().getHostAddress();
//这里接收的是缓冲区的字节数组长度
String data = new String(dp.getData(),0,dp.getLength());
System.out.println(ip+"----"+data);

}

}
catch (Exception e)
{
throw new RuntimeException("发送失败");
}

}
}

class ChatDemo
{
public static void main(String[] args) throws Exception
{
DatagramSocket sendSocket = new DatagramSocket();
DatagramSocket receiveSocket = new DatagramSocket(10009);
//开启线程
new Thread(new Send(sendSocket)).start();
new Thread(new Receive(receiveSocket)).start();
}
}


2、TCP(Transmission Control Protocol)传输控制协议
TCP是一种面向无连接的协议,必须形成传输数据的通道,所以在客户端一初始化时就要指定主机,连接成功后形成通路后在该通道进行数据的传输。还要经过三次握手,即客户端在向服务端发送数据前要先告知服务端“我要发送数据了”,然后开始正式发送数据,最后告知服务端发送数据结束。由于在发送数据前要建立连接比较耗费资源,而且是大数据量传输,故效率稍低。
TCP传输过程:客户端(Socket)服务端(ServerSocket)
TCP是面向连接的,所以客户端一初始化就要指定主机,与服务端形成桐通路后在该通道进行数据传输(通过Socket服务的IO流:输入、输出流)。
步骤:(1)创建客户端socket服务,指定目的主机和端口,
(2)为发送数据应获取socket流中的输出流。

(1)建立服务端的socket服务,并监听一个端口(数据标识),
(2)获取链接过来的客户端对象,通过ServerSocket的accept(返回类型为Socket)方法(没有连接就会等待,所以该方法是阻塞式的),
(3)客户端若发来数据,服务端要使用对应的客户端对象并用客户端对象的读取流来读取数据,
(4)关闭资源(可选),但要关闭客户端。
import java.io.*;
import java.net.*;

//客户端
class TcpClient
{
public static void main(String[] args) throws Exception
{
//建立客户端socket服务,并指定目的主机和端口
Socket s = new Socket("192.168.1.103",10003);

//为发送数据,应获取socket流中的输出流
//因为在socket对象中已经封装了获得输出流的功能
OutputStream out = s.getOutputStream(); //返回类型为OutputStream

out.write("你好".getBytes());

//关闭客户端
s.close();
}
}

//服务器端
class TcpServer
{
public static void main(String[] args) throws Exception
{
//建立服务端的socket服务,并监听一个端口(加数字标识)
ServerSocket ss = new ServerSocket(10003);

//获取连接过来的客户端对象,通过ServerSocket的accept(返回类型为Socket)方法(没有连接就会等待,所以该方法是阻塞式的)
Socket s = ss.accept();

//通过socket对象的get方法获取ip地址
String ip = s.getInetAddress().getHostAddress();
//把ip地址变成字符串打印
System.out.println(ip);

//客户端若发过来数据,服务端要使用对应的客户端对象,并用客户端Socket对象的读取流来读取数据
//因为在socket对象中已经封装了获得输出流的功能
InputStream in = s.getInputStream();

//定义一个字节数组,用于存储发过来的数据
byte[] buf = new byte[1024];
int len = in.read(buf);
System.out.println(new String(buf,0,len));

//关闭客户端 服务端可不关闭
s.close();


}
}

---->实例:
(1)客户端发过来数据,服务端进行相应的回应,并将回应信息反馈给客户端
import java.io.*;
import java.net.*;

//客户端
class TcpClient2
{
public static void main(String[] args) throws Exception
{
//建立socket服务,指定连接主机和端口
Socket s = new Socket("192.168.1.103",10006);

//通过socket对象获取输出流,将数据写到该流中,通过网络发送给服务器端
OutputStream out = s.getOutputStream();
//因为存储的都是字节数据,所以要将字符串进行转换
out.write("你好呀,服务器端!".getBytes());

//通过socket对象获取网络流中的输入流,将服务器端反馈的数据信息获取到,并打印

//在这里,当客户端发送信息给服务端后,服务端会立即收到,但服务端反馈的信息会停10秒再发送过来
Thread.sleep(10000);
InputStream in = s.getInputStream();

//定义一个字节数组,用于存储收到的服务器端数据
byte[] buf = new byte[1024];
//通过输入流的read方法读入数据
int len = in.read(buf);
System.out.println(new String(buf,0,len));


//关闭客户端资源
s.close();

}
}

//服务端
class TcpServer2
{
public static void main(String[] args) throws Exception
{
//建立服务端socket服务,并监听一个端口
ServerSocket ss = new ServerSocket(10006);

//通过ServerSocket对象的accept方法建立连接
Socket s = ss.accept();

//获取ip地址 测试 是否连接成功
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip);

//通过对应的客户端对象的读取流读取发过来的数据
InputStream in = s.getInputStream();

byte[] buf = new byte[1024];
int len = in.read(buf);
System.out.println(new String(buf,0,len));

//在读取后,服务端做出相应的反馈,
//通过对应的客户端对象的获取输出流的方法,将反馈数据发给对应的客户端
OutputStream out = s.getOutputStream();

//将反馈数据写入,发送出去
out.write("我已收到,我是客户端,谢谢".getBytes());

//关闭客户端
s.close();

ss.close();//可选操作

}
}


(2)建立一个文本转换服务器
客户端给服务端发送文本,服务端会将文本转成大写再返回给客户端。
而且客户端可以不断的进行文本转换。当客户端输入over时,转换结束
客户端:
操作的是设备上的数据,所以用IO技术,并按IO的操作规律来思考
源:键盘录入
目的:网络设备 网络输出流
操作的是文本数据,所以选择字符流,为提高效率,加入缓冲技术
步骤:
1、建立服务
2、获取键盘录入
3、将数据发送给服务端
4、获取服务端返回的大写数据
5、结束,关闭资源
import java.io.*;
import java.net.*;
class TransClient
{
public static void main(String[] args) throws Exception
{
//建立客户端socket服务,指定主机和端口
Socket s = new Socket("192.168.1.103",10004);

//获取键盘录入的数据 定义读取键盘数据的流对象
BufferedReader bufr =
new BufferedReader(new InputStreamReader(System.in));

//定义目的,将数据写入socket输出流,发给服务端
BufferedWriter bufOut =
new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));

//定义一个socket读取流,读取服务端返回的大写信息

//因为通过socket对象获取的是字节流,所以要转换为字符流
//BufferedReader bufIn =
// new BufferedReader(new InputStreamReader(s.getInputStream()));

PrintWriter out= new PrintWriter(s.getOutputStream(),true);

String line = null;

while ((line=bufr.readLine())!=null)
{
if ("over".equals(line))
break;

out.println(line);
//bufOut.write(line);
//bufOut.newLine();
//bufOut.flush();


String str = bufIn.readLine();
System.out.println("server---"+str);
}
bufr.close();
s.close();

}
}
class TransServer
{
public static void main(String[] args) throws Exception
{
//建立服务端socket服务,并监听一个端口
ServerSocket ss = new ServerSocket(10004);

//获取发送过来的连接对象,
Socket s = ss.accept();
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+ "is connected");

//读取socket读取流中的数据
BufferedReader bufIn =
new BufferedReader(new InputStreamReader(s.getInputStream()));

//目的 socket输出流 将大写数据写入到socket输出流,并发送给客户端
//BufferedWriter bufOut =
//new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));

PrintWriter out = new PrintWriter(s.getOutputStream().true);

String line = null;
while ((line=bufIn.readLine())!=null)
{
out.prinln(line.toUpperCase());//带换行标记
//bufOut.write(line.toUpperCase());
//bufOut.newLine();
//bufOut.flush();
}

s.close();
ss.close();
}
}

/*
出现的问题
客户端和服务端都在莫名的等待
因为客户端和服务端都有阻塞式方法。这些方法没有读到结束标记,所以导致两端都等待
*/


3、TCP客户端并发操作
(1)TCP复制文件 :
客户端:
源:硬盘上的文件;目的:网络设备,即网络输出流。-->若操作的是文本数据,可选字符流,并加入高效缓冲区。若是媒体文件,用字节流。
服务端:
源:socket读取流;目的:socket输出流。
文件已经上传成功了,但是没有得到服务端的反馈信息。即使得到反馈信息,但得到的是null,而不是“上传成功”的信息。
原因:
因为客户端将数据发送完毕后,服务端仍然在等待这读取数据,并没有收到结束标记,就会一直等待读取。上个问题解决后,收到的不是指定信息而是null,是因为服务端写入数据后,也需要刷新,才能将信息反馈给客服端。
解决:
方法一:定义结束标记,先将结束标记发送给服务端,让服务端接收到结束标记,然后再发送上传的数据。但是这样定义可能会发生定义的标记和文件中的数据重复而导致提前结束。
方法二:定义时间戳,由于时间是唯一的,在发送数据前,先获取时间,发送完后在结尾处写上相同的时间戳,在服务端,接收数据前先接收一个时间戳,然后在循环中判断时间戳以结束标记。
方法三:通过socket方法中的shutdownOutput(),关闭客户端输出流资源,这就相当于给六中添加了一个结束标记-1。
在该例子中就是使用shutdownOutput()方法关闭客户端输出流给出标记。
//客户端  
class CopyClient
{
public static void main(String[] args)
{
//定义全局变量
Socket s = null;
try
{
s = new Socket("192.168.1.101",10003);
}
catch (IOException e)
{
throw new RuntimeException("创建连接失败");
}
BufferedReader bufr = null;
try
{
//创建读取流,读取指定文件
bufr = new BufferedReader(new FileReader("D:\\File\\udp.java"));
//创建写入缓冲区,写入网络输出流中
BufferedWriter bufOut =
new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));

//读取文本,并写入流中
String line = null;
while((line=bufr.readLine())!=null)
{
bufOut.write(line);
bufOut.newLine();
bufOut.flush();
}
//关闭客户端的输出流。
s.shutdownOutput();
//创建读取流,读取网络输入流
BufferedReader bufIn =
new BufferedReader(new InputStreamReader(s.getInputStream()));
//读取反馈信息
String str = bufIn.readLine();
System.out.println(str);
}
catch (IOException e)
{
throw new RuntimeException("发送失败");
}
//关闭流资源
finally
{
try
{
if(bufr!=null)
bufr.close();
s.close();
}
catch (IOException e)
{
throw new RuntimeException("资源关闭失败");
}
}
}
}

//服务端  
class CopyServer
{
public static void main(String[] args)
{
//定义全局变量
ServerSocket ss = null;
Socket s= null;
try
{
//创建服务,接收客户端数据
ss = new ServerSocket(10003);
s= ss.accept();
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+"....connected");
}
catch (IOException e)
{
throw new RuntimeException("连接失败");
}
BufferedWriter bufw = null;
try
{
//创建写入流
bufw = new BufferedWriter(new FileWriter("server.txt"));
//创建读取流
BufferedReader bufIn =
new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = null;
//读取网络输入流数据
while((line=bufIn.readLine())!=null)
{
bufw.write(line);
bufw.newLine();
bufw.flush();
}
//创建写入流,反馈给客户端信息
BufferedWriter bufOut =
new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
bufOut.write("上传成功");
bufOut.flush();

}
catch (IOException e)
{
throw new RuntimeException("发送失败");
}
//关闭流资源
finally
{
try
{
if(bufw!=null)
bufw.close();
s.close();
ss.close();
}
catch (IOException e)
{
throw new RuntimeException("资源关闭失败");
}
}
}
}


(2)图片上传:
客户端:
1、创建服务端点
2、读取客户端以后图片数据
3、通过Socket输出流将数据发给服务端
4、读取服务端反馈信息
5、关闭客户端
服务端
对于客户端并发上传图片,服务端如果单纯的使用while(true)循环式有局限性的,当A客户端连接上以后,被服务端获取到,服务端执行具体的 流程,这时B客户端连接就只有等待了,因为服务端还未处理完A客户端的请求,还有循环来回执行下须accept方法,所以暂时获取不到B客户端对象,那么 为了可让多个客户端同时并发访问服务端,那么服务端最好就是将每个客户端封装到一个单独的线程,这样就可以同时处理多个客户端的请求。如何定义线程呢?只 要明确每个客户端要在服务端执行的代码即可,将改代码存入到run方法中。
//客户端  
class UploadClient
{
public static void main(String[] args)
{
//判断上传的文件是否符合要求
if(args.length!=1)
{
System.out.println("请选择一个jpg格式的图片");
return ;
}
File file = null;
try{
file = new File(args[0]);
if(!(file.exists() && file.isFile()))
{
System.out.println("该文件有问题,要么补存在,要么不是文件");
return ;
}

if(!file.getName().endsWith(".jpg"))
{
System.out.println("图片格式错误,请重新选择");
return ;
}
if(file.length()>1024*1024*5)
{
System.out.println("文件过大,没安好心");
return ;
}
}catch (Exception e){
throw new RuntimeException("获取文件失败");
}
//创建客户端,接收客户端流对象
Socket s = null;
FileInputStream fis = null;
try{
s = new Socket("192.168.1.101",10003);
fis = new FileInputStream(file);
OutputStream out = s.getOutputStream();
//将数据写入到读取流中,传送给服务端
byte[] b = new byte[1024];
int len = 0;
while((len=fis.read(b))!=-1)
{
out.write(b,0,len);
}
s.shutdownOutput();
//获取服务端反馈的信息
InputStream in = s.getInputStream();
byte[] by = new byte[1024];
int n = in.read(by);
System.out.println(new String(by,0,n));

}catch (IOException e){
throw new RuntimeException("服务创建或读取流失败");
}
finally{
try{
if(fis!=null)
fis.close();
}catch (IOException e){
throw new RuntimeException("读取流关闭失败");
}
try{
if(s!=null)
s.close();
}catch (IOException e){
throw new RuntimeException("资源关闭失败");
}
}
}
}

//服务端
//创建服务端多线程
class ServerThread implements Runnable
{
private Socket s;
ServerThread(Socket s)
{
this.s = s;
}
public void run()
{
//获取客户端的ip
int count = 1;
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+"...connected");
FileOutputStream fos = null;
try{
//获取客户端数据,判断文件是否重名
InputStream in = s.getInputStream();
File dir = new File("D:\\File\\pics");
File file = new File(dir,ip+".jpg");
while(file.exists())
file = new File(dir,ip+"("+(count++)+").jpg");
//将数据写入到指定文件中
fos = new FileOutputStream(file);
byte[] b = new byte[1024];
int len = 0;
while((len=in.read(b))!=-1)
{
fos.write(b,0,len);
}
//将信息反馈给客户端
OutputStream out = s.getOutputStream();
out.write("上传成功".getBytes());
}catch (Exception e){
throw new RuntimeException(ip+"上传失败");
}
finally{
try{
if(fos!=null)
fos.close();
}catch (IOException e){
throw new RuntimeException("写入流关闭失败");
}
try{
if(s!=null)
s.close();
}catch (IOException e){
throw new RuntimeException("资源关闭失败");
}
}
}
}

//服务端
class UploadServer
{
public static void main(String[] args)
{
ServerSocket ss = null;
Socket s = null;
try{
//创建服务端,并接受多个客户端并发访问
ss = new ServerSocket(10003);
while(true)
{
s = ss.accept();
new Thread(new ServerThread(s)).start();
}
}catch (IOException e){
throw new RuntimeException("上传失败");
}

finally{
try{
if(s!=null)
s.close();
}catch (IOException e){
throw new RuntimeException("资源关闭失败");
}
}
}
}


4、浏览器客户端-服务端
浏览器是一个标准的客户端,它可以对服务端传送过来的数据消息进行解析,把符合应用层协议的消息部分解析后,将头信息拆包掉,传送到应用层,只保留了正确的正文主题部分显示在主题部分上。

import java.io*;
import java.net.*;
class ServerDemo{
public static void main (String[] agrs){
ServerSocket ss = new ServerSocket(11000);
Socket s = ss.accpet();
System.out.println(s.getInetAddress.getHostAddress());

InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int len = in.read(buf);
Sysrem.out.println(buf,0,len);

PrintWriter out = new PrintWriter(s.getOutputStream(),true);
out.println(“客户端登陆”);
s.close();
ss.close();
}
}

Tomcat服务器可以读取自定义的资源。
浏览器之所以这么强大,因为他有很多的解析引擎,浏览器根据发送的数据的类别来调用对应的引擎来解析,如http解析引擎、css解析引擎等。

5、URL和URLConnection
(1)URL:
URI:范围更大,条形码也包含于此范围
URL:范围较小,即域名
类 URL 代表一个统一资源定位符,它是指向互联网“资源”的指针。资源可以是简单的文件或目录,也可以是对更为复杂的对象的引用,例如对数据库或搜索引擎的查询。
http://www.socs.uts.edu.au/MosaicDocs-old/url-primer.html
上面的 URL 示例指示使用的协议为 http (超文本传输协议)并且该信息驻留在一台名为 www.socs.uts.edu.au 的主机上。主机上的信息名称为 /MosaicDocs-old/url-primer.html。主机上此名称的准确含义取决于协议和主机。该信息一般存储在文件中,但可以随时生成。该 URL 的这一部分称为路径部分。
方法:

[img]http://dl2.iteye.com/upload/attachment/0085/2416/820424ce-9bfc-34b8-9a22-bacfc8686f51.png[/img]

[img]http://dl2.iteye.com/upload/attachment/0085/2418/7bfa58b7-76cc-3be0-aed3-96561f43813d.png[/img]

应该注意的地方是,在url路径中没有指定端口的话会返回-1,。
getQuery()方法时获得该url的参数信息。
对于 http://192.168.1.254:8080/myweb/demo.html?name=wangwu&age=30,
getQuery()方法获得的就是name=wangwu&age=30这个信息。
(2)URLConnection:
方法:
URLConnection openConnection() ---> 表示到URL所引用的远程对象的链接URLConnection openConnection() ---> 表示到URL所引用的远程对象的链接
以下两个方法都是Socket的方法,因为URLConnection将Socket服务封装起来,走的是应用层。
InputStream getInputStream():获取输入流
OutputStream getOutputStream():获取输出流
6、小知识点
InetSocketAddress对象(IP+端口)
ServerSocket对象中的构造函数:
ServerSocket(int port,int backlog),其中的backlog表示队列的最大长度,即能够同时连接到服务端的客户端的最大个数。
7、域名解析(DNS)
在浏览器中输入如下url路径:http://192.168.1.100:8080/myweb/demo.html,
或者直接输入主机名:http://baidu.com.cn等,那么如何通过主机名获取IP地址,从而连接到这台主机的呢?这就需要将主机名翻译成IP地址,即域名解析:DNS。
在进行访问的时候,会在本地的hosts文件(C:\WINDOWS\system32\drivers\etc\hosts)中找对应的映射, 若有,则直接返回请求,若无,则到公网的映射列表即DNS中找对应的映射,找到后,将主机名对应的IP地址返回给本机,本机通过这个IP地址找到对应的服务器。
在我们查看无线网络连接的属性的时候,最下面的那个获取DNS服务地址一栏,就是根据这种方式来查找的。当我们选择自动获得DNS服务地址,那么你办理的联通的无线就直接从联通的那个DNS服务地址查找。
应用:提高速度,屏蔽网站等。


[align=center]-------[url]http://www.itheima.com[/url]java培训、android培训期待与您交流!------- [/align]

你可能感兴趣的:(学习日志)