黑马程序员-------------(九)网络编程

目录

一 网络通讯

    1.网络通讯三要素

    2.网络通讯的步骤

二 网络模型

    1.网络模型知识

    2.OSI参考模型

    3.TCP/IP参考模型

    4.TCP和UDP协议

    5.Java中涉及的网络编程和网络传输模型

三 网络通讯三要素在Java中的体现
    
    1.IP地址在Java中的体现

    2.端口Port在Java中的体现

    3.传输协议在Java中的体现

四 网络编程

    1.Socket概述

    2.UDP协议的Socket

    3.TCP协议的Socket

    4.网络架构

五 URL
    1.URL

    2.URLConnection

六 正则表达式

    1.概述

    2.具体操作功能

    3.网页爬虫(蜘蛛)

注:本章重点内容:TCP/UDP,Socket,正则表达式,根据知识的重要程度用◆◆◆、◆◆◆◆、◆◆◆◆◆进行了标注。

####################################################################################
一、网络通讯
####################################################################################

1.网络通讯三要素
IP地址、端口号、传输协议

1.1 IP地址 Internet Protocol
(1)定义:网络中设备的标识
(2)主机名:单纯的IP地址不易记忆,可用主机名替代。
(3)特殊IP:如果本机上没有配置任何IP地址的情况下,本机默认的IP地址为127.0.0.1
本地回环地址:127.0.0.1  主机名:localhost
用处:检验本机网卡是否运行正常 cmd中输入ping 127.0.0.1 或ping localhost

1.2 端口号 port
(1)定义:用于标识进程的数字称为进程的逻辑地址,简称端口号。它是不同进程的标识。有效端口:0~65535,其中0~1024系统使用或保留端口。
(2)常用的端口号
1)Web服务器端口:80 (位于0~1024之间)
2)Tomcat服务器端口:8080
3)MySQL数据库端口:3306
(3)物理端口和逻辑端口:
网卡口是物理上的端口。可以接收到多种类型网络数据并把接收到的数据发送给本机中不同类型的网络应用程序;进程端口是逻辑端口,是进程的逻辑地址,用于标识进程。网络应用程序必须有数字标识 (也就是端口)。这样外面的应用程序才能将数据发送到该标识对应的网络应用程序上。

注意:
a.一台主机上的任何类型的进程(未必是网络进程)都具有端口号;
b.任务管理器中的PID(ProcessID),即进程ID,就是端口号;
c.一个网络应用程序可以对应一个或者多个端口号;
d.如果将IP比作一座大厦,端口就是大厦中的一间屋子。

1.3 传输协议 Transmission Protocol 简称TP
(1)定义:传输协议是通信的规则
(2)常见的传输协议:TCP、UDP、HTTP、FTP
(3)TCP/IP协议简介
TCP/IP协议是国际组织通用的传输协议。几乎所有类型的操作系统(OS)都安装了这个协议。TCP/IP协议既可以用于LAN,又可以用于WAN。

你发现了吗?
通讯协议可以类比成两个人之间是用汉语交流还是用英语交流,也就是两个人交流的语言。


2.网络通讯的步骤
根据网络通讯的三要素确定网络通信步骤
找IP--->定Port--->确定传输协议
(1)找IP:一台主机找到另一台主机的IP地址
(2)定端口:确定数据发送到另一台主机指定的应用程序上
(3)定义一个通讯规则:这个通讯规则就称为协议,国际组织定义了通用协议TCP/IP。

####################################################################################
二 网络模型
####################################################################################

1.网络模型知识
(1)网络传输模型诞生的背景
网络传输需要不同的组件协同工作。不同的组件工作的时间不同。为了区分哪些组件在什么时候该做什么事,就把这些组件按照层次的形式进行了
细致的划分,这样的划分就诞生了网络传输模型。
(2)网络传输模型的每个层次都有特定的任务要完成


2.OSI参考模型
Open System Interconnect:开放式系统互联
应用层、表示层、会话层、传输层、网络层、数据链路层和物理层

2.1 从主机1向外发送数据
应用层->表示层->会话层->传输层->网络层->数据链路层->物理层。向外发送数据的过程就是将原始数据根据每层具有的特点和协议对数据进行层层封装。

(1)传输层
1)传输层的协议是TCP和UDP
2)当向下进行到传输层的时候,要根据传输层的特点和协议对从会话层传来的数据进行封装
3)数据在传输层被加上TCP/UDP的层次信息
(2)网络层
1)网络层的协议是IP地址协议
2)数据从传输层进行到网络层的时候,数据被加上一个目标IP地址,用于告知数据运输的终点。
(3)物理层
1)物理层的典型设备是网线、光纤、红外或者蓝牙
2)数据传输到物理层的时候,已经被封装成数据包。此时物理层将数据传输出去
3)数据包通过连接在两台主机之间的物理层介质,从一台主机传向另一台主机

