黑马程序员-Java基础-网络编程
Java 为网络编成提供了java.net 包。Java支持TCP/UDP 及其上层的网络编程。、
一、网络程序设计基础
1、TCP和UDP
TCP(Transmission Control Protocol,传输控制协议)是面向连接的传输层协议。即应用进程在使用TCP协议之前,必须建立TCP连接,在传输完毕后,释放已经建立的连接。利用TCP协议进行
通信的两个应用进程:
1、是服务器进程。
2、客户端进程。
UDP(User Datagram Protocol,用户数据报协议)是面向无连接的传输层协议,即应用进程在使用UDP协议之前,不必先建立连接。
2、端口和套接字
端口也称为协议端口。端口用一个整数型标识符来表示,即端口号。端口号跟协议相关,TCP/IP传输层的两个协议TCP和UDP是完全独立的两个软件模块,因此各自的端口号保留给预定义的服务,
他的范围是0~655535。
端口好拼接到IP 地址即构成套接字(Socket)。 在客户/服务器通信模式中,客户端需要主动创建与服务器连接的Socket,服务器进程接收到了客户端的连接请求时,也会创建与客户连接的Socket。Socket可看做是通信连接两端的收发器,服务器进程与客户进程都通过Socket来收发数据。在一个Socket 对象中同时包含了远程服务器的IP地址和端口信息,以及本地客户的IP 地址和端口信息。从Sokcet对象中还可以获得输入输出流,分别用于接收从服务器端发来的数据,以及向服务器发送数据。
二、UDP网络编程
在Java中,java.net.DatagramSocket 负责接收和发送UDP 数据报 ,而java.net.DatagramPacket 表示UDP数据报。
每个DatagramSocket 与一个数据报套接字绑定,每个DatagramSocket 可以把UDP数据报发送给任意一个远程DatagramSocket,也可以接收来自任意一个远程DatagramSocket 的数据报,
在UDP 数据报中包含了目的信息,DatagramSocket 可以根据该信息把数据发送到目的地。
DatagramSocket 类常用的方法:
> void send(DatagramPacket p ) throws IOException :发送一个UDP 数据报。
> void receive(DatagramPacket p ) throws IOException : 接收一个UDP 数据报。
> void connect(InetAddress addr, int post) :将该UDPSocket 变成一个连接型的UDPSocket 。
> void disconnect( ) :将该UDPSocket 变成一个非连接型的UDPSocket 。
> void close() :关闭该UDPSocket 。
DatagramPacket 类的对象代表了一个UDP 数据报包,通过UDP 发送数据时,
1、根据发送的数据生成一个DatagramPacket 对象。
2、通过DatagramSocket 对象的send() 方法发送这个对象。
接收数据包时:
1、根据要接收数据的缓冲区生成一个DatagramPacket 对象。
2、通过DatagramSocket 对象的receive() 方法接收这个对象的内容。
所以,DatagramPacket 类的构造方法分为两类:
1、创建DatagramPacket 对象用来发送数据报,如:
DatagramPacket(byte[] buf, int length , InetAddress addr , int post) ,
2、用于接收数据包构造方法,如:
DatagramPacket(byte[] buf, int length) ;
DatagramPacket 类的常用方法:
> byte[] getData() : 返回DatagramPacket对象中包含的数据。
> int getLength() : 返回发送/接收数据的长度。
练习:通过UDP传输方式,客户程序向接收程序发送小写字符串,服务器程序接收到后,并将其转换成大写返回给客户程序。
思路:
服务程序 :
1、建立UDPSocket服务,并监听一个端口。
2、创建接收数据报对象。
3、接收到数据报,并将数据类容转换成大写。
4、将转换完的内容发送给客户程序。
5、关闭资源。
1 public class UDPServer { 2 public static void main(String[] args) throws Exception { 3 // 定义服务端Socket , 端口号为 10001 4 DatagramSocket ds = new DatagramSocket(10001) ; 5 while(true) { 6 // 创建接收数据包 7 byte[] buf = new byte[1024] ; 8 DatagramPacket r_dp = new DatagramPacket(buf,buf.length) ; 9 ds.receive(r_dp) ; 10 // 取出发送端的IP地址和端口, 11 InetAddress addr = r_dp.getAddress() ; 12 int post = r_dp.getPort() ; 13 // 客户端发送的数据: 14 String str = new String(buf,0,r_dp.getLength()) ; 15 System.out.println("IP:"+addr+",post:"+post 16 +":"+ str); 17 18 // 将转换的完的内容发送个客户程序 19 buf = str.toUpperCase().getBytes(); 20 DatagramPacket s_dp = new DatagramPacket(buf 21 , buf.length, addr , post) ; 22 ds.send(s_dp) ; 23 } 24 } 25 }
客户程序:
1、建立UDPSocket服务,并监听一个端口。
2、提供数据。并将数据封装到数据包。
3、通过Socket服务的发送功能,将数据包发出去。
4、接收服务端发送的数据。
5、关闭资源。
1 public class UDPClient { 2 public static void main(String[] args) throws Exception { 3 DatagramSocket ds = new DatagramSocket() ; 4 5 BufferedReader bufr = 6 new BufferedReader(new InputStreamReader(System.in)) ; 7 8 String line = null ; 9 InetAddress address = InetAddress.getByName("127.0.0.1") ; 10 byte[] buf = new byte[1024] ; 11 while ((line = bufr.readLine()) != null) { 12 if ("exit".equals(line)) 13 break ; 14 15 buf = line.getBytes() ; 16 DatagramPacket s_dp = 17 new DatagramPacket(buf, buf.length, address ,10001) ; 18 ds.send(s_dp) ; 19 20 // 接收数据包 21 DatagramPacket r_dp = new DatagramPacket(buf, buf.length) ; 22 ds.receive(r_dp) ; 23 System.out.println(new String(buf,0,r_dp.getLength())); 24 } 25 ds.close() ; 26 } 27 }
总结:对于网络编程知识,首先要有清晰的功能流程思路,这样编写起来会非常上手。
三、TCP网络编程
Java 的基于套接字编程分为服务端编程和客户端编程:
服务器编程:
1、调用ServerSocket(int port) 创建一个服务器套接字,并绑定到指定端口上。
2、调用accept(),监听连接请求,如果客户端请求连接,并接收连接,返回客户端套接字Scoket。
3、调用Socket 类的getOutputStream 和getInputStream 获取网络输出和输入流,开始网络数据的发送和接收。
4、关闭通信套接字。
客户端编程:
1、调用Socket()创建一个流套接字,并请求连接到服务器。
2、调用Socket 类的getOutputStream 和getInputStream 获取网络输出和输入流,开始网络数据的发送和接收。
3、关闭通信套接字。
练习:模拟一个文件上传的C/S 程序。即可以多个用户同时向客户端上传mp3格式文件。
分析:在多个客户端同时连接服务器,并对服务器进行上传文件操作时,则需要使用多线程,否则会出现局限性:当一个客户端连接服务器时,其他客户端则不能连接上。
问:如何定义线程?
明确每个客户端要在服务端执行的代码,将该代码存入run方法中。如:
服务器:
1、创建服务器Socket 。
2、接收客户端连接。
3、接收客户端上传的文件。
4、接收完成,返回信息。
5、关闭资源。
上面5个步骤中,3、4则是客户端要在服务端执行的代码,我们则将该代码存入run()方法中。
服务端代码:
1 import java.io.*; 2 import java.net.*; 3 public class TCPServer{ 4 public static void main(String[] args) throws Exception { 5 // 创建服务器socket 6 ServerSocket server = new ServerSocket(10000) ; 7 while(true) { 8 // 接收客户端连接 9 Socket client = server.accept() ; 10 // 有客户端连接,便创建线程 11 new Thread(new TCPFileToServer(client)).start() ; 12 } 13 } 14 } 15 class TCPFileToServer implements Runnable{ 16 private Socket client ; 17 TCPFileToServer(Socket client){ 18 this.client = client ; 19 } 20 public void run(){ 21 int count = 1 ; 22 try{ 23 // 定义存储文件的名字。 24 String ip = client.getInetAddress().getHostAddress() ; 25 File file = new File("f:\\"+ip+"("+(count++)+")"+".mp3") ; 26 27 while(file.exists()) 28 file = new File("f:\\"+ip+"("+(count++)+")"+".mp3") ; 29 // 收取文件 30 // 文件输出流: 31 BufferedWriter bufw = 32 new BufferedWriter( 33 new OutputStreamWriter( 34 new FileOutputStream(file))) ; 35 // 网络输入流: 36 BufferedReader bufr = 37 new BufferedReader( 38 new InputStreamReader(client.getInputStream())); 39 String line = null ; 40 while ((line = bufr.readLine())!= null) { 41 bufw.write(line) ; 42 } 43 // 网络输出流: 44 PrintWriter out = new PrintWriter (client.getOutputStream(),true) ; 45 out.println("上传成功!") ; 46 // 关闭资源。 47 bufw.close(); 48 client.close() ; 49 } 50 catch(Exception e){ 51 new RuntimeException("上传失败。") ; 52 } 53 } 54 }
客户端代码如:
1 import java.net.*; 2 import java.io.*; 3 public class TCPFileClient { 4 public static void main(String[] args) throws Exception{ 5 // 创建客户端Socket 6 Socket client = new Socket(InetAddress.getByName("127.0.0.1"),10000) ; 7 if (args.length != 1) 8 return ; 9 // 输出要上传文件的地址 10 File file = new File(args[0]) ; 11 12 // 网络输出流 13 PrintWriter out = 14 new PrintWriter( 15 client.getOutputStream(),true); 16 // 文件读取流 17 BufferedReader bufr = 18 new BufferedReader( 19 new InputStreamReader( 20 new FileInputStream(file))) ; 21 String line = null ; 22 while ((line = bufr.readLine())!=null) { 23 out.println(line) ; 24 } 25 client.shutdownOutput() ; 26 // 网络输入流 27 byte[] buf = new byte[1024] ; 28 InputStream in = client.getInputStream(); 29 int len = in.read(buf) ; 30 System.out.println(new String(buf,0,len)); 31 // 关闭资源 32 bufr.close() ; 33 client.close() ; 34 } 35 }
练习2:客户端并发登录:客户端通过键盘录入登录用户名。服务端对这个用户名进行校验。
如果该用户存在,在服务端显示xxx,已经登录。
并在客户端显示,xxx,欢迎登录。
如果该用户不存在,在服务端显示xxx,尝试登录。
并在客户端显示,xxx,该用户不存在。(在用户错误的情况下不能连续登录3次以上)
思路:
客户端:
1、创建Socket 服务,连接服务器。
2、键入用户名,并发送给服务器。
3、收到返回的信息。
4、关闭资源。
1 import java.io.*; 2 import java.net.*; 3 public class LoginClient { 4 public static void main(String[] args) throws Exception { 5 // 创建客户端Socket 6 Socket client = new Socket("127.0.0.1", 10000) ; 7 8 // 输入用户名端。 9 BufferedReader bufr = 10 new BufferedReader(new InputStreamReader(System.in)) ; 11 // 网络输出流 12 PrintWriter pw = 13 new PrintWriter(client.getOutputStream(),true) ; 14 // 网络输入流 15 BufferedReader br = 16 new BufferedReader(new InputStreamReader(client.getInputStream())) ; 17 // 键入用户名 18 for (int i = 0 ; i < 3 ; i ++) { 19 String line = bufr.readLine() ; 20 if(line == null) 21 break ; 22 pw.println(line) ; 23 // 接收信息 24 String info = br.readLine() ; 25 System.out.println(line+","+info); 26 if(info.contains("欢迎")) 27 break ; 28 } 29 } 30 }
服务器:
思路:
1、建立服务器Socket.
2、接收客户端连接。
3、创建客户端线程。
3.1、判断用户名是否合法。
3.2、判断用户是否存在。
4、关闭资源。
1 import java.net.*; 2 import java.io.*; 3 public class LoginServer { 4 public static void main(String[] args) throws Exception { 5 ServerSocket server = new ServerSocket(10000) ; 6 while (true) { 7 Socket client = server.accept() ; 8 new Thread(new ClientThread(client)).start() ; 9 } 10 } 11 } 12 13 class ClientThread implements Runnable { 14 private Socket client ; 15 public ClientThread(Socket client) { 16 this.client = client ; 17 } 18 public void run() { 19 try { 20 for (int i = 0 ; i < 3 ; i ++) { 21 // 文件输入流:读取用户信息 22 BufferedReader bufr = 23 new BufferedReader(new FileReader("F:\\user.txt")) ; 24 // 网络输入流 25 BufferedReader br = 26 new BufferedReader(new InputStreamReader(client.getInputStream())) ; 27 // 网络输出流 28 PrintWriter pw = 29 new PrintWriter(client.getOutputStream(),true) ; 30 String name = br.readLine() ; 31 if (name == null) 32 break ; 33 String line = null ; 34 boolean flag = false ; 35 while ((line = bufr.readLine()) != null) { 36 if ( line.equals(name) ) { 37 flag = true ; 38 break ; 39 } 40 } 41 if (flag) { 42 System.out.println(name + ",已经登录"); 43 pw.println("欢迎登录.") ; 44 } 45 else { 46 System.out.println(name + ",尝试登录。"); 47 pw.println("用户不存在.") ; 48 } 49 } 50 client.close() ; 51 } 52 catch(Exception e) { 53 54 } 55 } 56 }
四、URL
URL(Uniform Resurse Locator ,统一资源定位器),用来表示从英特网上得到资源位置和访问位置这些资源的方法。协议名://资源名。
如:http://www.cnblogs.com/jbelial/
其中:协议名是http 协议,主机名是www.cnblogs.com,端口默认80,文件路径名是/jbelial/。
1、URL类
URL 类代表了一个同一资源定位符,它是指向互联网资源的指针。
URL 类的构造方法如下:
> URL(String spec ) :由一个表示URL 地址的字符串构造一个URL对象。
> URL(String protocol , String host , int port , String file) :根据指定protocol、host、port号和file创建URL 对象。
常用方法:
> InputStream openStream() :打开一个连接到该URL 的InputStream 的对象,通过对象,可以URL 中读取Web 页面内容。
> URLConnection openConnection() :创建并返回一个 URLConnection 对象,它表示到URL所引用的远程对象的连接。
> Object getContent() : 获取此URL 的内容。
> String getFile() :获取次URL的文件名。
> String getHost(): 获取次URL的主机名
> String getPath(): 获取次URL的路径部分。
> String getProtocol(): 获取次URL的协议名称
> String getQuery(): 获取次URL 的查询。
1 public class TomcatDemo { 2 public static void main(String[] args) throws Exception { 3 if(args.length != 1) 4 return ; 5 // 创建一个URL 对象 6 URL url = new URL(args[0]) ; 7 // 缓冲流 8 BufferedReader bufr = new BufferedReader(new InputStreamReader(url.openStream())) ; 9 String line = null ; 10 while ((line = bufr.readLine()) != null) { 11 System.out.println(line); 12 } 13 bufr.close() ; 14 } 15 }
运行时输入:http://jbelial.cnblogs.com 则可以获取到该URL的内容。
2、URLConnection 类
抽象类 URLConnection
是所有类的超类,它代表应用程序和 URL 之间的通信链接。此类的实例可用于读取和写入此 URL 引用的资源。