接触网络编程之前,首先需要了解计算机网络的概念:
网络即将不同区域的电脑连接到一起,组成局域网、城域网、或者广域网。把分布在不同地理区域的计算机与专门的外部设备用通信线路互连成一个规模大、功能强的网络系统,从而使众多的计算机可以方便的互相传递信息,共享硬件、软件、数据信息等资源。
局域网的图片
( IP) 是 Internet Protocol 的外语缩写, 网络之间互连的协议也就是为计算机网络相互连接进行通信而设计的协议。 在因特网中,它是能使连接到网上的所有计算机网络实现相互通信的一套规则,规定了计算机在因特网上进行通信时应当遵守的规则。任何厂家生产的计算机系统,只要遵守 IP 协议就可以与因特网互连互通。
端口:
区分数据流向的软件 0-65535不要使用 1024 以下的端口 ,容易和计算机本身的各种端口冲突每一个协议拥有自己的端口,在同一个协议下端口不能重复 ,常用端口 -> FTP:21 HTTP:80网络编程中类InetAddress操作IP地址的一些常用操作:
该类没有构造器,直接使用类调用静态static方法
byte[] getAddress() 返回此 InetAddress对象的原始IP地址。 static InetAddress getByName(String host) 确定主机名称的IP地址。 String getHostAddress() 返回文本显示中的IP地址字符串。 String getHostName() 获取此IP地址的主机名。 static InetAddress getLocalHost() 返回本地主机的地址。端口是虚拟的概念,并不是说在主机上真的有若干个端口。通过端口,可以在一个主机上运行多个网络应用程序。可以类比为:IP=公司,端口=公司的各个部门,URL=各部门的人员
包含端口,用于Socket通信的方法及构造器
//1) 、获取对象 InetSocketAddress(String hostname, int port) InetSocketAddress(InetAddress addr, int port) //2) 、方法 getAddress() //返回 InetAddress 对象 getPort() //返回端口 getHostName() //返回域名URl全称(Uniform Resource Location)统一资源定位符。
**作用:**标识一个资源,同时还会为资源提供一个特定的网络位置,客户端可以通过它来获取URL对应的资源。
语法形式:
protocol://userInfo@host:port/path?query#fragment 协议://用户信息@主机名:端口/路径?查询#锚点IP地址唯一标识了Internet上的计算机,而URL则标识了这些计算机上的资源。
JDK中提供了URL类,该类的全名是java.net.URL,有了这个类,就可以使用它当中的各种方法对URL对象进行分割、合并等处理。
了解到这,我们可以简易的写一个爬虫实例:
public class SpiderDemo01 { public static void main(String[] args) throws IOException { // 1、创建一个URL类对象,使用其构造器获取 URL URL url = new URL("http://www.baidu.com"); // 2、使用字符流的缓冲流读取网页的内容 // url.openStream()方法返回一个InputStream字节输入流 BufferedReader rd = new BufferedReader(new InputStreamReader(url.openStream())); // 3、读取网页数据 String msg = null; // 循环读取内容,每次读一行 while ((msg=rd.readLine())!=null){ System.out.println(msg); } // 4、关闭缓冲流 rd.close(); } } // 有些网页做了处理,无法爬取内容,感兴趣的话自己了解一下TCP(Transfer Control Protocol):类似于打电话,面向连接、安全、可靠、但是效率低
UDP(UserDatagramProtocol):类似于发短信,非面向连接、不安全、数据可能丢失,但是效率高
简易版服务器端和客户端的连接测试:
服务端代码:
public class UdpCopyReceive { public static void main(String[] args) throws Exception { // 1、定义接收端,DatagramSocket的构造器方法 DatagramSocket rec = new DatagramSocket(9999); System.out.println("-----------接收端----------"); // 2、准备包裹,用来接收数据 BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("2.jpg")); byte[] bytes = new byte[10];// 接收数据的字节数组 DatagramPacket packet = new DatagramPacket(bytes, bytes.length);// 打包数据 while (true){ // 3、接收数据 rec.receive(packet); // 4、处理数据 byte[] arr = packet.getData(); int len = packet.getLength(); // 结束循环的条件 if ("结束".equals(new String(arr,0,len))){ break; } // 5、写入数据 bos.write(arr,0,len); } System.out.println("接收结束"); // 6、刷新 bos.flush(); // 7、关闭 bos.close(); rec.close(); } }客户端代码:
public class UdpCopySend { public static void main(String[] args) throws Exception { //1、定义发送端 DatagramSocket() DatagramSocket sendF = new DatagramSocket(8888); System.out.println("-----------发送端----------"); // 2、准备数据 File的构造器方法,参数:文件路径 File f = new File("1.jpg"); // 3、获取缓冲流,字符读取 BufferedInputStream br = new BufferedInputStream(new FileInputStream(f)); // 4、定义一个字节数组 byte[] bytes = new byte[1024*60]; int len; DatagramPacket dp = null; while ((len=br.read(bytes))!=-1){ // 5、打包数据 dp = new DatagramPacket(bytes,0,len,new InetSocketAddress("192.168.4.196",8999)); // 6、发送 sendF.send(dp); } // 7、发送一个结束的标识 sendF.send(new DatagramPacket("结束".getBytes(),0,"结束".getBytes().length,new InetSocketAddress("192.168.4.196",8999))); // 8、关闭 sendF.close(); } }基于 tcp 协议,建立稳定连接的点对点的通信;实时、快速、安全性高、占用系统资源多、效率低;“请求-响应”模 式: a)、客户端:在网络通讯中,第一次主动发起通讯的程序被称作客户端(Client)程序
b)、服务器:第一次通讯中等待连接的程序被称作服务器端(Server)程序
与其相关类:InetAddress:封装计算机的IP地址和DNS(没有端口信息)
该类没有构造函数。如果想要得到对象,只能通过静态方法:
getLocalHost,getByName,getAllByName,getAddress,getHostName
具体方法前往官网查看:
这里简单实现一个TCP三次握手建立连接的服务器端和客户端的案例:
模拟登陆客户端和服务端交互,使用了多线程处理多个客户端访问服务器端
服务端:
public class TCPLoginServer{ public static void main(String[] args) throws Exception { // 1、开启服务器端 ServerSocket server = new ServerSocket(8998); System.out.println("-------开启服务器端-------"); // 2、通过死循环监听窗口,获取Socket while (true) { // 3、通过阻塞式监听获取Client客户端 Socket s = server.accept(); System.out.println("客户端开始请求"); // 4、new 一个客户端对象 -> 我的理解是每获取到一个请求,就生成一个客户端对象,将得到的socket传进去,得到其Socket管道中的I/O流 Client c = new Client(s); // 5、开启客户端对应线程 Thread th = new Thread(c); th.start(); } } } // 通过一个client类实现不同客户端开启不同线程 class Client implements Runnable{ private Socket socket; public Client(Socket socket) { this.socket = socket; } @Override public void run() { try { // 1、获取一个基本数据输入流 DataInputStream dis = new DataInputStream(new BufferedInputStream(socket.getInputStream())); // 2、读取数据 String msg = dis.readUTF(); // 3、处理数据 String[] strs = msg.split("&"); String userName = strs[0].substring(strs[0].indexOf("=")+1); String password = strs[1].substring(strs[1].indexOf("=")+1); // 4、判断用户名和密码并返回信息 // 1)打开一个基本数据输出流 DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream())); // 2)判断用户名和密码 if ("yls".equals(userName)){ if ("123".equals(password)){ dos.writeUTF("登录成功!"); }else{ dos.writeUTF("密码错误!"); } }else{ dos.writeUTF("用户名错误"); } // 3)刷出 dos.flush(); // 5、关闭 dos.close(); dis.close(); } catch (IOException e) { e.printStackTrace(); } } }客户端:
public class TCPLoginClient { public static void main(String[] args) throws Exception { // 1、开启客户端 Socket client = new Socket("127.0.0.1", 8998); System.out.println("--------客户端--------"); // 使用 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); System.out.println("请输入用户名:"); String name = br.readLine(); System.out.println("请输入密码:"); String pwd = br.readLine(); // 2、获取输出流,使用DataOutputStream发送基本数据流 DataOutputStream os = new DataOutputStream(new BufferedOutputStream(client.getOutputStream())); // 3、发送一个基本数据流 os.writeUTF("username="+name+"&password="+pwd); os.flush(); // 接收输入流 DataInputStream is = new DataInputStream(new BufferedInputStream(client.getInputStream())); // 接收返回的数据 String msg = is.readUTF(); System.out.println(msg); // 4、关闭 is.close(); os.close(); client.close(); } }