2.2 主机2接收数据
物理层->数据链路层->网络层->传输层->会话层->表示层->应用层。从外接收数据的过程将数据包根据每层具有的特点和协议对数据包进行层层拆封。数据在应用层的拆封以及数据的最终去向
(1)应用层将数据包拆开,获得原始数据和其他的相关信息 (包含该数据应该发送到本台主机的那一个具体端口)
(2)根据端口号将原始数据发送到本机指定的端口对应的应用程序上。

2.3 数据的封包和数据的拆包
(1)从应用层到物理层对数据的向下传输过程中,是对数据的封包
(2)从物理层到应用层对数据的向上传输过程中,是对数据的拆包

3.TCP/IP参考模型
应用层、传输层、网际层、主机至网络层

3.1 TCP/IP参考模型和OSI参考模型的关系
TCP/IP是对OSI模型的简化,进行了两次合并。第一次合并是将OSI的应用层、表示层和会话层合并成TCP/IP的应用层;第二次合并是将OSI的数据链路层和物理层合并成TCP/IP的主机至网络层。两次合并之后,凸显了OSI的传输层和网络层,其余的层全部进行了整合。这样分别就凸显了传输层和网络层的协议:TCP/UDP协议和IP地址协议。这样OSI简化后四层模型就称为TCP/IP参考模型。

3.2 TCP/IP主要的协议

(1)应用层主要有HTTP地址协议和FTP协议
HTTP协议  :超文本传输协议 Hyper Text Transmission Protocol              
FTP协议   :文件传输协议 File Transmission Protocol             
(2)传输层主要有UDP协议和TCP协议
UDP协议   :用户数据报协议 User Datagram Protocol          
TCP协议   :传输控制协议 Transmission Controllable Protocol       
(3)网际层主要有IP地址协议
IP地址协议:互联网协议 Internet Protocol         

4.TCP和UDP协议
4.1 TCP和UDP协议的区别
(1)是否面相连接
TCP: 必须面向连接。只有建立连接,才能形成数据传输通道。TCP通讯必须确定通信的双方都存在才能进行数据传输。
UDP: 面向无连接。将数据及源和目的封装在数据包中,只要发送方存在数据就能被传输出去,接收方是否存在并不影响通信过程。
(2)传输的数据量的大小
TCP: 建立连接,形成传输数据的通道。通过TCP协议可以在通道中进行大数据量的传输。Java代码中体现就是通过IO读/写流或者输入/输出流来进行持续的数据传送。
UDP: 数据被封装成数据报包,每次发送的数据报包大小都限制在64KB以内。Java代码中体现就是通过发送端的send方法和接收端的receive方法进行数据传输。
(3)可靠性
TCP: 通过"三次握手"机制来判断通信的双方是否建立连接,是可靠协议。由于TCP协议面向连接,所以在进行传输数据之前,必须要求通信双方建立连接,因此传输可靠。
UDP: 由于面向无连接,是不可靠协议。发送方不管接收方是否存在都会将数据打成数据报包进行数据发送。如果接收方存在,数据就接收到了,否则数据丢失。
(4)速度
TCP: 速度相对慢。发送数据前,TCP协议一定要确保通信双方全部存在并且建立连接,所以传输速度相对慢。这正是TCP协议面向连接和可靠性的特性所体现的。
UDP: 速度快。发送数据之前,通信双方不用建立连接,所以传输速度快。这正好是UDP协议面向无连接和可靠性差的特点所体现的。
(5)举例
TCP: 如打电话,下载。
UDP: 如对讲机,去邮局寄东西。网络聊天,桌面共享,视频会议。


4.2  TCP和UDP协议在网络编程中的区别:
(1)TCP和UDP创建的Socket通信端点的类型
UDP协议是面相无连接的协议。两个Socket通信端点是用同一个DatagramSocket类进行创建的。因此UDP通信不区分通信双方的主次。
TCP协议是面相连接的协议。两个Socket通信端点并不是用同一个Socket类创建的。通信的一方程序称为服务器Socket端点,使用ServerSocket类
进行创建。通信的另一方程序称为客户端Socket端点,使用Socket类进行创建。
(2)TCP和UDP传输数据的类型
UDP协议数据的传输形式是数据报包。数据byte[]被DatagramPacket类封装成数据报包在通信端点之间进行传输。
TCP协议数据的传输形式是IO网络流。没有特殊的类对数据进行封装,直接使用IO网络流对通行端点的数据进行传输。
(3)通过UDP协议创建的Socket端点称为数据报Socket、Datagram Socket;通过TCP协议创建的Socket端点称为网络流Socket、Stream Socket

