Java基础7——网络编程
计算机网络是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统。
网络编程就是用来实现网络互联的不同计算机上运行的程序间可以进行数据交换
网络模型研究计算机网络之间以何种规则进行通信
网络模型一般是指OSI参考模型 TCP/IP参考模型
网络编程三要素
IP地址——网络中每台计算机的唯一标识
端口——计算机上每个应用程序的唯一标识
协议——通信之间的规则
计算机只能识别二进制的数据,所以我们的IP地址应该是一个二进制的数据
但是我们配置的IP地址不是二进制的
IP:192.168.1.100
换算11000000 10101000 00000001 01100100
假如真是11000000 10101000 00000001 01100100的话每次配置的时候非常麻烦
为了方便表示,我们就把IP地址每个字节上的数据换算成十进制,然后用.分开表示
这种表示方法叫点分十进制
IP地址的组成:网络号段+主机号段
IP地址的分类:
A类:第一号段为网络号段+后三段的主机号段
一个网络号段可以配256*256*256台计算机
B类:前二号段为网络号段+后二段的主机号段
C类:前三号段为网络号段+后一段的主机号段
ipconfig 查看本机IP地址
ping 后面跟ip地址 测试本机与指定ip地址间的通信是否有问题
x.x.x.255 广播地址
x.x.x.0 网络地址
//InetAddress无构造方法 //如果一个类没有构造方法: //A:成员全部是静态的(Math,Arrays,Collection) //B:单例设计模式(Runtime) //C:类中有静态方法返回该类的对象(InetAddress) //class Demo{ // private Demo(){} // public static Demo getXxx(){ // // } //} public class InetAddressDemo(){ public static void main(String[] args) throws UnknownHostException{ //根据主机名或IP地址的字符串表示得到IP地址对象 //InetAddress address=InetAddress.getByName("192.168.1.23"); InetAddress address=InetAddress.getByName("aaa"); //获取主机名 IP地址 String name=address.getHostName(); String ip=address.getHostAddress(); } }
端口号
物理端口:网卡口
逻辑端口:平时说的端口就是逻辑端口
每个网络程序都至少有一个逻辑端口
用于标识进程的逻辑地址,不同进程的标识不同
有效端口:0-65535,其中0-1024是系统使用或保留端口
端口号是正在运行的程序的标识
协议
通信规则
UDP:将数据源和目的封装成数据包,不需要建立连接,每个数据包的大小限制在64K,无连接,不可靠协议,速度快
TCP:建立连接,形成传输数据的通道,在连接中进行大数据量传输,通过三次握手完成连接,是可靠协议,速度慢
TCP协议又叫三次握手协议
举例:
UDP:发短信 群聊
TCP:打电话
Socket网络套接字
网络上具有唯一标识的IP地址和端口号组合在一起才能构成唯一能识别的标识符套接字
UDP发送和接收数据
//UDP发送数据 //创建发送端的Socket对象 //创建数据,并把数据打包 //调用Socket对象的发送方法发送数据包 //释放资源 public class SendDemo{ //通过查看源码,SocketException继承自IOException //public static void main(String[] args) throws SocketException,UnknownHostException{ //最后根据代码补充只抛出了IOException这一个异常,说明上面两个异常都是IOException的子类 public static void main(String[] args) throws IOException{ //DatagramSocket类用来发送和接收数据报包的套接字 DatagramSocket ds=new DatagramSocket(); //DatagramPacket是数据报包类 byte[] bys="hello,udp".getBytes(); int len=bys.length; InetAddress ad=InetAddress.getByName("192.168.12.92"); int port=10086;
//打包 DatagramPacket dp=new DatagramPacket(bys,len,ad,port); ds.send(dp); ds.close(); } } //创建接收端的Socket对象 //创建数据包(接收容器) //调用Socket对象的发送方法接收数据 //解析数据,并显示在控制台 //释放资源 public class ReceiveDemo{ public static void main(String[] args) throws IOException{ DatagramSocket ds=new DatagramSocket(10086); byte[] bys=new byte[1024]; int len=bys.length; DatagramPacket dp=new DatagramPacket(bys,len); //阻塞式方法 ds.receive(dp); InetAddress ad=dp.getAddress(); String ip=ad.getHostAddress(); byte[] bys2=dp.getData(); int len=dp.getLength(); String s=new String(bys2,0,len); System.out.println(s); ds.close(); } }
将上述代码优化
//多次启动接受端,就会报端口已占用的错误 BindException public class ReceiveDemo{ public static void main(String[] args) throws IOException{ DatagramSocket ds=new DatagramSocket(10086); byte[] bys=new byte[1024]; DatagramPacket dp=new DatagramPacket(bys,bys.length); ds.receive(dp); String ip=dp.getAddress().getHostAddress(); String s=new String(dp.getData(),0,dp.getLength()); System.out.println(s); ds.close(); } } public class SendDemo{ public static void main(String[] args) throws IOException{ DatagramSocket ds=new DatagramSocket(); byte[] bys="hello,udp".getBytes(); DatagramPacket dp=new DatagramPacket(bys,bys.length,InetAddress.getByName("192.168.12.23"),10086); ds.send(dp); ds.close(); } }
发送键盘录入的数据 需要自己控制结束
public class ReceiveDemo{ public static void main(String[] args) throws IOException{ DatagramSocket ds=new DatagramSocket(10086); while(true){ byte[] bys=new byte[1024]; DatagramPacket dp=new DatagramPacket(bys,bys.length); ds.receive(dp); String ip=dp.getAddress().getHostAddress(); String s=new String(dp.getData(),0,dp.getLength()); System.out.println(s); } //接收端一直开着等待接收数据,不会关,其实类似服务器必须什么时候都得开着的道理一样 //ds.close(); } } //数据来自键盘录入 public class SendDemo{ public static void main(String[] args) throws IOException{ DatagramSocket ds=new DatagramSocket(); //封装键盘录入数据 System.in是字节流,所以先通过转换流将其转换成字符流然后再将其封装 BufferedReader br=new BufferedReader(new InputStreamReader(System.in)); String line=null; while((line=br.readline())!=null){ if("886".equals(line)){ break; } byte[] bys="hello,udp".getBytes(); //如果要让所有人收到数据,需要将IP改为广播地址192.168.12.255 DatagramPacket dp=new DatagramPacket(bys,bys.length,InetAddress.getByName("192.168.12.23"),10086);
ds.send();
} ds.close(); } }
多线程实现聊天程序从而实现在一个窗口中发送和接收数据
public class ChatRoom{ public static void main(String[] args) throws IOException{ //发送的Socket对象 DatagramSocket dsSend=new DatagramSocket(); //接收的Socket对象 DatagramSocket dsReceive=new DatagramSocket(12306); SendThread st=new SendThread(dsSend); ReceiveThread rt=new ReceiveThread(dsReceive); } } public class SendThread implements Runnable{ private DatagramSocket ds; public SendThread(DatagramSocket ds){ this.ds=ds; } public void run(){ try{ BufferedReader br=new BufferedReader(new InputStreamReader(System.in)); String line=null; while((line=br.readline())!=null){ if("886".equals(line)){ break; } byte[] bys="hello,udp".getBytes(); //如果要让所有人收到数据,需要将IP改为广播地址192.168.12.255 DatagramPacket dp=new DatagramPacket(bys,bys.length,InetAddress.getByName("192.168.12.23"),12306); ds.send(dp); } ds.close(); }catch(IOException e){ e.printStackTrace(); } } } public class ReceiveThread implements Runnable{ private DatagramSocket ds; public ReceiveDemo(DatagramSocket ds){ this.ds=ds; } public void run(){ try{ while(true){ byte[] bys=new byte[1024]; DatagramPacket dp=new DatagramPacket(bys,bys.length); ds.receive(dp); String ip=dp.getAddress().getHostAddress(); String s=new String(dp.getData(),0,dp.getLength()); System.out.println(s); } }catch(IOException e){ e.printStackTrace(); } } }
TCP发送和接收数据
//TCP发送数据 //1 创建发送端Socket对象 这一步如果成功,就说明连接已经建立成功了 //2 获取输出流,写数据 //3 释放资源 //ConnectException:Connection refused:connect //TCP协议一定要先开服务器,否则就会报这个错!!因为TCP要保证数据能收到 //而UDP是不可靠传输,所以测试的时候要先开客户端没问题 public class ClientDemo{ public static void main(String[] args){ //Socket是客户端套接字类 //Socket s=new Socket(InetAddress.getByName("192.168.12.32"),8888); Socket s=new Socket("192.168.12.23",8888); OutputStream os=s.getOutputStream(); os.write("hello,tcp".getBytes()); s.close(); } } public class ServerDemo{ public static void main(String[] args) throws IOException{ ServerSocket ss=new ServerSocket(8888); //监听客户端连接,返回对应的Socket的对象,该方法在连接传入之前一直阻塞 Socket s=ss.accept(); InputStream is=s.getInputStream(); byte[] bys=new byte[1024]; int len=is.read(bys);//阻塞式方法 String str=new String(bys,0,len); String ip=s.getInetAddress().getHostAddress(); System.out.println(ip+":"+str); System.out.println(str); s.close(); //ss.close(); 服务器不应该关闭 所以这句不应该加 } }
服务器给客户端反馈的案例
public class ServerDemo{ public static void main(String[] args){ ServerSocket ss=new ServerSocket(9999); Socket s=ss.accept(); InputStream is=s.getInputStream(); byte[] bys=new byte[1024]; int len=is.read(bys); String server=new String(bys,0,len); System.out.println("server:"+server); OutputStream os=s.getOutputStream(); os.write("数据已收到".getBytes()); s.close(); } } public class ClientDemo{ public static void main(String[] args){ Socket s=new Socket("192.168.12.92",9999); OutputStream os=s.getOutputStream(); os.write("aaaaaaaaaa".getBytes()); InputStream is=s.getInputStream(); byte[] bys=new byte[1024]; int len=is.read(bys); String client=new String(bys,0,len); System.out.println("client:"+client); s.close(); } }