1 网络编程概述
网络编程就是通过使用套接字(Socket)来达到进程间通信目的的编程,在两个设备之间进行数据交换。
在网络中的连接,是基于请求/响应的方式,就是发起连接请求的程序(Client客户端),发送连接请求到接收请求的程序(Server服务端),客户端可以在需要时才启动,而服务端需要一直运行,一直侦听请求。
网络模型:
OSI七层模型从下往上:物理层、数据链路层、网络层、传输层、会话层、表示层、应用层。
TCP/IP四层模型从下往上:网络接入层(主机-网络)、网际互联层、传输层、应用层。
常用概念:
IP地址:IP地址是网络中设备的标识,是一台主机在网络中的地址。127.0.0.1是本地回环地址。
端口号:数据要发送到对方主机上指定的应用程序上,为了标识这些应用程序,所以给这些网络应用程序都用数字进行标识。为了方便称呼这个数字,叫做端口(逻辑端口),是不同进程的标识。
有效端口:0~65535,其中0~1024系统使用或保留端口。
传输协议:我们定义了网络中的通信规则,这个通信规则称为协议。国际组织定义了通用协议TCP/IP。
TCP/IP模型中,
应用层协议有HTTP、FTP协议,
传输层有TCP、UDP协议,
网际层有IP协议,
网络接入层有ARP协议。
2 IP地址InetAddress类
java.net包中的InetAddress类:此类表示互联网协议 (IP) 地址。
InetAddress类中常用方法:
getLocalHost():静态方法,获取本地主机的IP地址对象,返回InetAddress类型。
getByName():静态方法,根据一个主机名或IP地址,返回一个InetAddress对象。
getAllByName():静态方法,根据一个主机名,返回其IP地址组成的数组,InetAddress[]。
getHostAddress():获取InetAddress对象的主机IP地址,返回字符串类型。
getHostName():获取InetAddress对象的主机名,返回字符串类型。
代码演示:
import java.net.*;
class IPDemo {
public static void main(String[] args) throws Exception {
//获取一个InetAddress对象
InetAddress i = InetAddress.getLocalHost(); //获取本地主机的IP地址对象。
System.out.println(i.toString());
System.out.println("address: "+i.getHostAddress());//返回主机IP地址
System.out.println("name: "+i.getHostName());//返回主机名
//根据IP或主机名,获取一个InetAddress对象
InetAddress ia = InetAddress.getByName("192.168.0.103");
System.out.println("罗琦HostAddress: "+ia.getHostAddress());
System.out.println("罗琦HostName: "+ia.getHostName());
//根据IP或主机名,获取一个InetAddress对象。
InetAddress[] ib = InetAddress.getAllByName("www.baidu.com");
for(InetAddress ibb : ib){
System.out.println("百度HostAddress: "+ibb.getHostAddress());
System.out.println("百度HostName: "+ia.getHostName());
}
}
}
3 TCP、UDP和Socket(套接字)
TCP和UDP是两种不同的传输层协议,TCP即传输控制协议,UDP即用户数据报协议。
TCP特点:
1,建立连接,形成传输数据的通道。
2,在连接中进行大数据量传输。
3,通过三次握手完成连接,是可靠协议。
4,必须建立连接,效率会稍低。
UDP特点:
1,将数据及源和目的封装成数据包中,不需要建立连接。
2,每个数据报的大小在限制在64k内。
3,因无连接,是不可靠协议。
4,不需要建立连接,速度快。
Socket即套接字,是进程间通信的机制,在网络上的主机一般运行了多个服务软件,同时提供几种服务。每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务。Socket英文原意为插座,在Socket通信机制中,一台主机就像一个插座,每个服务对应的端口就是一个插孔,连接到不同的端口,就对应不同的服务。
Socket特点:
1,Socket就是为网络服务提供的一种机制。
2,通信的两端都有Socket。
3,网络通信其实就是Socket间的通信。
4,数据在两个Socket间通过IO传输。
4 UDP发送端实例——DatagramSocket类、DatagramPacket类
定义一个UDP协议的发送端。
需求:通过UDP传输方式,将一段文字数据发送出去。
思路:
1,建立UDP的socket服务。
2,提供数据,并将数据封装到数据包中。
3,通过socket服务的发送功能,将数据包发出去。
4,关闭资源。
DatagramSocket类,是UDP协议中用于发送和接收数据包的套接字。Java.net包中。
DatagramSocket( int)构造方法,创建一个Socket并绑定到本地主机的指定端口。
发送时,通过构造函数指定要发送到的端口。
接收时,通过构造函数指定要接收哪个端口上的数据包。
send( dp)方法:从这个Socket发送数据包。
receive( dp)方法:从这个Socket接收数据包,阻塞式方法。
DatagramPacket类,表示数据包。Java.net包中的。
getAddress():DatagramPacket类的getAddress方法返回数据包中IP地址,InetAddress对象。
getData():获取数据包中的数据,返回字节数组。
getLength():返回数据包中数据的长度,字节数组长度。
getPort():返回端口号,数据包发送到这个端口,或从这个端口发出。
代码示例:
import java.net.*;
class UdpSend { //创建服务、IP地址均会出现异常,send()有IO异常
public static void main(String[] args) throws Exception{
//1,创建udp的socket服务。通过DatagramSocket对象。
DatagramSocket ds = new DatagramSocket(); //套接字绑定到本地任意可用端口。
//2,确定数据,并封装成数据包。
//DatagramPacket(byte[] buf, int length, InetAddress address, int port)
byte[] buf = "udp ge men lai la".getBytes();
DatagramPacket dp =
new DatagramPacket(buf,buf.length,InetAddress.getByName("127.0.0.1"),10000);
//3,通过socket服务,将已有的数据包发送出去,通过send方法。
ds.send(dp);
//4,关闭资源。
ds.close();
}
}
5 UDP接收端实例、阻塞式方法
定义一个UDP协议的接收端。
需求:定义一个应用程序,用于接收UDP协议传输的数据并处理的。
思路:
1,创建UDP的Socket对象。通常会监听一个端口,其实就是给这个接收应用程序,定义一个数字标识(端口)。方便于明确哪些数据过来该应用程序可以处理。
2,创建一个数据包,因为要存储接收到的字节数据。因为数据包对象中有更多功能,可以提取字节数据中的不同数据信息。
3,通过Socket对象的receive方法,将收到的数据存入已定义好的数据包中。
4,通过数据包对象的特有功能,将这些不同的数据取出,打印在控制台上。
5,关闭资源。
DatagramSocket的receive( dp)是阻塞式方法。
阻塞式方法:程序一直停在这里等,直到接收到数据才继续执行。IO流中的输入流的read方法也是阻塞式方法。
代码示例:
import java.net.*;
class UdpReceive{
public static void main(String[] args) throws Exception{
//1,创建udp的socket服务,指定端口。
DatagramSocket ds = new DatagramSocket(10000);//监听10000端口,如果不指定,端口随机分配
//2,定义数据包,用于存储接收到的数据。
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf,buf.length);//数据包存储buf字节数组
//3,通过服务的receive方法,将收到的数据存入数据包中。
ds.receive(dp); //阻塞式方法:程序一直停在这里等,接收到数据才继续执行。
//4,通过数据包的方法,获取其中的数据。(IP地址、端口、数据)
String ip = dp.getAddress().getHostAddress();//getAddress()返回主机的InetAddress对象(即IP地址)
//getHostAddress()返回InetAddress对象的主机ip
String data = new String(dp.getData(),0,dp.getLength());//得到数据包中封装的数据。
int port = dp.getPort(); //得到端口。
System.out.println(ip+"::"+data+"::"+port); //打印出来
//5,关闭资源。
ds.close();
}
}
6 完整的UDP传输实例(键盘录入方式)
使用键盘录入的方式,实现UDP数据传输。
发送端通过键盘,每录入一些数据,接收端就接收一些数据。再发送,再接收。
两个包含主函数的类在一个java文件中,先统一编译,再分别执行。
即:javac UdpDemo2.java
java UdpSend2
java UdpReceive2
代码示例:
import java.net.*; //用到net包往往会用到io包,网络传输用到IO流
import java.io.*;
class UdpSend2 {
public static void main(String[] args) throws Exception{
DatagramSocket ds = new DatagramSocket(); //创建Socket服务
BufferedReader bufr =
new BufferedReader(new InputStreamReader(System.in));//读取键盘录入
String line = null;
while((line=bufr.readLine())!=null){ //readLine阻塞式方法
if(line.equals("886"))
break;
byte[] buf = line.getBytes();
//创建一个发送的数据包,其中定义了数据、数据长度、目的ip、目的端口。
DatagramPacket dp =
new DatagramPacket(buf,buf.length,InetAddress.getByName("127.0.0.1"),10001);//本机IP
ds.send(dp); //Socket服务把数据包dp发送出去,udp传输。
}
ds.close(); //关闭Socket资源。
}
}
class UdpReceive2 {
public static void main(String[] args) throws Exception{
DatagramSocket ds = new DatagramSocket(10001);//绑定10001端口
while(true){
byte[] buf = new byte[1024*64]; //数据包最大64KB
//创建一个存储接收数据的数据包,定义了数据、数据长度。
DatagramPacket dp = new DatagramPacket(buf,buf.length);
ds.receive(dp); //阻塞式方法,Socket接收数据并存入dp数据包中。
String ip = dp.getAddress().getHostAddress(); //获取IP地址
String data = new String(dp.getData(),0,dp.getLength());
System.out.println(ip+"::"+data);
}
//没有关闭资源,演示阻塞式方法。
}
}
7 UDP聊天程序
需求:编写一个聊天程序,这一个程序中有接收数据的部分,也有发送数据的部分。这两部分需要同时执行,需要用多线程技术。
思路:
1,需要用到多线程技术。一个线程控制收,一个线程控制发。
2,因为收和发动作是不一致的,所以要定义两个run方法。
3,而且这两个方法要封装到不同的类中。
为了方便演示阻塞式方法,此例中没有关闭资源DatagramSocket。
代码示例:
import java.net.*;
import java.io.*;
class Send implements Runnable {//实现Runnable接口,多线程的方式之一。
private DatagramSocket ds;
public Send(DatagramSocket ds){
this.ds = ds;
}
public void run(){
try{
BufferedReader bufr =
new BufferedReader(new InputStreamReader(System.in)); //读取键盘录入
String line = null;
while((line=bufr.readLine())!=null){ //readLine()阻塞式方法
if(line.equals("886"))
break;
byte[] buf = line.getBytes();
//创建一个发送的数据包,其中定义了数据、数据长度、目的ip、目的端口。
DatagramPacket dp =
new DatagramPacket(buf,buf.length,InetAddress.getByName("127.0.0.1"),10002);
ds.send(dp); //Socket服务把数据包dp发送出去,udp传输方式。
}
}
catch(Exception e){
throw new RuntimeException("发送端失败");
}
}
}
class Receive implements Runnable{
private DatagramSocket ds;
public Receive(DatagramSocket ds){
this.ds = ds;
}
public void run(){
try{
while(true){
byte[] buf = new byte[1024*64];
DatagramPacket dp = new DatagramPacket(buf,buf.length);//创建一个存储接收数据的数据包
ds.receive(dp); //阻塞式方法,Socket接收数据并存入dp数据包中。
String ip = dp.getAddress().getHostAddress();
String data = new String(dp.getData(),0,dp.getLength());
System.out.println(ip+"::"+data);
}
}
catch(Exception e){
throw new RuntimeException("接收端失败");
}
}
}
class ChatDemo{
public static void main(String[] args) throws Exception{//抛出监听端口时的Socket异常
DatagramSocket sendSocket = new DatagramSocket();
DatagramSocket receSocket = new DatagramSocket(10002);
new Thread(new Send(sendSocket)).start(); //启动线程
new Thread(new Receive(receSocket)).start();
}
}