4.3 TCP协议如何确定通信的另一方存在?
TCP协议的三次握手机制。先确定对方在不在,在的话才发送。单方面断开,数据不再传递。
发送方:在吗?
接收方:在。
发送方:哦,知道你在了。
TCP通讯双方通过三次握手之后,就完成了TCP传输通道的建立。

5.Java中涉及的网络编程和网络传输模型
(1)主机至网络层主要是和硬件打交道,不属于软件开发的范围;
(2)Java中的网络编程是在TCP/IP中的传输层和网际层进行开发;
(3)Java Web进行的程序开发是在TCP/IP中的应用层进行开发,应用层是基于传输层和网络层,使用起来更加便捷。

####################################################################################
三 网络通迅三要素在Java中的体现
####################################################################################

1.IP地址在Java中的体现
InetAddress类表示的是对网络通讯的IP地址的封装。
String getHostName() 获取此 IP 地址的主机名。
String getHostAddress() 返回 IP 地址字符串(以文本表现形式)。
static InetAddress getLocalHost() 返回本地主机。
static InetAddress getByAddress(byte[] addr) 在给定原始 IP 地址的情况下,返回 InetAddress 对象。
static InetAddress getByAddress(String host, byte[] addr) 根据提供的主机名和 IP 地址创建 InetAddress。
static InetAddress getByName(String host) 在给定主机名的情况下确定主机的 IP 地址。
static InetAddress[] getAllByName(String host) 在给定主机名的情况下,根据系统上配置的名称服务返回其 IP 地址所组成的数组。

2.端口Port在Java中的体现
由于端口仅仅是数字标识,所以没有必要将端口封装成对象。

3.传输协议在Java中的体现
Java将传输层常见的协议TCP和UDP分别封装成了TCP相关类和UDP相关类。

3.1 UDP协议相关类
与UDP传输协议本身相关的类就是DatagramSocket类。与UDP每次传输数据形式相关的类就是DatagramPacket类。

3.1.1 DatagramPacket
此类是对UDP数据传输过程中的数据报包的封装。被final修饰,不可以被继承.构造方法中涉及到的IP地址和端口号指的是数据报包被发送的目的
地所在主机的IP地址和端口号。DatagramPacket是一个JavaBean类,它的方法主要分成了对私有字段的Set和Get方法

构造方法摘要
用于UDP数据传输中的用于接收数据的数据报包对象
DatagramPacket(byte[] buf,int length);
DatagramPacket(byte[] buf,int offset,int length);
用于UDP数据传输中的用于发送数据的数据报包对象
DatagramPacket(byte[] buf,int length,InetAddress address,int port);
DatagramPacket(byte[] buf,int offset,int length,InetAddress address,int port);

你发现了吗?
构造方法中凡是带有IP和PORT的DatagramPacket的数据报包都是用来发送的数据报包,否则都是用于接收的数据报包。


3.1.2 DatagramSocket
对UDP协议传输数据需要的Socket通信端点进行了封装。此类表示的通信端点Socket用于发送和接收UDP数据传输过程中的数据报包.DatagramSocket既可以用于发送数据,又可以用于接收数据

(1)DatagramSocket和DatagramPacket构造方法中IP和Port参数的关系
DatagramPacket类在构造方法中涉及到的IP地址和端口号指的是Socket通信端点将数据报包发送到的目的地所在主机的IP地址和端口号;
DatagramSocket类在构造方法中涉及到的IP地址和端口号指的是Socket通信端点本身所在主机的IP地址和端口号;
DatagramSocket构造中指定的是数据的源,DatagramPacket构造中指定的是数据的目的地;
DatagramSocket仅仅负责数据的发送和接收,而对数据的封装工作全部由DatagramPacket类进行了封装。

(2)构造方法摘要
DatagramSocket() 构造数据报套接字并将其绑定到本地主机上任何可用的端口。
DatagramSocket(int port) 创建数据报套接字并将其绑定到本地主机上的指定端口。
DatagramSocket(int port, InetAddress laddr) 创建数据报套接字,将其绑定到指定的本地地址。

(3)方法摘要
void close() 关闭此数据报套接字。
void send(DatagramPacket p) 从此套接字发送数据报包。
void receive(DatagramPacket p) 从此套接字接收数据报包。
注意:
receive方法是一个阻塞式方法。程序中如果调用了这个方法,并且对应的接收端的Socket端点没有接收到数据报包,就会一直等待不动。直到这个接收端的DatagramSocket接收到了数据报包,receive方法才会被执行完。这样整个程序就向下继续执行。

3.3 TCP协议相关类

