1.网络模型
OSI参考模型:应用层,表示层,会话层,传输层,网络层,数据链路层,物理层;
TCP/IP参考模型:应用层,传输层,网际层,主机至网络层;
一般来说开发处于传输层和网际层
应用层协议有:FTP,HTTP等;
传输层协议有:UDP,TCP等;
网际层协议有:IP;
通常用户操作的是应用层,而编程人员要做的是处理传输层和网际层,各层对数据进行封装成包和解包;
2.网络通信三要素
(1).IP地址
概述:网络中的设备标识,不容易记忆,可用主机名表示,两者存在映射关系;
特别:本地回环地址:127.0.0.1;本地主机名:localhost;
Java中对IP地址的描述:java.net.InetAddress
InetAddress中的一些基本方法:
byte[] getAddress():返回此InetAddress对象的原始IP地址;
static InetAddress getByAddress(byte[] addr):在给定原始IP地址的情况下返回InetAddress对象;
static InetAddress[] getAllByName(String host):在给定主机的情况下,根据系统上配置的名称返回IP地址所组成的数组;需要遍历;
static InetAddress getByName(Sting hostName):在给定主机名的情况下确定主机的IP地址;
String getHostAddress():返回IP地址字符串;
String getHostName():返回此IP地址的主机名;
static InetAddress getLocalHost():返回本地主机;
示例:
package com.zr.day23; import java.net.*; class InetAddressDemo { public static void main(String[] args) throws UnknownHostException { //获取本地主机的InetAddress对象; InetAddress ia = InetAddress.getLocalHost(); //分别获取主机名和IP地址 String ip = ia.getHostAddress(); String host = ia.getHostName(); System.out.println(ip+"::"+host); //根据主机名获取InetAddress对象 InetAddress[] ias = InetAddress.getAllByName("www.baidu.com"); for(InetAddress i : ias) { String iip = i.getHostAddress(); String ihost = i.getHostName(); System.out.println(iip+":::"+ihost); } //根据主机ip地址获取InetAddress对象 InetAddress iia = InetAddress.getByName("180.97.33.108"); String iiip = iia.getHostAddress(); String iiihost = iia.getHostName(); System.out.println(iiip+"::::"+iiihost); } }
(2).端口号
概述:用于标识应用程序的逻辑地址;
特别:有效端口:0~65535;其中系统使用或保留端口是:0~1024;
在定义端口的时候要特别注意不能重用,否则会出现Address already in use 的BindException;
(3).传输协议
通信规则,传输层的通信协议有:TCP,UDP;
UDP:
1).将数据以及源和目的封装成数据报包,面向无连接;
2).每个数据包大小限制在64K范围内;
3).不可靠协议;
4),速度快;
TCP:
1).通信时要建立连接,形成数据传输的通道;
2).在连接中进行大量数据传输;
3).可靠协议,通过三次握手完成连接;
4).必须建立连接,效率稍低;
Socket:
Socket就是为网络服务提供的一种机制,通信的两端都有Socket,数据在两个Socket之间通过IO传输;
3.UDP通信
(1).UDP协议的Socket对象和数据报包对象;
DatagramSocket:
构造方法:
1).DatagramSocket():构造数据报套接字,有系统分配可以的端口;
2).DatagramSocket():构造数据报套接字,自定义绑定的端口号;
常用方法:
1).void close():关闭此数据报套接字;
2).void receive(DatagramPacket p):从此套接字接收数据报包,用数据报包对象来接收;
3).void send(DatagramPacket p):从此套接字发送指定的数据报包,数据都被封装到了数据报包对象中;
DatagramPacket:
构造方法:
1).用来接收的:DatagramPacket(byte[] buf, int length)
2).用来发送的:DatagramPacket(byte[] buf, int length, InetAddress address, int port)
常用方法:用来获取数据报包中的各种成分
InetAddress getAddress():此数据报将要发送或者被接受的主机地址;
byte[] getData():返回数据缓冲区;
int getLength():返回将要发送或接收的数据的长度;
int getPort():返回主机的端口号;
(2).UDP发送端
具体步骤:
1).建立UDPSocket服务;
2).提供数据,并将数据封装为数据报包对象;
3).通过Socket服务的发送功能,将数据报包发送出去;
4).关闭Socket服务;
具体代码实现:
import java.net.*; //定义UDP发送端 class UDPSend { public static void main(String[] args)throws Exception { //建立UDPSocket服务;可以不用指定端口,由系统分配; DatagramSocket ds = new DatagramSocket(9999); //建立数据报包对象,封装数据; //需要指定要发送的数据,目的地址,目的端口; byte[] buf = "this is sharon".getBytes(); DatagramPacket dp = new DatagramPacket(buf,buf.length,InetAddress.getByName("10.0.0.227"),10000); //调用Socket服务中的发送功能,发送数据; ds.send(dp); //关闭Socket服务; ds.close(); } }
(3).UDP接收端
具体步骤:
1).建立UDPSocket服务,并且监听某个端口,明确哪些数据被接受后由该应用程序处理;
2).建立一个数据报包对象,用来封装接收的数据,使用数据报包对象提供的丰富的方法来获取有用的信息;
3).通过Socket服务的接收功能,获取网络数据;
4).关闭Socket服务;
具体代码实现:
import java.net.*; //定义UDP接收端 class UDPRec { public static void main(String[] args)throws Exception { //建立UDPSocket服务,必须要指定接收的数据的数字标识,即端口; DatagramSocket ds = new DatagramSocket(10000); //建立数据报包对象,用来接收网络数据; byte[] buf = new byte[1024]; DatagramPacket dp = new DatagramPacket(buf,buf.length); //调用Socket服务中的接收功能,接收数据; ds.receive(dp); //处理数据,获取有用信息; //获取源IP; String ip = dp.getAddress().getHostAddress(); //获取源端口; int port = dp.getPort(); //获取发送的数据; String data = new String(dp.getData(),0,dp.getLength()); System.out.println(ip+"::"+port+":"+data); //关闭Socket服务; ds.close(); } }
(4).UDP通信示例
1).键盘录入
//从键盘录入数据,然后通过UDP发送数据; import java.io.*; import java.net.*; //定义发送端,发送的是键盘录入的数据; class UDPKeySend { public static void main(String[] args) throws Exception { //获取键盘录入 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); //定义Socket服务 DatagramSocket ds = new DatagramSocket(); for(String line=null; (line=br.readLine())!=null;) { //定义循环终止条件 if("over".equals(line)) break; //定义数据报包对象 byte[] buf = line.getBytes(); DatagramPacket dp = new DatagramPacket(buf,buf.length,InetAddress.getByName("10.0.0.227"),10001); //发送数据 ds.send(dp); } //关闭流资源; br.close(); //关闭Socket服务; ds.close(); } } //定义接收端,因为是键盘录入,所以接收端的接收服务要一直开启,以便接收数据; //接收端接收数据后将数据信息输出在控制台上; class UDPKeyRec { public static void main(String[] args) throws Exception { //创建Socket服务;指定要处理的数字标识 DatagramSocket ds = new DatagramSocket(10001); //不停的接收数据; while(true) { //定义数据报包对象 byte[] buf = new byte[1024]; DatagramPacket dp = new DatagramPacket(buf,buf.length); //接收数据 ds.receive(dp); //处理数据 String ip = dp.getAddress().getHostAddress(); int port = dp.getPort(); String data = new String(dp.getData(),0,dp.getLength()); System.out.println(ip+"::"+port+":"+data); } } }
2).UDP聊天通信
//实现UDP通信下的聊天功能; //即同时能接收数据,又能发送数据,多线程技术; import java.io.*; import java.net.*; //定义发送线程要执行的代码; class Send implements Runnable { //接收Socket服务对象; private DatagramSocket ds; Send(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("over".equals(line)) break; //封装成数据报包对象 byte[] buf = line.getBytes(); //地址可以设置为广播地址; DatagramPacket dp = new DatagramPacket(buf,buf.length,InetAddress.getByName("10.0.0.227"),10002); ds.send(dp); } br.close(); ds.close(); } catch (Exception e) { throw new RuntimeException("发送端创建失败"); } } } //定义接收线程要执行的代码; class Rec implements Runnable { private DatagramSocket ds; Rec(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(); int port = dp.getPort(); String data = new String(dp.getData(),0,dp.getLength()); System.out.println(ip+"::"+port+":"+data); } } catch (Exception e) { throw new RuntimeException("接收端创建失败"); } } } class UDPChatDemo { public static void main(String[] args)throws Exception { DatagramSocket send = new DatagramSocket(); DatagramSocket rec = new DatagramSocket(10002); new Thread(new Send(send)).start(); new Thread(new Rec(rec)).start(); } }
4.TCP通信
(1).TCP协议下的Socket和ServerSocket
java.net.Socket:客户端对象;
1).构造方法:客户端对象在创建时需要指定所要连接的服务器;
无参构造:Socket():没有指定要连接的服务器;
可以使用方法:void connnet(SocketAddress endPoint):来连接指定的服务器;
有参构造:Socket(InnetAddress address, int port):在创建客户端对象时就指定要连接的服务器;
有参构造:Socket(String host, int port):
2).常用方法:
close():关闭此套接字;
void bind(SocketAddress bindpoint):绑定本地地址;
InputStream getInputStream():返回此套接字的输入流;
OutputStream getOutputStream():返回此套接字的输出流;
InnetAddress getInnetAddress():返回此套接字连接的地址;
int getPort():返回套接字连接到的远程端口;
InnetAddress getLocalAddress():获取套接字绑定的本地地址;
int getLocalPort():返回套接字绑定的本地端口;
void shutdownInput():关闭此套接字的输入流;
void shutdownOutput():关闭此套接字的输出流;
java.net.ServerSocket:服务器端对象
构造方法:服务器对象在创建时要绑定指定的端口,以便处理特定端口的应用程序;
ServerSocket():空参,没有指定数字标识;
ServerSocket(int port):创建绑定特定端口的服务器套接字;
ServerSocket(int port, int backlog):绑定特定端口,同时指定可以连接到该服务器的带有指定数字标识的客户端对象的最大数目;
ServerSocket(int port ,int basklog, InnetAddress bindAdd):指定端口,设置数量,同时绑定本地地址;
常用方法:
Socket accept():侦听并接受此套接字的连接,获取客户端的套接字对象,利用该对象中的流和客户端进行通信数据传输;
void close():关闭此套接字;
InnetAddress getInnetAddress():返回此服务器套接字的本地地址;
int getLocalPort():返回此套接字在其上侦听的端口;
(2).客户端和服务器端的通信原理
服务器端创建对象,指定要侦听的端口;
在客户端对象创建时,需要建立服务器连接,指定要连接的服务器;
建立连接后通过Socket中的IO流进行数据的传输;
原理图:
(3).TCP简单的发送与接收示例
//TCP客户端和服务器端之间的简单发送与接收 import java.net.*; import java.io.*; //客户端 //1.建立Socket对象,确定要连接的服务器; //2.获取输出流,向服务器发送数据; //3.关闭服务; class Client { public static void main(String[] args) throws Exception { //建立客户端的套接字对象; //指定要连接的服务器; Socket s = new Socket("10.0.0.170",20000); //获取Socket对象的输出流 OutputStream out = s.getOutputStream(); //向服务器端发送数据 byte[] buf = "你好,服务端".getBytes(); out.write(buf); //关闭服务 s.close(); } } //服务器端 //1.建立ServerSocket,监听端口,提供服务; //2.获取客户端对象; //3.获取客户端对象的输入流,读取客户端发送的数据; //4.关闭服务; class Server { public static void main(String[] args) throws Exception { //建立服务端的套接字对象,监听端口 ServerSocket ss = new ServerSocket(20000); //获取客户端对象,accept()为阻塞式方法; Socket s = ss.accept(); //在服务器端获取客户端的主机信息 String ip = s.getInetAddress().getHostAddress(); System.out.println(ip+" connecting ..."); //获取客户端对象的输入流 InputStream in = s.getInputStream(); //通过输入流读取数据; byte[] buf = new byte[1024]; int len = in.read(buf); System.out.println(new String(buf,0,len)); s.close(); } }
(4).TCP客户端和服务端的互访
//实现TCP协议下服务器端和客户端之间的互访 import java.net.*; import java.io.*; //客户端:向服务器发送信息,然后要能接收服务器返回的信息; class Client1 { public static void main(String[] args) throws Exception { //定义客户端套接字对象,连接服务器 Socket s = new Socket("10.0.0.170",20001); //获取输出流,发送数据 OutputStream out = s.getOutputStream(); out.write("尼豪,服务端!".getBytes()); //获取输入流,接收返回信息 InputStream in = s.getInputStream(); byte[] buf = new byte[1024]; int len = in.read(buf); System.out.println(new String(buf,0,len)); //关闭服务 s.close(); } } //服务器端:要能接受客户端发送的信息,同时向客户端发送反馈信息; class Server1 { public static void main(String[] args)throws Exception { //定义服务端套接字对象,监听接口 ServerSocket ss = new ServerSocket(20001); //获取客户端套接字对象 Socket s = ss.accept(); //获取客户端的主机信息 String ip = s.getInetAddress().getHostAddress(); System.out.println(ip+" connneted ..."); //获取输入流,接收客户端的数据 InputStream in = s.getInputStream(); byte[] buf = new byte[1024]; int len = in.read(buf); System.out.println(new String(buf,0,len)); //睡眠一段时间后发动数据; Thread.sleep(5000); //获取输出流,向客户端返回反馈信息; OutputStream out = s.getOutputStream(); out.write("hello, client, welcom".getBytes()); //关闭服务 s.close(); } }
(5).文本转换服务器
客户端向服务器端发送文本,服务器端会将文本转成大写形式返回给客户端,而且客户端可以不断的进行文本转换,当客户端输入over时,转换结束;//实现文本转换 import java.io.*; import java.net.*; //客户端:将键盘录入的信息发送至服务器端,获取服务器端返回的大写形式文本; class TransClient { public static void main(String[] args) throws Exception { //创建套接字对象,连接服务器 Socket s = new Socket("10.0.0.170",20002); //获取键盘录入信息 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); //获取套接字对象的输出流,向服务器端发送录入信息; BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); //获取套接字对象的输入流,获取转码后的文本数据; BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream())); for(String line = null;(line=br.readLine())!=null;) { //定义循环结束条件 if("over".equals(line)) break; bw.write(line); //此处容易出现的问题是没有定义结束标记,出现服务器和客户端都在等待的情况; //定义行结束标记 bw.newLine(); bw.flush(); //读取转码后的文本 String transline = in.readLine(); System.out.println("Server:"+transline); } br.close(); //关闭服务; s.close(); } } //服务器端:接收客户端的文本数据,进行转码,返回给客户端; class TransServer { public static void main(String[] args)throws Exception { //创建服务器端套接字对象,监听端口; ServerSocket ss = new ServerSocket(20002); //获取客户端套接字对象 Socket s = ss.accept(); //获取客户端的主机信息 String host = s.getInetAddress().getHostName(); String ip = s.getInetAddress().getHostAddress(); System.out.println(ip+" connected ..."); //获取输入流,接收客户端发送的原始文本,显示在服务器端的控制台 BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream())); //获取输出流,转码文本,发送个客户端; BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); for(String line = null;(line=br.readLine())!=null;) { System.out.println(host+":"+line); bw.write(line.toUpperCase()); //定义行结束标记 bw.newLine(); bw.flush(); } //关闭服务; s.close(); } }
文本转换的优化://文本转换的优化 //在之前的示例中:输出流在向外写数据时,因为缺少行结束标记,所以输入流无法判断是否改行结束; //使用BufferedWriter中的newLine()方法和flush()方法可以定义结束标记 //现在优化:将输出流对象装饰在Printwriter中,利用其提供的打印方法,以及自动刷新功能,来简化操作; import java.io.*; import java.net.*; class Trans_Client { public static void main(String[] args) throws Exception { Socket s = new Socket("10.0.0.170",20003); BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); //PrintWriter可以封装字节流和字符流的对象 //定义自动刷新 PrintWriter pw = new PrintWriter(s.getOutputStream(),true); BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream())); for(String line = null; (line=br.readLine())!=null;) { if("over".equals(line)) break; pw.println(line); String returnLine = in.readLine(); System.out.println("Server:"+returnLine); } br.close(); s.close(); } } class Trans_Server { public static void main(String[] args) throws Exception { ServerSocket ss = new ServerSocket(20003); Socket s = ss.accept(); String host = s.getInetAddress().getHostName(); String ip = s.getInetAddress().getHostAddress(); System.out.println(ip+" connected..."+host); PrintWriter pw = new PrintWriter(s.getOutputStream(),true); BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream())); for(String line = null;(line = br.readLine())!=null;) { System.out.println(host+":"+line); pw.println(line.toUpperCase()); } s.close(); } }
(6).TCP复制文件
//上传文件,文件的复制 import java.net.*; import java.io.*; //客户端:关联文件,上传文件;接收服务端返回的信息; class FileClient { public static void main(String[] args) throws Exception { //创建套接字对象,连接服务器 Socket s = new Socket("10.0.0.170",20004); //建立文件输入流,关联文件 BufferedReader br = new BufferedReader(new FileReader("demo.txt")); //获取套接字输出流,上传文件 PrintWriter out = new PrintWriter(s.getOutputStream(),true); for(String line=null; (line = br.readLine())!=null; System.out.println("正在上传...")) { out.println(line); } //服务器端无法判断是否客户端的输出流结束; //输出流结束,否则出现两端都等待的情况; s.shutdownOutput(); //关闭文件输入流 br.close(); //获取套接字输入流,获取反馈信息 InputStream in = s.getInputStream(); byte[] buf = new byte[1024]; int len = in.read(buf); System.out.println(new String(buf,0,len)); //关闭服务 s.close(); } } //服务器端:接收客户端上传的文件,并存储;向客户端反馈信息; class FileServer { public static void main(String[] args)throws Exception { //创建套接字对象,监听端口 ServerSocket ss = new ServerSocket(20004); //获取客户端套接字对象,获取主机信息 Socket s = ss.accept(); //获取套接字输入流,接收文件数据 BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream())); //建立文件输出流,关联文件 PrintWriter pw = new PrintWriter(new FileWriter("tcp_copy.txt"),true); for(String line=null; (line=in.readLine())!=null; System.out.println("继续上传...")) { pw.println(line); } pw.close(); //获取套接字输出流,发送反馈信息 OutputStream out = s.getOutputStream(); out.write("上传成功!".getBytes()); //关闭服务; s.close(); } }
(7).TCP上传图片
//上传图片 import java.io.*; import java.net.*; //客户端:定义字节输入流,关联图片;接收服务器端的反馈信息; class PicClient { public static void main(String[] args) throws Exception { //创建客户端套接字对象,连接服务器; Socket s = new Socket("10.0.0.170",20005); //创建字节输入流,关联文件 FileInputStream fis = new FileInputStream("demo.png"); //获取套接字对象的输出流,向服务器上传数据; OutputStream out = s.getOutputStream(); byte[] bytebuf = new byte[1024]; for(int len=0; (len=fis.read(bytebuf))!=-1;System.out.println("正在上传...")) { out.write(bytebuf,0,len); } //关闭套接字对象的输出流; s.shutdownOutput(); fis.close(); //获取套接字对象的输入流,接收服务器返回的信息; InputStream in = s.getInputStream(); byte[] buf = new byte[1024]; int len = in.read(buf); System.out.println(new String(buf,0,len)); //关闭服务; s.close(); } } //服务器端:接收服务器端上传的图片,建立字节输出流,存储图片;向客户端返回反馈信息; class PicServer { public static void main(String[] args) throws Exception { //创建服务器套接字对象,监听端口 ServerSocket ss = new ServerSocket(20005); //获取客户端套接字对象,输出主机信息 Socket s = ss.accept(); String ip = s.getInetAddress().getHostAddress(); System.out.println(ip+" connected...."); //获取套接字对象的输入流,获取客户端的上传数据 InputStream in = s.getInputStream(); //建立字节输出流,关联文件,存储图片; FileOutputStream fos = new FileOutputStream("copy.jpg"); byte[] buf = new byte[1024]; for(int len=0; (len=in.read(buf))!=-1; System.out.println("继续上传。。。")) { fos.write(buf,0,len); } fos.close(); //获取套接字对象的输出流,向客户端返回信息; OutputStream out = s.getOutputStream(); out.write("pic上传成功!".getBytes()); //关闭服务; s.close(); } }
(8).TCP并发上传
//并发上传图片文件; //原理是服务器端每获取一个客户端套接字对象就会创建一个线程,该线程完成上传功能; //同时要对同名文件进行处理; import java.io.*; import java.net.*; //客户端 //在控制台通过向主函数传递参数,来声明要上传的文件 class Client { public static void main(String[] args) throws Exception { //对传入的参数作判断 if(args.length<1) { System.out.println("请指定一个图片文件!"); return; } File file = new File(args[0]); //对文件路径进行判断 if((!file.exists()&&file.isFile())) { System.out.println("指定的文件有问题,要么不存在,要么不是文件"); return; } //对文件格式进行判断 if(!file.getName().endsWith(".jpg")) { System.out.println("指定的文件格式有问题,必须是JPG格式类型"); return; } //对文件的大小进行判断 if(file.length()>1024*1024*5) { System.out.println("文件太大,文件大小不能超过5M"); return; } //上传动作; //客户端套接字,连接服务器 Socket s = new Socket("10.0.0.170",20006); //创建文件输入字节流,关联传递过来的文件 FileInputStream fis = new FileInputStream(file); //获取套接字输出流,上传文件 OutputStream out = s.getOutputStream(); byte[] buf = new byte[1024]; for(int len=0; (len=fis.read(buf))!=-1;) { out.write(buf,0,len); } //传递结束标记 s.shutdownOutput(); fis.close(); //获取服务器的反馈信息 InputStream in = s.getInputStream(); byte[] inbuf = new byte[1024]; int len = in.read(buf); System.out.println(new String(buf,0,len)); s.close(); } } //服务器端 class Server { public static void main(String[] args) throws Exception { //创建服务器套接字,监听接口 ServerSocket ss = new ServerSocket(20006); //服务器常开启为客户端提供服务 while(true) { //每当有客户端连接,就获取其套接字对象,并且创建处理请求的线程; //阻塞式方法 Socket s = ss.accept(); new Thread(new PicThread(s)).start(); } } } //服务端需要定义多线程来处理对应客户端的上传请求; //同时还要处理同名文件; class PicThread implements Runnable { private Socket s; PicThread(Socket s) { this.s = s; } //服务器端创建的线程指定内容; public void run() { //定义计数器来处理同名文件 int count = 1; //获取客户端的主机信息 String ip = s.getInetAddress().getHostAddress(); System.out.println(ip+" connected..."); try { //处理同名文件 File file = new File(ip+".jpg"); //直到没有同名 while(file.exists()) file = new File(ip+"("+(count++)+")"+".jpg"); //接收客户端的上传数据; InputStream in = s.getInputStream(); FileOutputStream fos = new FileOutputStream(file); byte[] buf = new byte[1024]; for(int len=0; (len=in.read(buf))!=-1;) { fos.write(buf,0,len); } fos.close(); //向客户端发送反馈信息 OutputStream out = s.getOutputStream(); out.write("图片上传成功".getBytes()); s.close(); } catch (Exception e) { throw new RuntimeException(ip+":上传失败"); } } }
(9).TCP并发登陆
//并发登录 //客户端通过键盘录入用户名,服务端对这个用户名进行校验; //如果该用户存在,在服务端显示xxx,已经登录;并在客户端显示xxx,欢迎光临; //如果用户不存在,在服务端显示xxx,尝试登录;并在客户端显示xxx,该用户不存在; //每个客户端最多登录三次; import java.io.*; import java.net.*; //客户端:从键盘输入用户名信息;传递给服务器;获取服务器返回信息; class LoginClient { public static void main(String[] args) throws Exception { //创建套接字对象,连接服务器 Socket s = new Socket("10.0.0.170",20007); //获取键盘录入 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); //获取套接字的输出流,向服务器发送数据 PrintWriter out = new PrintWriter(s.getOutputStream(),true); //获取套接字的输入流,用来获取服务器的返回信息 BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream())); //每个客户端有三次登录的机会 String line=null; for(int i=0; i<3; i++) { line = br.readLine(); if (line==null) { break;//如果键盘没有输入,则直接结束 } out.println(line); //获取服务器的返回信息; String returnLine = in.readLine(); System.out.println("Server:"+returnLine); //判断返回信息,确定是否登录成功; if(returnLine.contains("欢迎")) break; } br.close(); s.close(); } } //服务器端:从客户端获取用户名;校验;发送校验结果; class LoginServer { public static void main(String[] args) throws Exception { //创建套接字对象,监听端口 ServerSocket ss = new ServerSocket(20007); //服务器总是开启用来处理客户端的连接 while(true) { //对于每个客户端,获取其套接字,创建线程处理登录; Socket s = ss.accept(); new Thread(new LoginThread(s)).start(); } } } //服务器在处理客户端请求时要使用多线程技术 class LoginThread implements Runnable { private Socket s; LoginThread(Socket s) { this.s = s; } public void run() { String ip = s.getInetAddress().getHostAddress(); System.out.println(ip+" connected ..."); try { //比较客户端传递过来的用户名是否存在; for(int i=0; i<3; i++) { boolean flag = false; //用户信息存放在硬盘文件中,定义输入流读取文件信息; BufferedReader br = new BufferedReader(new FileReader("user.txt")); //获取客户端套接字的 BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream())); String line = in.readLine(); if(line == null) break; for(String user = null;(user = br.readLine())!=null;) { if(line.equals(user)) { flag = true; break; } } //将数据写入到指定文件中 PrintWriter out=new PrintWriter(s.getOutputStream(),true); //根据比较结果,返回相应的信息; if (flag) { System.out.println(line+",已登陆!"); out.println(line+",欢迎光临!"); break; } else { System.out.println(line+",尝试登陆!"); out.println(line+",用户名不存在!"); } br.close(); } s.close(); } catch (Exception e) { throw new RuntimeException("用户登录失败"); } } }
5.服务器端和客户端之间的访问形式
(1).自定义服务端,浏览器客户端
自定义服务端代码:运行服务端开启服务器;
import java.io.*; import java.net.*; //服务器 class ServerDemo { public static void main(String[] args)throws Exception { //创建服务,监听端口 ServerSocket ss=new ServerSocket(20008); //获取客户端 Socket s=ss.accept(); //显示ip String ip=s.getInetAddress().getHostAddress(); System.out.println(ip+" conneted..."); //读取客户端读取流数据 InputStream in=s.getInputStream(); byte[] buf=new byte[1024]; int len=in.read(buf); //显示数据 System.out.println(new String(buf,0,len)); //返回信息写入客户端输出流 PrintWriter out=new PrintWriter(s.getOutputStream(),true); out.println("<font color='red' size='7'>客户端你好!</font>"); //关闭服务 s.close(); ss.close(); } }
在客户端通过浏览器访问,在地址栏中输入:主机ip地址和端口号;运行结果:![]()
(2).Tomcat服务端,浏览器客户端
可以再本地开启Tomcat服务器,然后通过浏览器访问tomcat服务器;Tomcat服务器的默认端口是8080;
(3).Tomcat服务端,自定义浏览器客户端
自定义浏览器需要向服务器发送的消息头如下:![]()
自定义的浏览器只要想服务器发送如上的消息头,以及指定正确地端口就可以访问tomcat服务器;
(4).Tomcat服务端,自定义图形化界面浏览器客户端
自定义的浏览器:package com.zr.day23; import java.net.*; import java.awt.*; import java.awt.event.*; import java.io.*; class FrameDemo { private Frame f; private TextField tf; private Button b; private TextArea ta; FrameDemo() { init(); } public void init() { f = new Frame("我的浏览器窗口"); f.setBounds(300,200,500,500); f.setLayout(new FlowLayout()); b = new Button("转到"); tf = new TextField(50); ta = new TextArea(25,60); f.add(tf); f.add(b); f.add(ta); myEvent(); f.setVisible(true); } public void show() { ta.setText(""); String urltext = tf.getText(); try { //封装地址对象 URL url =new URL(urltext); //建立连接 URLConnection conn = url.openConnection(); //读取流,用于读取服务器返回数据 InputStream in = conn.getInputStream(); byte[] buf=new byte[1024*1024]; int len=in.read(buf); //将数据显示在文本区中 ta.append(new String(buf,0,len)); } catch(Exception e) { throw new RuntimeException("浏览器访问失败"); } } public void myEvent() { f.addWindowListener(new WindowAdapter(){ @Override public void windowClosing(WindowEvent e) { System.exit(0); } }); tf.addKeyListener(new KeyAdapter(){ @Override public void keyPressed(KeyEvent e) { if(e.getKeyCode()==KeyEvent.VK_ENTER) { show(); } } }); b.addActionListener(new ActionListener(){ @Override public void actionPerformed(ActionEvent e) { show(); } }); } } class MyIE { public static void main(String[] args) { new FrameDemo(); } }
6.其他对象
(1).java.net.URL
URL:统一资源定位符;
构造方法:
URL(String spec):以字符串形式创建URL对象
URL(String protocol, String host, int port, String file):以详细的形式创建URL对象
常用方法:
String getFile():文件名
String getHost():主机名
String getPath():路径
int getPort():端口,一般的输入的网址中没有端口号,此时获取的端口是-1,在这里可以进行判断处理,为端口设置一个有效的默认值;
String getProtocol():协议
String getQuery():查询部分
URLConnetion openConnetion():
InputStream openStrean():打开到此URL的连接,返回一个用于从该连接读入的输入流;
(2).java.net,URLConnection:直接子类:HttpURLConnection
表示到URL所引用的远程对象的连接;
构造方法:
URLConnection(URL url):构造一个到指定URL的URL连接;
常用方法:
URL getURL():获取URL对象;
InputStream getInputStream()
OutputStream getOutputStream()
(3).java.net.SocketAddress:直接子类:InnetSocketAddress
抽象的SocketAddress,和InnetAddress的区别
SocketAddress:封装的是套接字地址,包括IP地址和端口号,也可以是主机名和端口号;
InnetAddress:封装的是IP地址;
(4).域名解析
在进行网络访问时,我们通常输入的是比较容易记忆的域名而不是IP地址,这时提交访问请求,系统会先在本地(c:\windows\system32\drivers\ext\host)查找域名和地址的映射关系;
在本地如果有要访问的域名的映射,直接获取IP地址访问;
在本地没有映射时,会访问公网的DNS服务器,服务器查询映射,向主机返回IP地址,然后通过IP地址访问指定的服务器