网络编程
网络通讯:
1、找到对方,通过IP地址
2、数组要发送到对方指定的应用程序上,为了辨识这些应用程序,所以给这些网络应用程序都用数字进行标识,为了方便称呼这个数字,叫做端口(逻辑端口)。
3、定义通讯规则,这个通讯规则称为协议
国际组织定义了一个通用的协议TCP/IP。
特殊IP地址:127.0.0.1本机回返地址
一般把0~1024的端口数留给系统使用。
三要素:IP,端口,协议
1、IP地址:InetAddress类
a) 网络中设备的标识
b) 不易记忆,可用主机名
c) 本机回环地址:127.0.0.1 主机名:localhost
2、端口号
a) 用于标识进程的逻辑地址,不同进程的标识
b) 有效端口:0~65535,其中0~1024系统使用或保留端口
3、传输协议
a) 通讯的规则
b) 常见协议:TCP,UDP
InetAddress类
该类表示互联网协议(IP)地址
重要方法:
Static InetAddress getLocalHost()返回本机
String getHostAddress()返回IP地址字符串
String getHostName()获取此IP地址的主机名
Static InetAddress getByName(String host)该方法可以在给定主机名的情况下确定主机的IP地址。
Static InetAddress[] getAllByName(String host)在给定主机名的情况下,根据系统上配置的名称服务返回其IP地址所组成的数组。
传输协议
1、UDP:
a) 将数据及源和目的封装成数据包中,不需要建立连接
b) 每个数据报的大小限制在64k内
c) 因无连接,是不可靠协议
d) 不需要建立连接,速度快
例子:邮政邮递邮件的例子
聊天软件的例子
网络视频
2、TCP
a) 建立连接,形成传输数据的通道
b) 在连接中进行大数量的传输
c) 通过三次握手完成连接,是可靠协议
d) 必须建立连接,效率会稍低
例子:相当于打电话
下载的例子
Socket
1、Socket就是为网络服务提供的一种机制
2、通信的两端都有Socket
3、网络通信其实就是Socket间的通信
4、数据在两个Socket间通过IO传输
UDP传输:
1、DatagramSocket与DatagramPacket
2、建立发送端与接收端
3、建立数据包
4、调用Socket的发送接收方法
5、关闭Socket
发送端和接收端是两个独立的运行程序
DataGramSocket类
此类表示用来发生和接收数据报包的套接字
有发送和接收方法:
Void receive(DatagramPacket p)从此套接字接收数据报包
Void send(DatagramPacket p)从此套接字发送数据报包
Socket:套接字
Receive:收到,接收
Send:发送
DatagramPacket类
此类表示数据报包
Packet:数据包
UDP传输代码示例:重点是理解传输的步骤
/** * 需求:定义一个应用程序,用于接收UDP协议传输的数据并处理 * 定义UDP的接收端: * 思路: * 1、定义UDPSocket服务,通常会监听一个端口,其实就是给这个接收网络应用程序定义数据标识。 * 方便于明确哪些数据过来,该应用程序可以处理 * 2、定义一个数包,因为要存储接收的字节数据,因为包对象中有更多功能可以提取字节数据中的不同数据信息 * 3、通过Socket服务的receive方法将收到的数据存入到已定义好的数据包中 * 4、通过数据包对象的特有功能将这些不同的数据取出,打印在控制台上 * 5、关闭资源 * */ public class UDPReceive { public static void main(String[] args) { udpReceive(); } /** * 该方法用于UDP的数据接收 */ public static void udpReceive(){ DatagramSocket ds = null; try { //1、创建UDP,服务,通过DatagramSocket对象 //要记住标识接收的端口,若不然不能接收数据 ds = new DatagramSocket(10000); //2、创建数据包,用于接收发送过来的字节数据 byte[] buf = new byte[65536]; DatagramPacket dp = new DatagramPacket(buf,buf.length); //3、接收发送过来的信息,通过receive方法 ds.receive(dp); //4、通过数据包中方法,将这些数据取出打印在控制台 System.out.println(dp.getAddress().getHostAddress()); System.out.println(new String(dp.getData(),0,dp.getLength())); System.out.println(dp.getPort()); } catch (Exception e) { throw new RuntimeException(e.toString()); } finally { try { if(ds!=null){ ds.close(); } } catch (Exception e2) { throw new RuntimeException(e2.toString()); } } }
} /** * 需求: * 通过UDP传输方式,将一段文字数据发送出去 * 定义了一个UDP的发送端 * 思路: * 1、建立UDPSocket服务 * 2、提供数据,并将数据封装到数据包中 * 3、通过Socket服务的发送功能将数包发出去 * 4、关闭资源 * */ public class UDPSend { public static void main(String[] args) { udpSend(); } /** * 该方法用于UDP的数据发送 */ private static void udpSend() { DatagramSocket ds = null; try { //1、创建UDP服务,通过DatagramSocket对象 ds = new DatagramSocket(); //2、确定数据,并封装成数据包 byte[] buf = "UDP 哥们来了!".getBytes(); DatagramPacket dp = new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.1.145"),10000); //3、通过Socket服务,将已有的数据包发送出去。通过send方法 ds.send(dp); } catch (IOException e) { throw new RuntimeException(e.toString()); } finally { try { if(ds!=null){ //4、关闭资源 ds.close(); } } catch (Exception e2) { throw new RuntimeException(e2.toString()); } } } }
用UDP传输协议制作的聊天小软件
代码示例:
/** * 聊天软件的接收 * @author xiuyuan * */ public class UDPReceive_3 implements Runnable{ private DatagramSocket ds; public UDPReceive_3(DatagramSocket ds){ this.ds = ds; } public void run(){ try { while(true){ //将接收的数据读取到数包中 byte[] buf = new byte[65536]; DatagramPacket dp = new DatagramPacket(buf,buf.length); ds.receive(dp); //获取数据包中的信息 String id = dp.getAddress().getHostAddress(); String data = new String(dp.getData(),0,dp.getLength()); //将获取的信息打印到控制台 System.out.println(id+":"+data); } } catch (Exception e) { throw new RuntimeException(e.toString()); } } } /** * 聊天软件的发送端 * @author xiuyuan * */ public class UDPSend_3 implements Runnable{ private DatagramSocket ds; public UDPSend_3(DatagramSocket ds){ this.ds = ds; } public void run(){ BufferedReader bufr = null; try { //读取键盘录入信息 bufr = new BufferedReader(new InputStreamReader(System.in)); String line = null; while((line = bufr.readLine())!=null){ //键盘录入接收标记 if("886".equals(line)){ break; } //创建数据包,用来封装数据 byte[] buf = line.getBytes(); DatagramPacket dp = new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.1.145"),10002); //将数据包中的信息发送出去 ds.send(dp); } } catch (Exception e) { throw new RuntimeException(e.toString()); } finally { try { if(bufr!=null){ bufr.close(); } } catch (Exception e2) { throw new RuntimeException(e2.toString()); } } } } /** *需求:模拟聊天软件通信 * @author xiuyuan * */ public class UDPTest { public static void main(String[] args) { try { new Thread(new UDPReceive_3(new DatagramSocket(10002))).start(); new Thread(new UDPSend_3(new DatagramSocket())).start(); } catch (Exception e) { throw new RuntimeException(e.toString()); } } }
TCP传输
1、Socket和ServerSocket
2、建立客户端和服务器端
3、建立连接后,通过Socket中的IO流进行数据的传输
4、关闭Socket
客户端与服务器端是两个独立的应用程序
演示tcp传输
1、tcp分客户端和服务器端
2、客户端对应的对象是Socket
服务器端对应的对象是ServerSocket
需求:用客户端向服务端发送文本信息
客户端:
通过查阅Socket对象,发现在该对象在建立时,就可以去连接指定的主机。因为tcp是面向连接的,所以在建立Socket服务时就要有服务端存在并连接成功,形成通路后在该通道进行数据的传输。
步骤:
1、创建Socket服务,并指定要连接的主机和端口
Socket类
此类实现了客户端套接字,套接字是两台机器间通信的端点。
方法:
getInputStream()可以获得Socket对象的输入流
getOutputStream可以获得Socket对象的输出流
getInetAddress()方法可以获得InetAddress对象,通过该对象getHostAddress()返回获得IP地址
服务器端:
1、建立服务器端的Socket服务,通过ServerSocket()建立。并监听一个端口
2、获取连接过来的客户端对象,通过ServerSocket的accept方法获取。没有连接就会等,所以这个方法是阻塞式的,
3、客户端如果发过来数据,俺们服务器端要使用对应的客户端对象,并获取到该客户端对象的读取流来读取发送过来的数据,并打印在控制台
4、关闭服务端。(可选操作)
ServerSocket类
此类实现服务器套接字。
方法accept()可以获取访问服务器的客户端对象Socket对象
需求:客户端给服务端发送数据,服务端收到后,给客户端反馈信息
客户端:
1、建立Socket服务,指定要连接的主机和端口
2、获取Socket流中的输出流,将数据写到该流中。通过网络发送给服务端
3、获取Socket流中的输入流,将服务端反馈的数据获得到,并打印
4、关闭客户端资源
练习:
需求:建立一个文本转换服务器,客户端给服务端发送文本数据,服务端会将文本转成大写后再返回给客户端,而且客户端可以不断的发送文本数据,当客户端输入over时,转换结束
该例出现的问题:数据发送过去,但对方没有接受到,一个原因是数据根本没有发送出去,原因是发送时使用了缓冲区,但没有刷新,数据还在缓冲区中。
还有就是数据已经发送过去,但没有明确结束标记,对方一直在等待数据,造成不能通信。
TCP通信,上传图片
注意问题:
1、客户端写数据给服务端,写完后必须给服务端一个结束标记,若不然会使服务端一直处于读取客户端发送过来的数据,操作阻塞,不能给客户端发送反馈信息,客户端也在等待。
解决该问题的方法,就是给服务端发送结束标记:shudownOutput(),该方法为禁用套接字的输出流,对应TCP套接字,任何以前写入的数据都将被发送,并且后跟TCP的正常连接终止序列。如果在套接字上调用shutdownOutput()后写入套接字输出流,则该流将抛出IOException异常
客户端并发上传的问题:
服务端:当A客户端连接上以后,被服务端获取到,服务端执行具体执行流程。这时,B客户端连接,只有等待,因为服务端还没有处理完A客户端的请求,还没有循环回来执行accept方法,所以暂时获取不到B客户端对象,为了可以让多个客户端同时并发访问服务端,服务端最好就是将每个客户端封装到一个单独的线程中,这样就可以同时处理多个客户端请求
如何定义线程呢?
只有明确了每一个客户端要在服务端执行的代码即可,将该代码存入run方法中,
服务器必须都是多线程的
服务器原理:服务器都是多线程的,创建一个类,继承Runnable接口,覆盖run方法,并且必须将服务器接收都的客户端对象传入该类中,run方法里面封装了服务器对客户端的处理方法。
浏览器访问服务器端时,会按规则向服务器端发送请求消息头
URL同一资源定位符,它是指向互联网“资源”的指针。资源可以说是简单的文件或目录,也可以是更为复杂的对象的引用,
常用方法:
String getFile(); 获取此URL的文件名
String getHost(); 获取此URL的主机名
String getProtocol(); 获取此URL的协议名称
Int getPort(); 获取此URL的端口号
String getPath(); 获取此URL的路径部分
String getQuery(); 获取此URL的查询部分
URLConnection openConnection();返回一个URLConnection对象,它表示到URL所引用的远程对象的连接。
URLConnection类
抽象类URLConnection是所有类的超类,它代表应用程序和URL之间的通信链接。
通过该类下的方法getInputStream()可以获得从此打开的连接读取的输入流
域名解析:
DNS:将主机名翻译成IP地址
访问服务器时,为了方便记忆,不会直接写ip地址,而是写访问的主机名,然后通过DNS域名解析翻译成对应的ip地址返还给浏览器,浏览器在根据ip地址访问对应的主机。
DNS会先走本机的配置表,找到对象的解析,如果本机没有,采用会访问公网的。
电脑没有设置DNS时,会自动默认寻找附近的DNS进行解析
网络架构:
1、C/S client/server
特点:
该结构的软件,客户端和服务端都需要编写。
开发成本较高,维护较为麻烦。
好处:
客户端在本地可以分担一部分运算。
2、B/S browser/server
特点:
该结构的软件,只开发服务器端,不开发客户端,因为客户端直接由浏览器取代
开发成本相对低,维护更为简单。
缺点:
所有运算都要在服务端完成。