3.3.1 Socket
两台主机间通信的端点(称客户端Socket端点或者Socket端点),实现了客户端的套接字。
(1)构造方法摘要
Socket() 通过系统默认类型的 SocketImpl 创建未连接套接字
Socket(String host, int port)  创建一个流套接字并将其连接到指定主机上的指定端口号。
Socket(InetAddress address, int port) 创建一个流套接字并将其连接到指定 IP 地址的指定端口号。

(2)Socket类负责通信的方法
InputStream getInputStream() 获取网络输入流,用于通信双方的“接收”。作用:可以通过这个输入流从Socket读取字节数据byte[]
OutputStream getOutputStream()获取网络输出流,用于通信双方的“发送”。作用:可以通过这个输出流向Socket写入字节数据byte[]
void shutdownInput() 关闭客户端的Socket输入流
void shutdownOutput() 关闭客户端的Socket输出流

3.3.2 ServerSocket
这个类实现了服务器端的Socket通信端点。这个服务器端的Socket端点等待来自网络中的其他客户端Socket端点的请求。
(1)构造方法摘要
ServerSocket() 创建非绑定服务器套接字。
ServerSocket(int port) 创建绑定到特定端口的服务器套接字。

(2)Socket类负责通信的方法
Socket accept() 监听要连接到这个ServerSocket服务端的连接并接受这个连接。连接完成之后返回连接客户端的Socket副本。
InetAddress getInetAddress()获取ServerSocket所在本机的IP地址
int getLocalPort()获取ServerSocket所监听的本机的进行网络通讯的App的端口号
注意:
accept()方法是一个阻塞式方法,这个方法会一直阻塞直到有来自客户端的Socket和这个ServerSocket之间的连接被创建。

####################################################################################
四 网络编程
####################################################################################
1.Socket概述

Socket插座、插槽、套接字。是为网络服务提供的一种机制,网络编程其实就是Socket编程。每个应用程序都有一个类似网卡的插槽,两台机器中的软件通信,每个软件都相当于Socket。Socket通信端点具备了,但是数据传输协议是不一样的。因此每一种数据传输协议都有自己特有创建Socket通信端点的方法。

Socket套接字和网卡口的类比:
两台主机想通讯,必须至少通过一根网线进行相连。要求每台主机必须有一个网卡口用来连接网线。这个用来插网线的网卡口就相当于一个插槽。Socket相当于应用程序的“网卡口”,想要通讯的两台主机上的应用程序也需要有一个类似于主机上的网卡口/插槽。在通信软件上,通信软件需要有Socket作为通信的端点。数据传输之后,传输到物理层之后,通过物理层的插在网卡口的网线将数据从一台主机传送到另一台主机的物理层。硬件上有网卡口来做网线中数据的连接点;程序方面有Socket来做数据的连接点。

2.UDP协议的Socket
基本思路:
(1)建立发送端,接收端。
(2)建立数据包。
(3)调用Socket的发送接收方法。
(4)关闭Socket。
注意:
发送端与接收端是两个独立的运行程序。

2.1 发送端
(1)UDP发送端的操作流程
1)建立UDPSocket服务,即建立Socket发送端点。
2)提供数据并将数据封装到DatagramPacket中。
3)通过UDP的Socket的发送功能将数据发送出去。
4)关闭Socket。因为网络通讯的最底层要走物理层,也就意味着底层的网卡资源被调用。所以关闭Socket就是在使用完底层资源之后对底层资源进行释放。
注意:
在发送端,要在数据包对象中明确目的地IP及端口

(2)代码体现
DatagramSocket ds = new DatagramSocket();
byte[] by = 'hello,udp'.getBytes();
DatagramPacket dp = new DatagramPacket(by,0,by.length,InetAddress.getByName("127.0.0.1"),10000);
ds.send(dp);
ds.close();

2.2 接收端
(1)UDP接收端的操作流程
1)建立UDPSocket服务,即建立Socket接收端点。
2)定义一个数据报包。因为要存储接收到通过UDP传输来的字节数据,并且接收到的数据有很多信息。如果自行提取会非常麻烦。因此直接封装成
数据报包对象,调用该对象不同的方法从而获取接收到的数据报包中的信息(直接利用DataPacket类的JavaBean的Get方法获取各种信息)。
3)通过UDP接收端的Socket的receive方法将接收到的数据取出,并存入已定义好的数据报包对象中。
4)关闭Socket资源。
注意:
在接收端,要指定监听的端口。

(2)代码体现
DatagramSocket ds = new DatagramSocket(10000);
byte[] by = new byte[1024];
DatagramPacket dp = new DatagramPacket(by,by.length);
ds.receive(dp);
String str = new String(dp.getData(),0,dp.getLength());
System.out.println(str+"--"+dp.getAddress());
ds.close();


