<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流!
一、网络编程概述
1、网络模型有两种:OSI模型将网络通信分成七层,从高到底依次是应用层、表示层、会话层、传输层、网络层、数据链路层、物理层,TCP/IP参考模型简化了OSI模型,将网络通信分为四层,从高到低依次是应用层、传输层、网际层、主机至网络层,应用层对应OSI中的应用层、表示层和会话层,传输层对应OSI中的传输层,网际层对应OSI中的网络层,主机至网络层对应OSI中的数据链路层和物理层。网络编程主要在传输层和网际层,Javaweb开发在应用层。
2、网络通讯要素包括IP地址、端口号、传输协议。网络通信的过程是第一步找到对方IP地址,第二步将数据发送到对方指定的应用程序上。为了标识这些应用程序,给这些网络应用程序都用数字进行了标识,为了方便称呼这个数字,叫做端口或逻辑端口。通信双方要定义通信规则,这个通信规则称为协议,国际组织定义了通用协议TCP/IP,既能用于广域网,也能用于局域网。传输层主要协议有TCP和UDP,网际层主要协议有IP,应用层协议有HTTP,FTP等。
3、IP地址:网络中设备的标识,不易记忆,可用主机名代替。本地回环地址:127.0.0.1,本地主机名:localhost。端口号:用于标识进程的逻辑地址,是不同进程的标识,有效端口为0~65535,其中0~1024端口为系统使用或保留端口。
二、InetAddress
InetAddress类位于java.net包中,有两个子类Inet4Address和Inet6Address。调用InetAddress类中的静态方法getLocalHost() 返回本机InetAddress对象,非静态方法getHostAddress() 返回表示IP地址的字符串,getHostName()返回表示主机名的字符串 ,toString()打印主机名和IP地址。
InetAdress类中静态方法getByName(String) 返回InetAddress对象,如果其对应的主机名没有在网络上,会解析失败,返回对象中的主机名就是其IP地址。InetAdress.getByName("www.baidu.com") 有可能返回的InetAddress对象不唯一,返回的是InetAddress对象数组,因为百度服务器有多个主机。
三、UDP和TCP
UDP将数据即源和目的封装成数据包,不需要建立连接,每个数据包的大小限制在64K内,因为是无连接,是不可靠协议,因为不需要建立连接,速度快,生活中相当于到邮局寄包裹,TCP建立连接,形成传输数据的通道,在连接中进行大数据量传输,通过三次握手完成连接,是可靠协议,必须建立连接,效率会稍低。聊天、视频共享、网络视频会议使用UDP协议,追求速度。下载视频用TCP。TCP就相当于打电话,UDP相当于步话机。
Socket就是为网络服务提供的一种机制。通信的两端都是Socket,网络通信其实就是Socket间的通信,数据在两个Socket间通过IO传输。
UDP传输使用DatagramSocket对象,DatagramSocket表示用来发送和接收数据包的套接字。方法receive(DatagramPacket)用来接收数据包, send(DatagramPacket)用来发送数据包。
DatagramPacket常用构造方法DatagramPacket(byte[] buf, int length, InetAddress address, int port)
需求:通过UDP传输方式,将一段文字数据发送出去。
思路:1、建立UDPSocket服务,通过DatagramSocket建立;2、提供数据,并将数据封装到数据包中;3、通过DatagramSocket服务的发送功能,将数据包发送出去,4、关闭资源。
代码如下:
import java.net.*;
import java.io.*;
class UDPsender{
public static void main(String[] args){
DatagramSocket ds=null;
DatagramPacket dp=null;
try{
ds=new DatagramSocket();
}catch(SocketException e1){
throw new RuntimeException(e1);
}
byte[] buff="UDP接收端,你好!".getBytes();
try{
dp=new DatagramPacket(buff,buff.length,InetAddress.getLocalHost(),18888);
}catch(UnknownHostException e2){
throw new IllegalArgumentException(e2);
}
try{
ds.send(dp);
}catch(IOException e3){
throw new RuntimeException(e3);
}
ds.close();
}
}
需求:定义一个应用程序,用于接收UDP协议传输的数据并处理。
思路:1、定义UDPSocket服务;2、定义一个数据包,因为要存储接收到的字节数据,数据包对象中有多种功能可以提取字节数据中的不同数据信息;3,通过Socket服务的receive方法(该方法是阻塞方法)将收到的数据存入已定义好的数据包中;4,通过数据包对象的特有功能,将这些不同的数据取出,打印在控制台上,5,关闭资源。
代码如下:
import java.net.*;
import java.io.*;
class UDPreceiver{
public static void main(String[] args){
DatagramSocket ds=null;
DatagramPacket dp=null;
try{
ds=new DatagramSocket(18888);
}catch(SocketException e1){
throw new RuntimeException(e1);
}
byte[] buff=new byte[1024];
dp=new DatagramPacket(buff,buff.length);
try{
ds.receive(dp);
}catch(IOException e2){
throw new RuntimeException(e2);
}
String ip=dp.getAddress().getHostAddress();
String data=new String(dp.getData(),0,dp.getLength());
System.out.println("UDP发送端ip:"+ip+"......"+data);
ds.close();
}
}
对存储接收信息的数据包调用方法dp.getAddress().getHostAddress();获得发送端IP字符串。发送端发送端口不一定与接收端系统分配给接收端的端口一致,所以定义UDP的接收端,通常会监听一个端口,其实就是给这个接收网络应用程序定义数字标识,方便于明确哪些数据过来该应用程序可以处理。DatagramSocket构造函数可以指定监听端口。
练习:键盘录入方式发送数据,客户端与服务端是两个独立的应用程序。
代码如下:
import java.net.*;
import java.io.*;
class sender{
public static void main(String[] args){
DatagramSocket ds=null;
DatagramPacket dp=null;
try{
ds=new DatagramSocket();
}catch(SocketException e1){
throw new RuntimeException(e1);
}
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
String s=null;
byte[] buff=new byte[1024];
try{
while((s=br.readLine())!=null){
if(s.equals("over"))
break;
buff=s.getBytes();
try{
dp=new DatagramPacket(buff,buff.length,InetAddress.getLocalHost(),18888);
}catch(UnknownHostException e2){
throw new IllegalArgumentException(e2);
}
try{
ds.send(dp);
}catch(IOException e3){
throw new RuntimeException(e3);
}
}
}catch(IOException e4){
throw new RuntimeException(e4);
}
ds.close();
}
}
import java.net.*;
import java.io.*;
class receiver{
public static void main(String[] args){
DatagramSocket ds=null;
DatagramPacket dp=null;
try{
ds=new DatagramSocket(18888);
}catch(SocketException e1){
throw new RuntimeException(e1);
}
byte[] buff=new byte[1024];
dp=new DatagramPacket(buff,buff.length);
while(true){
try{
ds.receive(dp);
}catch(IOException e2){
throw new RuntimeException(e2);
}
String ip=dp.getAddress().getHostAddress();
String data=new String(dp.getData(),0,dp.getLength());
if(data.equals("over")){
System.out.println("发送端断开连接,发送结束!");
break;
}
System.out.println("UDP发送端ip:"+ip+"......"+data);
}
ds.close();
}
}
如果要实现群聊,可以发送至广播地址。
TCP传输使用Socket和ServerSocket建立客户端和服务器端,建立连接后,通过Socket中的IO流进行数据的传输。Socket构造函数需要传递服务器端的ServerSocket,或者空构造函数,调用connect(SocketAddress)指定连接。TCP分客户端和服务端,客户端对应的对象是Socket,服务端对应的对象是ServerSocket。 通过查阅Socket对象,发现在该对象建立时就可以去连接指定主机,因为TCP是面向连接的,所以在建立Socket服务时就要有服务端存在并连接成功,形成通路后,在该通道进行数据传输。
需求:给服务端发一个文本数据。
步骤:客户端:1、创建Socket服务,并指定要连接的主机和端口,创建成功,就连接,有Socket流,既有输入流,也有输出流,通过getOutputStream()方法和getInputStream()方法获取;2、向输出流中输出文本数据;3、关闭资源。服务端:1、建立服务端的socket服务ServerSocket()并监听一个端口,,2、获取连接过来的客户端对象,通过servetSocket的accept()方法完成,没有连接就会等,所以这个方法是阻塞式的,客户端如果发过来数据,那么服务端要使用对应的客户端对象并获取到该客户端对象的读取流来读取发过来的数据并打印在控制台,4、关闭服务端(可选)。
代码如下:
import java.net.*;
import java.io.*;
class TCPclient{
public static void main(String[] args){
Socket s=null;
try{
s=new Socket(InetAddress.getLocalHost(),18888);
}catch(UnknownHostException e1){
throw new RuntimeException(e1);
}catch(IOException e2){
throw new RuntimeException(e2);
}
BufferedWriter bw=null;
try{
bw=new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
}catch(IOException e3){
throw new RuntimeException(e3);
}
try{
bw.write("TCP客户端来了!");
bw.newLine();
bw.flush();
}catch(IOException e4){
throw new RuntimeException(e4);
}
try{
s.close();
}catch(IOException e5){
throw new RuntimeException(e5);
}
}
}
import java.net.*;
import java.io.*;
class TCPserver{
public static void main(String[] args){
ServerSocket ss=null;
Socket s=null;
try{
ss=new ServerSocket(18888);
}catch(SocketException e1){
throw new RuntimeException(e1);
}catch(IOException e2){
throw new RuntimeException(e2);
}
try{
s=ss.accept();
}catch(IOException e3){
throw new RuntimeException(e3);
}
System.out.println("ip"+s.getLocalAddress().getHostAddress()+"......"+"已连接");
BufferedReader br=null;
try{
br=new BufferedReader(new InputStreamReader(s.getInputStream()));
}catch(IOException e4){
throw new RuntimeException(e4);
}
String buff=null;
try{
while((buff=br.readLine())!=null)
System.out.println("ip"+s.getLocalAddress().getHostAddress()+"......"+buff);
}catch(IOException e5){
throw new RuntimeException(e5);
}
try{
s.close();
}catch(IOException e6){
throw new RuntimeException(e6);
}
try{
ss.close();
}catch(IOException e7){
throw new RuntimeException(e7);
}
}
}
演示TCP传输的客户端和服务端的互访。需求:客户端给服务端发送数据,服务端收到后,给客户端反馈信息。
步骤:客户端:1、建立socket服务,指定要连接主机和端口;2、获取socket流中的输出流,将数据写到该流中,通过网络发送给服务端,3、获取socket流中的输入流,将服务端反馈的数据获取到,并打印,4关闭客户端资源。服务端:1、建立ServerSocket服务,指定监听端口;2、获取客户端的socket;3、获取客户端socket的输入流,得到客户端发来的数据,再获得客户端socket的输出流,将反馈数据发出;4、关闭客户端和服务端。
代码如下:
import java.net.*;
import java.io.*;
class TCPclient2{
public static void main(String[] args){
Socket s=null;
try{
s=new Socket(InetAddress.getLocalHost(),18888);
}catch(UnknownHostException e1){
throw new RuntimeException(e1);
}catch(IOException e2){
throw new RuntimeException(e2);
}
BufferedWriter bw=null;
BufferedReader br=null;
try{
bw=new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
}catch(IOException e3){
throw new RuntimeException(e3);
}
try{
br=new BufferedReader(new InputStreamReader(s.getInputStream()));
}catch(IOException e4){
throw new RuntimeException(e4);
}
try{
bw.write("TCP客户端来了!");
bw.newLine();
bw.flush();
s.shutdownOutput();
}catch(IOException e5){
throw new RuntimeException(e5);
}
String buff=null;
try{
while((buff=br.readLine())!=null)
System.out.println(buff);
}catch(IOException e6){
throw new RuntimeException(e6);
}
try{
s.close();
}catch(IOException e7){
throw new RuntimeException(e7);
}
}
}
import java.net.*;
import java.io.*;
class TCPserver2{
public static void main(String[] args){
ServerSocket ss=null;
Socket s=null;
try{
ss=new ServerSocket(18888);
}catch(SocketException e1){
throw new RuntimeException(e1);
}catch(IOException e2){
throw new RuntimeException(e2);
}
try{
s=ss.accept();
}catch(IOException e3){
throw new RuntimeException(e3);
}
System.out.println("ip"+s.getLocalAddress().getHostAddress()+"......"+"已连接");
BufferedReader br=null;
BufferedWriter bw=null;
try{
br=new BufferedReader(new InputStreamReader(s.getInputStream()));
}catch(IOException e4){
throw new RuntimeException(e4);
}
try{
bw=new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
}catch(IOException e5){
throw new RuntimeException(e5);
}
String buff=null;
try{
while((buff=br.readLine())!=null)
System.out.println("ip"+s.getLocalAddress().getHostAddress()+"......"+buff);
}catch(IOException e5){
throw new RuntimeException(e5);
}
try{
bw.write("服务器已连接");
bw.newLine();
bw.flush();
}catch(IOException e6){
System.out.println("服务器写异常");
throw new RuntimeException(e6);
}
try{
s.close();
}catch(IOException e7){
throw new RuntimeException(e7);
}
try{
ss.close();
}catch(IOException e8){
throw new RuntimeException(e8);
}
}
}
TCP练习:需求:建立一个文本转换服务器,客户端给服务端发送文本,服务端会将文本转成大写再返回给客户端。而且客户端可以不断的进行文本转换,当客户端输入over时,转换结束。
分析:客户端:既然是操作设备上的数据,那么就可以使用IO技术,并按照IO的操作规律来思考。源:键盘录入,目的:网络设备—网络输出流。而且操作的是文本数据,可以选择字符流。
步骤:1、建立服务,2、获取键盘录入,3、将数据发给服务端,4、获取服务端返回的大写数据,5,结束,关闭资源。都是文本数据,可以使用字符流进行操作。同时提高效率,加入缓冲。
代码如下:
import java.net.*;
import java.io.*;
class TCPserver3{
public static void main(String[] args){
ServerSocket ss=null;
Socket s=null;
try{
ss=new ServerSocket(18888);
}catch(SocketException e1){
throw new RuntimeException(e1);
}catch(IOException e2){
throw new RuntimeException(e2);
}
try{
s=ss.accept();
}catch(IOException e3){
throw new RuntimeException(e3);
}
System.out.println("ip"+s.getLocalAddress().getHostAddress()+"......"+"已连接");
BufferedReader br=null;
BufferedWriter bw=null;
try{
br=new BufferedReader(new InputStreamReader(s.getInputStream()));
}catch(IOException e4){
throw new RuntimeException(e4);
}
try{
bw=new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
}catch(IOException e5){
throw new RuntimeException(e5);
}
String buff=null;
try{
while((buff=br.readLine())!=null){
char[] c=buff.toCharArray();
for(int i=0;i<buff.length();i++){
if(c[i]>='a' && c[i]<='z')
c[i]=(char)(c[i]-32);
}
bw.write(new String(c,0,buff.length()));
bw.newLine();
bw.flush();
}
}catch(IOException e5){
throw new RuntimeException(e5);
}
try{
s.close();
}catch(IOException e7){
throw new RuntimeException(e7);
}
try{
ss.close();
}catch(IOException e8){
throw new RuntimeException(e8);
}
}
}
import java.net.*;
import java.io.*;
class TCPclient3{
public static void main(String[] args){
Socket s=null;
try{
s=new Socket(InetAddress.getLocalHost(),18888);
}catch(UnknownHostException e1){
throw new RuntimeException(e1);
}catch(IOException e2){
throw new RuntimeException(e2);
}
BufferedWriter bw=null;
BufferedReader br=null;
try{
bw=new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
}catch(IOException e3){
throw new RuntimeException(e3);
}
try{
br=new BufferedReader(new InputStreamReader(s.getInputStream()));
}catch(IOException e4){
throw new RuntimeException(e4);
}
BufferedReader in_br=null;
in_br=new BufferedReader(new InputStreamReader(System.in));
String buff=null;
try{
while((buff=in_br.readLine())!=null){
if("over".equals(buff))
break;
bw.write(buff);
bw.newLine();
bw.flush();
System.out.println(br.readLine());
}
s.shutdownOutput();
}catch(IOException e5){
throw new RuntimeException(e5);
}
try{
s.close();
}catch(IOException e7){
throw new RuntimeException(e7);
}
}
}
注意一点:客户端和服务端都有阻塞式的方法,客户端为读取键盘录入,服务端为读取客户端数据,这些方法若没有读到结束标记,那么就会一直等,而导致两端都在等待。PrintWriter既能接收字节流,也能接收字符流,还能自动刷新。改用PrintWriter效果更好。
再使用TCP协议复制文件时,通常以时间戳作为结束标记long time=System.currentTimeMillis(),用data流,客户端先输入writeLong(time),服务端先writeLong(time)得到结束标记。再循环判断。
调用Socket的shutdownOutput()方法关闭输出流,相当于给流中加结束标记-1。
演示使用TCP上传图片
思路:1、建立客户端点和服务端点;2、 读取客户端已有的图片数据;3、通过socket输出流将数据发给服务端;4、读取服务端反馈信息;5、关闭资源。
代码如下:
import java.net.*;
import java.io.*;
class TCPclient4{
public static void main(String[] args){
Socket s=null;
try{
s=new Socket(InetAddress.getLocalHost(),18888);
}catch(UnknownHostException e1){
throw new RuntimeException(e1);
}catch(IOException e2){
throw new RuntimeException(e2);
}
BufferedReader br=null;
try{
br=new BufferedReader(new InputStreamReader(s.getInputStream()));
}catch(IOException e3){
throw new RuntimeException(e3);
}
OutputStream os=null;
try{
os=s.getOutputStream();
}catch(IOException e4){
throw new RuntimeException(e4);
}
FileInputStream fis=null;
try{
fis=new FileInputStream("TCPpic.jpg");
}catch(IOException e5){
throw new RuntimeException(e5);
}
byte[] buff=new byte[1024];
int len=0;
try{
while((len=fis.read(buff))!=-1){
os.write(buff,0,len);
os.flush();
}
s.shutdownOutput();
}catch(IOException e6){
throw new RuntimeException(e6);
}
try{
System.out.println(br.readLine());
}catch(IOException e7){
throw new RuntimeException(e7);
}
try{
s.close();
}catch(IOException e8){
throw new RuntimeException(e8);
}
}
}
import java.net.*;
import java.io.*;
class TCPserver4{
public static void main(String[] args){
ServerSocket ss=null;
Socket s=null;
try{
ss=new ServerSocket(18888);
}catch(SocketException e1){
throw new RuntimeException(e1);
}catch(IOException e2){
throw new RuntimeException(e2);
}
try{
s=ss.accept();
}catch(IOException e3){
throw new RuntimeException(e3);
}
System.out.println("ip"+s.getLocalAddress().getHostAddress()+"......"+"已连接");
InputStream is=null;
BufferedWriter bw=null;
FileOutputStream fos=null;
try{
is=s.getInputStream();
}catch(IOException e4){
throw new RuntimeException(e4);
}
try{
bw=new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
}catch(IOException e5){
throw new RuntimeException(e5);
}
try{
fos=new FileOutputStream("TCPpic_copy.jpg");
}catch(IOException e6){
throw new RuntimeException(e6);
}
byte[] buff=new byte[1024];
int len=0;
try{
while((len=is.read(buff))!=-1){
fos.write(buff,0,len);
fos.flush();
}
}catch(IOException e5){
throw new RuntimeException(e5);
}
try{
bw.write("上传成功!");
bw.newLine();
bw.flush();
}catch(IOException e6){
throw new RuntimeException(e6);
}
try{
s.close();
}catch(IOException e7){
throw new RuntimeException(e7);
}
try{
ss.close();
}catch(IOException e8){
throw new RuntimeException(e8);
}
}
}
这个服务端有个局限性,当A客户端连接上以后,被服务端获取到,服务端执行具体流程,这时B客户端连接,只有等待,因为服务端还没有处理完A客户端的请求,如果通过循环,还没有循环回来执行下一次accept()方法,所以暂时获取不到B客户端对象。那么为了可以让多个客户端同时并发访问服务端,服务端最好就是将每个客户端封装到一个单独的线程中,这样就可以同时处理多个客户端请求。
只要明确了每一个客户端要在服务端执行的即可,就将该代码存入run()方法中。
需求:客户端通过键盘录入用户名,服务端对这个用户名进行校验,如果该用户存在,在服务端显示XXX,已登陆,并在客户端显示XXX,欢迎光临。如果该用户不存在,在服务端显示XXX,尝试登陆,并在客户端显示XXX,该用户不存在,最多就登陆三次。本例将数据库简化为文本文件,存储用户名信息。
代码如下:
import java.net.*;
import java.io.*;
class TCPserver5{
static class user_thread implements Runnable{
private Socket s;
public user_thread(Socket s){
this.s=s;
}
public void run(){
BufferedReader fbr=null;
BufferedReader br=null;
BufferedWriter bw=null;
try{
br=new BufferedReader(new InputStreamReader(s.getInputStream()));
}catch(IOException e1){
throw new RuntimeException(e1);
}
try{
bw=new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
}catch(IOException e2){
throw new RuntimeException(e2);
}
try{
fbr=new BufferedReader(new FileReader("user.txt"));
}catch(IOException e3){
throw new RuntimeException(e3);
}
String name=null,search=null;
boolean flag=false;
int count=0,i=1;
for(;i<=3;i++){
try{
name=br.readLine();
}catch(IOException e4){
throw new RuntimeException(e4);
}
try{
while((search=fbr.readLine())!=null){
if(search.equals(name)){
flag=true;
break;
}
}
}catch(IOException e5){
throw new RuntimeException(e5);
}
try{
if(flag){
bw.write(name+"欢迎光临");
bw.newLine();
bw.flush();
break;
}else{
bw.write("你已错误登陆"+i+"次");
bw.newLine();
bw.flush();
}
}catch(IOException e6){
throw new RuntimeException(e6);
}
}
if(i>3){
try{
bw.write("你已错误登陆三次,"+name+"该用户不存在,登陆失败!");
bw.newLine();
bw.flush();
}catch(IOException e7){
throw new RuntimeException(e7);
}
System.out.println("用户"+name+"尝试登陆");
}
try{
s.close();
}catch(IOException e8){
throw new RuntimeException(e8);
}
}
}
public static void main(String[] args){
ServerSocket ss=null;
try{
ss=new ServerSocket(18888);
}catch(SocketException e1){
throw new RuntimeException(e1);
}catch(IOException e2){
throw new RuntimeException(e2);
}
while(true){
Socket s=null;
try{
s=ss.accept();
}catch(IOException e4){
throw new RuntimeException(e4);
}
System.out.println("ip"+s.getLocalAddress().getHostAddress()+"......"+"已连接");
new Thread(new user_thread(s)).start();
}
}
}
import java.net.*;
import java.io.*;
class TCPclient5 {
public static void main(String[] args){
Socket s=null;
try{
s=new Socket(InetAddress.getLocalHost(),18888);
}catch(UnknownHostException e1){
throw new RuntimeException(e1);
}catch(IOException e2){
throw new RuntimeException(e2);
}
BufferedReader br=null;
try{
br=new BufferedReader(new InputStreamReader(s.getInputStream()));
}catch(IOException e3){
throw new RuntimeException(e3);
}
BufferedWriter bw=null;
try{
bw=new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
}catch(IOException e4){
throw new RuntimeException(e4);
}
BufferedReader br_in=null;
br_in=new BufferedReader(new InputStreamReader(System.in));
int i=0;
String info=null;
try{
for(;i<3;i++){
bw.write(br_in.readLine());
bw.newLine();
bw.flush();
info=br.readLine();
System.out.println(info);
if(info.contains("欢迎"))
break;
}
}catch(IOException e5){
throw new RuntimeException(e5);
}
if(i==3)
System.out.println("登陆失败");
try{
s.close();
}catch(IOException e6){
throw new RuntimeException(e6);
}
}
}
演示客户端和服务端:
1、客户端是浏览器,服务端是自定义
对服务端进行编程,向Socket输出流中输出反馈信息,当浏览器访问时,得到反馈信息。
2、客户端是浏览器,服务端是Tomcat服务器
在Tomcat服务器上设置网页,当浏览器访问该网页时,将网页内容输出给客户端,显示在浏览器中。
3、客户端自定义 服务端Tomcat服务器
可以仿照浏览器,再访问Tomcat服务器时,发送带有HTTP协议请求消息头,Tomcat服务器收到后会解析,再将请求的网页内容发回给客户端。
4、客户端采用自定义图形化界面,服务端Tomcat服务器
参照3,可以在图形化界面文本区显示服务器发回的带有HTTP协议响应消息头的全部信息。
四、URL对象
在第三部分用自定义图形化界面访问服务端时,用户输入的信息包含有HTTP协议请求消息头的多个内容,需要在程序中对用户输入的字符串进行拆分,分别得到各种信息,再组成格式化的HTTP协议请求消息头发给服务端,比较麻烦。在java.net包中有URL类(统一资源定位符),可以通过将用户输入的字符串(URL地址)封装成URL对象,方便连接。URL用于应用层。
URL对象的方法:
String getFile() 获取要访问的资源(文件)
String getHost()获取要访问的主机名
String getPath() 获取要访问的资源路径
int getPort() 获取要访问的服务端端口号,没有指定端口,getPort()返回-1,在编程时,封装Socket对象访问时,判断getPort()如果是-1,则在Socket对象中设置port值,一般为80。
String getProtocol()获取该URL采取的协议名称
String getQuery() 获取URL中的请求信息条件,通常是键值对的形式。
URLConnection openConnection() 获取主机连接对象,返回 URLConnection的子类HttpURLConnection对象,实际上返回对象中封装了一个Socket对象用于访问主机,可调用 getInputStream(),getOutputStream()获得输入流和输出流, openStream()先连接再获取流,URLConnection对象可以获取响应消息头字段类型和字段值。
五、小知识点
Socket有一个空参数的构造函数,创建后用connect(SocketAddress)连接。SocketAddress有一子类InetSocketAddress,它封装了IP地址和端口, ServerSocket有一构造函数ServerSocket(int port,int backlog),参数backlog表示同时连接到服务器端的在线数,一般最多50。
五、域名解析
想要将主机名翻译成ip地址,需要域名解析服务器DNS。在访问DNS之前,计算机先查询本机C:\windows\systems\drivers\ext\host,当找到对应的IP地址,直接连接该IP地址,若没有找到,再访问DNS。本机回环地址127.0.0.1与Host之间的对应关系就存储在给该文件中。