练习:编写一个UDP聊天程序。有收数据的部分,和发数据的部分。这两部分需要同时执行。

 1 import java.io.*;
 2 import java.net.*;
 3 
 4 //发送端
 5 class Send implements Runnable
 6 {
 7     private DatagramSocket ds;
 8     public Send(DatagramSocket ds)
 9     {
10         this.ds = ds;
11     }
12     public void run()
13     {
14         try
15         {
16 
17     //读取键盘录入
18             BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
19             String line = null;
20             while((line=bufr.readLine())!=null)
21             {
22 
23       //把数据封装进 DatagramPacket,发送出去
24                 byte[] buf = line.getBytes();
25                 DatagramPacket dp =
26                     new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.1.255"),10002);
27                 ds.send(dp);
28                 if("886".equals(line))
29                     break;
30             }
31         }
32         catch (Exception e)
33         {
34             throw new RuntimeException("发送端失败");
35         }
36     }
37 }
38 
39 //接收端
40 class Rece implements Runnable
41 {
42     private DatagramSocket ds;
43     public Rece(DatagramSocket ds)
44     {
45         this.ds = ds;
46     }
47     public void run()
48     {
49         try
50         {
51             while(true)
52             {
53 
54                //接受数据
55                 byte[] buf = new byte[1024];
56                 DatagramPacket dp = new DatagramPacket(buf,buf.length);
57                 ds.receive(dp);
58                 String ip = dp.getAddress().getHostAddress();
59                 String data = new String(dp.getData(),0,dp.getLength());
60                 if("886".equals(data))
61                 {
62                     System.out.println(ip+"....离开聊天室");
63                     break;
64                 }
65                 System.out.println(ip+":"+data);
66             }
67         }
68         catch (Exception e)
69         {
70             throw new RuntimeException("接收端失败");
71         }
72     }
73 }
74 class  ChatDemo
75 {
76     public static void main(String[] args) throws Exception
77     {
78         DatagramSocket sendSocket = new DatagramSocket();
79         DatagramSocket receSocket = new DatagramSocket(10002);
80         new Thread(new Send(sendSocket)).start();
81         new Thread(new Rece(receSocket)).start();
82     }
83 }


3.TCP协议的Socket
基本思路:
(1)建立客户端和服务器端
(2)建立连接后,通过Socket中的IO流进行数据的传输
(3)关闭socket
注意:
同样,客户端与服务器端是两个独立的应用程序。

3.1 客户端
(1)客户端的操作流程(这里以只发送数据为例)
1)创建TCPSocket服务,指定目的主机和端口。
2)为了发送数据,通过getOutputStream()方法获取客户端Socket中的输出流。
3)写数据到输出流, 并随网络发送到对应主机。
4)关闭Socket资源 对应的流资源不用关闭。这是因为Socket资源被释放之后,通过Socket本身获得的输出流也随着Socket资源的释放而释放。
注意:
客户端需要明确服务器的ip地址以及端口,这样才可以去试着建立连接,如果连接失败,会出现异常。连接成功,说明客户端与服务端建立了通道,那么通过IO流就可以进行数据的传输,而Socket对象已经提供了输入流和输出流对象,通过getInputStream(),getOutputStream()获取即可。与服务端通讯结束后,关闭Socket。

(2)代码体现
Socket s = new Socket("192.168.1.1",9999);
OutputStream out = s.getOutputStream();
out.write("hello".getBytes());
s.close();

3.2 服务端
(1)服务端的操作流程(这里以只接收数据为例)
1)创建TCPServerSocket服务,指定要监听的端口。
2)通过ServerSocket对象的accept()获取来自客户端的Socket对象。
3)通过获取到的客户端Socket对象的getInputStream()方法读取客户端发送的信息。
4)关闭ServerSocket资源(可选操作,一般情况下服务器端不会被关闭)
注意:
服务端需要明确它要处理的数据是从哪个端口进入的。当有客户端访问时,要明确是哪个客户端,可通过accept()获取已连接的客户端对象,并通过该对象与客户端通过IO流进行数据传输。当该客户端访问结束,关闭该客户端。

(2)代码体现
ServerSocket ss = new ServerSocket(9999);
Socket s = ss.accept ();
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int num = in.read(buf);
String str = new String(buf,0,num);
System.out.println(s.getInetAddress().toString()+":"+str);
s.close();
ss.close();

练习:并发上传图片。
思路:
客户端。
1,服务端点。
2,读取客户端已有的图片数据。
3,通过socket 输出流将数据发给服务端。
4,读取服务端反馈信息。
5,关闭。
服务端
为了可以让多个客户端同时并发访问服务端。那么服务端最好就是将每个客户端封装到一个单独的线程中,这样就可以同时处理多个客户端请求。
如何定义线程呢?
只要明确了每一个客户端要在服务端执行的代码即可。将该代码存入run方法中。

  1 import java.io.*;
  2 import java.net.*;
  3 
  4 //客户端
  5 class  PicClient
  6 {
  7     public static void main(String[] args)throws Exception
  8     {
  9 
 10   //对出入文件名进行健壮性判断
 11         if(args.length!=1)
 12         {
 13             System.out.println("请选择一个jpg格式的图片");
 14             return ;
 15         }
 16         File file = new File(args[0]);
 17         if(!(file.exists() && file.isFile()))
 18         {
 19             System.out.println("该文件有问题,要么不存在,要么不是文件");
 20             return ;
 21         }
 22         if(!file.getName().endsWith(".jpg"))
 23         {
 24             System.out.println("图片格式错误,请重新选择");
 25             return ;
 26         }
 27         if(file.length()>1024*1024*5)
 28         {
 29             System.out.println("文件过大,没安好心");
 30             return ;
 31         }
 32 
 33        //获取Socket对象
 34         Socket s = new Socket("192.168.1.254",10007);
 35 
 36         //读取一张图片并写出去
 37         FileInputStream fis = new FileInputStream(file);
 38         OutputStream out = s.getOutputStream();
 39         byte[] buf = new byte[1024];
 40         int len = 0;
 41         while((len=fis.read(buf))!=-1)
 42         {
 43             out.write(buf,0,len);
 44         }
 45         //告诉服务端数据已写完
 46         s.shutdownOutput();
 47 
 48         //接受客户端返回数据
 49         InputStream in = s.getInputStream();
 50         byte[] bufIn = new byte[1024];
 51         int num = in.read(bufIn);
 52         System.out.println(new String(bufIn,0,num));
 53         fis.close();
 54         s.close();
 55     }
 56 }
 57 
 58 //服务端线程
 59 class PicThread implements Runnable
 60 {
 61     private Socket s;
 62     PicThread(Socket s)
 63     {
 64         this.s = s;
 65     }
 66     public void run()
 67     {
 68         int count = 1;
 69         String ip  = s.getInetAddress().getHostAddress();
 70         try
 71         {
 72 
 73              //将客户端发送的图片存储起来
 74             System.out.println(ip+"....connected");
 75             InputStream in = s.getInputStream();
 76             File dir =  new File("d:\\pic");
 77             File file = new File(dir,ip+"("+(count)+")"+".jpg");
 78             while(file.exists())
 79                 file = new File(dir,ip+"("+(count++)+")"+".jpg");
 80             FileOutputStream fos = new FileOutputStream(file);
 81             byte[] buf = new byte[1024];
 82             int len = 0;
 83             while((len=in.read(buf))!=-1)
 84             {
 85                 fos.write(buf,0,len);
 86             }
 87 
 88             //返回数据
 89             OutputStream out = s.getOutputStream();
 90             out.write("上传成功".getBytes());
 91             fos.close();
 92             s.close();
 93         }
 94         catch (Exception e)
 95         {
 96             throw new RuntimeException(ip+"上传失败");
 97         }
 98     }
 99 }
100 
101 //服务端
102 class  PicServer
103 {
104     public static void main(String[] args) throws Exception
105     {
106         ServerSocket ss = new ServerSocket(10007);
107         while(true)
108         {
109 
110              //每连接一个客户端对象就开启一个线程,实现并发上传
111             Socket s = ss.accept();
112             new Thread(new PicThread(s)).start();
113         }
114         //ss.close();
115     }
116 }

 


练习:客户端通过键盘录入用户名,服务端对这个用户名进行校验。如果该用户存在,在服务端显示xxx,已登陆。并在客户端显示 xxx,欢迎光临。如果该用户不存在,在服务端显示xxx,尝试登陆。并在客户端显示 xxx,该用户不存在。最多就登录三次。

  1 import java.io.*;
  2 import java.net.*;
  3 
  4 //客户端
  5 class  LoginClient
  6 {
  7     public static void main(String[] args) throws Exception
  8     {
  9 
 10         //获取socket对象并指定目的主机和端口
 11         Socket s = new Socket("192.168.1.254",10008);
 12         BufferedReader bufr =
 13             new BufferedReader(new InputStreamReader(System.in));
 14         PrintWriter out = new PrintWriter(s.getOutputStream(),true);
 15 //        BufferedReader bufIn =new BufferedReader(new InputStreamReader(s.getInputStream()));
 16 
 17        //三次机会输入姓名
 18         for(int x=0; x<3; x++)
 19         {
 20             String line = bufr.readLine();
 21             if(line==null)
 22                 break;
 23             out.println(line);
 24             String info = bufIn.readLine();
 25             System.out.println("info:"+info);
 26             if(info.contains("欢迎"))
 27                 break;
 28         }
 29         bufr.close();
 30         s.close();
 31     }
 32 }
 33 
 34 //服务端线程
 35 class UserThread implements Runnable
 36 {
 37     private Socket s;
 38     UserThread(Socket s)
 39     {
 40         this.s = s;
 41     }
 42     public void run()
 43     {
 44         String ip = s.getInetAddress().getHostAddress();
 45         System.out.println(ip+"....connected");
 46         try
 47         {
 48 
 49            //最多只校验三次
 50             for(int x=0; x<3; x++)
 51             {
 52                 BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
 53                 String name = bufIn.readLine();
 54                 if(name==null)
 55                     break;
 56                 BufferedReader bufr = new BufferedReader(new FileReader("user.txt"));
 57                 //user.txt需要提前在当前目录下穿件,并记录用户名信息
 58                 PrintWriter out = new PrintWriter(s.getOutputStream(),true);
 59                 String line = null;
 60                 boolean flag = false;
 61 
 62               //增加健壮性判断,当客户端取消输入时的处理
 63                 while((line=bufr.readLine())!=null)
 64                 {
 65                     if(line.equals(name))
 66                     {
 67                         flag = true;
 68                         break;
 69                     }                
 70                 }
 71 
 72                //登录成功,返回数据
 73                 if(flag)
 74                 {
 75                     System.out.println(name+",已登录");
 76                     out.println(name+",欢迎光临");
 77                     break;
 78                 }
 79 
 80                 //登录失败
 81                 else
 82                 {
 83                     System.out.println(name+",尝试登录");
 84                     out.println(name+",用户名不存在");
 85                 }
 86             }
 87             s.close();
 88         }
 89         catch (Exception e)
 90         {
 91             throw new RuntimeException(ip+"校验失败");
 92         }
 93     }
 94 }
 95 class  LoginServer
 96 {
 97     public static void main(String[] args) throws Exception
 98     {
 99         ServerSocket ss = new ServerSocket(10008);
100 
101         //每连接到一个客户端,就开启一个新的线程
102         while(true)
103         {
104             Socket s = ss.accept();
105             new Thread(new UserThread(s)).start();
106         }
107     }
108 }

 


3.3 Tcp传输最容易出现的问题
客户端连接上服务端,两端都在等待,没有任何数据传输。这是因为read方法或者readLine方法是阻塞式。而服务端并不知道读取是否结束,会一直阻塞,此时客户端其实已经发送完毕,开始等待服务端返回信息,也在阻塞。
解决办法:
使用shutdownInput,shutdownOutput方法。
shutdownOutput();//关闭客户端的输出流。相当于给流中加入一个结束标记-1.

3.4 C/S通信原理
Server端和Client端通信原理
一个服务端的存在可以允许接入多个客户端。服务端ServerSocket对象本身没有流对象,但是客户端Socket对象本身有流对象。一旦某个客户端和服务端建立了连接之后,服务端会通过自身的accept方法获取到这个客户端的通信端点Socket对象的副本,并使用这个Socket副本和真实的客户端对象进行通信。因此在服务端的程序仅仅是通过ServerSocket对象获取到连接进来的Client的Socket对象之后,便是用这个Socket进行通信。

4.网络架构:

(1)C/S
Client/Server 即客户端/服务端。
特点:
1)客户端和服务端都需要单独编写软件。
2)维护较麻烦。
好处:可以减轻服务端的压力,如网络游戏。

(2)B/S
Browser/Server 即浏览器/服务端。
特点
1)客户端不用单独编写软件。因为客户端用的就是浏览器。
2)对于软件升级,只要考虑服务端即可。
弊端:所有的程序都运行在服务端,客户端的浏览器毕竟解析能力较弱。对游戏等。

####################################################################################
五 URL
####################################################################################

1.URL
类 URL 代表一个统一资源定位符,是对URL地址的封装,方便了我们对 URL 的操作。

(1)构造方法摘要
URL(String spec) 根据 String 表示形式创建 URL 对象。
URL(String protocol, String host, String file) 根据指定的 protocol 名称、host 名称和 file 名称创建 URL。
URL(String protocol, String host, int port, String file) 根据指定 protocol、host、port 号和 file 创建 URL 对象。
 
(2)常用方法摘要
String getProtocol()获取此 URL 的协议名称。
String getHost() 获取此 URL 的主机名(如果适用)。
String getPath()获取此 URL 的路径部分。
String getFile() 获取此 URL 的文件名。
String getQuery() 获取此 URL 的查询部
int getPort() 获取此 URL 的端口号。
URLConnection openConnection() 返回一个 URLConnection 对象,它表示到 URL 所引用的远程对象的连接。

2.URLConnection
抽象类。它代表应用程序和 URL 之间的通信链接。此类的实例可用于读取和写入此 URL 引用的资源。

(1)创建一个到 URL 的连接需要几个步骤:
1)通过在 URL 上调用 openConnection 方法创建连接对象。
2)处理设置参数和一般请求属性。
3)使用 connect 方法建立到远程对象的实际连接。
4)远程对象变为可用。远程对象的头字段和内容变为可访问。

(2)常用方法
InputStream getInputStream() 返回从此打开的连接读取的输入流。
OutputStream getOutputStream() 返回写入到此连接的输出流。
abstract void connect() 打开到此 URL 引用的资源的通信链接(如果尚未建立这样的连接)。

####################################################################################
六 正则表达式regex
####################################################################################

1.概述

正则表达式:符合一定规则的表达式。
作用:用于专门操作字符串。
特点:用于一些特定的符号来表示一些代码操作。这样可以简化书写。所以学习正则表达式,就是在学习一些特殊符号的使用。
好处:可以简化对字符串的复杂操作。
弊端:符号定义越多,正则越长,阅读性越差。

2.具体操作功能:
2.1 操作方法
(1)匹配:String  matches方法。用规则匹配整个字符串,只要有一处不符合规则,就匹配结束,返回false。
(2)切割:String split();
(3)替换:String replaceAll(regex,str);如果regex中有定义组,可以在第二参数中通过$符号获取正则表达式中的已有的组。
(4)获取:将字符串中的符合规则的子串取出。
    
2.2 思路方式
1)如果只想知道该字符是否对是错,使用匹配。
2)想要将已有的字符串变成另一个字符串,替换。
3)想要按照自定的方式将字符串变成多个字符串。切割。获取规则以外的子串。
4)想要拿到符合需求的字符串子串,获取。获取符合规则的子串。

2.3 操作步骤
1)将正则表达式封装成对象。
2)让正则对象和要操作的字符串相关联。
3)关联后,获取正则匹配引擎。
4)通过引擎对符合规则的子串进行操作,比如取出。

3.网页爬虫(蜘蛛)
做一个爬虫程序,到一个指定的网站把网页获取到以后,对其中数据进行逐行扫描,凡是符合指定规则的数据都被取出来。就能得到想要的数据百度就是自动散发大量爬虫(机器人/蜘蛛)到网络上获取各种信息,存储到自己的服务器上,所以我们才能搜索到结果。

制作一个爬虫程序

 1 import java.io.*;
 2 import java.util.regex.*;
 3 import java.net.*;
 4 import java.util.*;
 5 class MailsSpider{
 6     public static void main(String[] args) throws Exception{
 7         //getFileMails();
 8         getWebMails();
 9     }   
10    // 获取指定文档中的邮件地址。使用获取功能。Pattern  Matcher  
11     public static void getFileMails()throws Exception{
12         BufferedReader bufr =new BufferedReader(new FileReader("mail.txt"));//mail.txt为数据源,即从这里找信息
13         String line = null;
14 
15         //定义对邮件地址的校验规则
16         String mailreg = "\\w+@\\w+(\\.\\w+)+";
17         Pattern p = Pattern.compile(mailreg);
18         while((line=bufr.readLine())!=null){
19 
20             //将正则与目的字符串相关联
21             Matcher m = p.matcher(line);
22 
23            //通过匹配引擎的方法操作结果
24             while(m.find()){
25                 System.out.println(m.group());
26             }
27         }
28     }
29     //获取指定网页中的邮件地址。
30     public static void getWebMails()throws Exception{
31 
32         //将地址封装成URL对象
33         URL url = new URL("http://192.168.1.254:8080/myweb/mail.html");
34 
35          //获得URL 所引用的远程对象的连接
36         URLConnection conn = url.openConnection();
37 
38         //获得从连接读取的输入流
39         BufferedReader bufIn = new BufferedReader(new InputStreamReader(conn.getInputStream()));
40         String line = null;
41 
42           //校验邮箱
43         String mailreg = "\\w+@\\w+(\\.\\w+)+";
44         Pattern p = Pattern.compile(mailreg);
45         while((line=bufIn.readLine())!=null){
46             Matcher m = p.matcher(line);
47             while(m.find()){
48                 System.out.println(m.group());
49             }
50         }
51     }
52 }

 

转载于:https://www.cnblogs.com/emos/p/3367305.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值