网络编程
网络编程三要素:
A:IP地址
B:端口
C:协议
IP地址:
网络中计算机的唯一标识。
计算机只能识别二进制的数据,所以我们的IP地址应该是一个二进制的数据。
但是呢,我们配置的IP地址确不是二进制的,为什么呢?
IP:192.168.1.100
换算:11000000 10101000 00000001 01100100
假如真是:11000000 10101000 00000001 01100100的话。
我们如果每次再上课的时候要配置该IP地址,记忆起来就比较的麻烦。
所以,为了方便表示IP地址,我们就把IP地址的每一个字节上的数据换算成十进制,然后用.分开来表示:
"点分十进制"
IP地址的组成:网络号段+主机号段
A类:第一号段为网络号段+后三段的主机号段
一个网络号:256*256*256 = 16777216
B类:前二号段为网络号段+后二段的主机号段
一个网络号:256*256 = 65536
C类:前三号段为网络号段+后一段的主机号段
一个网络号:256
IP地址的分类:
A类 1.0.0.1---127.255.255.254 (1)10.X.X.X是私有地址(私有地址就是在互联网上不使用,而被用在局域网络中的地址) (2)127.X.X.X是保留地址,用做循环测试用的。
B类 128.0.0.1---191.255.255.254 172.16.0.0---172.31.255.255是私有地址。169.254.X.X是保留地址。
C类 192.0.0.1---223.255.255.254 192.168.X.X是私有地址
D类 224.0.0.1---239.255.255.254
E类 240.0.0.1---247.255.255.254
两个DOS命令:
ipconfig 查看本机ip地址
ping 后面跟ip地址。测试本机与指定的ip地址间的通信是否有问题
特殊的IP地址:
127.0.0.1 回环地址(表示本机)
x.x.x.255 广播地址
x.x.x.0 网络地址
端口号:
正在运行的程序的标识。
有效端口:0~65535,其中0~1024系统使用或保留端口。
协议:
通信的规则
UDP:
把数据打包
数据有限制
不建立连接
速度快
不可靠
TCP:
建立连接通道
数据无限制
速度慢
可靠
举例:
UDP:发短信
TCP:打电话
* 如果一个类没有构造方法:
* A:成员全部是静态的(Math,Arrays,Collections)
* B:单例设计模式(Runtime)
* C:类中有静态方法返回该类的对象(InetAddress)
* class Demo {
* private Demo(){}
*
* public static Demo getXxx() {
* return new Demo();
* }
* }
*
* 看InetAddress的成员方法:
* public static InetAddress getByName(String host):根据主机名或者IP地址的字符串表示得到IP地址对象
1 // public static InetAddress getByName(String host) 2 // InetAddress address = InetAddress.getByName("liuyi"); 3 // InetAddress address = InetAddress.getByName("192.168.12.92"); 4 InetAddress address = InetAddress.getByName("192.168.12.63"); 5 6 // 获取两个东西:主机名,IP地址 7 // public String getHostName() 8 String name = address.getHostName(); 9 // public String getHostAddress() 10 String ip = address.getHostAddress(); 11 System.out.println(name + "---" + ip);
Socket通信原理图解:
* UDP协议发送数据:
* A:创建发送端Socket对象
* B:创建数据,并把数据打包
* C:调用Socket对象的发送方法发送数据包
* D:释放资源
1 // 创建发送端的Socket对象 2 DatagramSocket ds = new DatagramSocket(); 3 4 // 创建数据并打包 5 byte[] bys = "helloworld".getBytes(); 6 DatagramPacket dp = new DatagramPacket(bys, bys.length, 7 InetAddress.getByName("192.168.12.92"), 12345); 8 9 // 发送数据 10 ds.send(dp); 11 12 // 释放资源 13 ds.close();
* UDP协议接收数据:
* A:创建接收端Socket对象
* B:创建一个数据包(接收容器)
* C:调用Socket对象的接收方法接收数据
* D:解析数据包,并显示在控制台
* E:释放资源
1 // 创建接收端的Socket对象 2 DatagramSocket ds = new DatagramSocket(12345); 3 4 // 创建一个包裹 5 byte[] bys = new byte[1024]; 6 DatagramPacket dp = new DatagramPacket(bys, bys.length); 7 8 // 接收数据 9 ds.receive(dp); 10 11 // 解析数据 12 String ip = dp.getAddress().getHostAddress(); 13 String s = new String(dp.getData(), 0, dp.getLength()); 14 System.out.println("from " + ip + " data is : " + s); 15 16 // 释放资源 17 ds.close();
* 多次启动接收端:
* java.net.BindException: Address already in use: Cannot bind
* 端口被占用。
通过多线程改进刚才的聊天程序,这样我就可以实现在一个窗口发送和接收数据了
1 public class ReceiveThread implements Runnable { 2 private DatagramSocket ds; 3 4 public ReceiveThread(DatagramSocket ds) { 5 this.ds = ds; 6 } 7 8 @Override 9 public void run() { 10 try { 11 while (true) { 12 // 创建一个包裹 13 byte[] bys = new byte[1024]; 14 DatagramPacket dp = new DatagramPacket(bys, bys.length); 15 16 // 接收数据 17 ds.receive(dp); 18 19 // 解析数据 20 String ip = dp.getAddress().getHostAddress(); 21 String s = new String(dp.getData(), 0, dp.getLength()); 22 System.out.println("from " + ip + " data is : " + s); 23 } 24 } catch (IOException e) { 25 e.printStackTrace(); 26 } 27 } 28 29 }
1 public class SendThread implements Runnable { 2 3 private DatagramSocket ds; 4 5 public SendThread(DatagramSocket ds) { 6 this.ds = ds; 7 } 8 9 @Override 10 public void run() { 11 try { 12 // 封装键盘录入数据 13 BufferedReader br = new BufferedReader(new InputStreamReader( 14 System.in)); 15 String line = null; 16 while ((line = br.readLine()) != null) { 17 if ("886".equals(line)) { 18 break; 19 } 20 21 // 创建数据并打包 22 byte[] bys = line.getBytes(); 23 // DatagramPacket dp = new DatagramPacket(bys, bys.length, 24 // InetAddress.getByName("192.168.12.92"), 12345); 25 DatagramPacket dp = new DatagramPacket(bys, bys.length, 26 InetAddress.getByName("192.168.12.255"), 12306); 27 28 // 发送数据 29 ds.send(dp); 30 } 31 32 // 释放资源 33 ds.close(); 34 } catch (IOException e) { 35 e.printStackTrace(); 36 } 37 } 38 39 }
1 public class ChatRoom { 2 public static void main(String[] args) throws IOException { 3 DatagramSocket dsSend = new DatagramSocket(); 4 DatagramSocket dsReceive = new DatagramSocket(12306); 5 6 SendThread st = new SendThread(dsSend); 7 ReceiveThread rt = new ReceiveThread(dsReceive); 8 9 Thread t1 = new Thread(st); 10 Thread t2 = new Thread(rt); 11 12 t1.start(); 13 t2.start(); 14 } 15 }
* TCP协议发送数据:
* A:创建发送端的Socket对象
* 这一步如果成功,就说明连接已经建立成功了。
* B:获取输出流,写数据
* C:释放资源
*
* 连接被拒绝。TCP协议一定要先开服务器。
* java.net.ConnectException: Connection refused: connect
1 // 创建接收端的Socket对象 2 // ServerSocket(int port) 3 ServerSocket ss = new ServerSocket(8888); 4 5 // 监听客户端连接。返回一个对应的Socket对象 6 // public Socket accept() 7 Socket s = ss.accept(); // 侦听并接受到此套接字的连接。此方法在连接传入之前一直阻塞。 8 9 // 获取输入流,读取数据显示在控制台 10 InputStream is = s.getInputStream(); 11 12 byte[] bys = new byte[1024]; 13 int len = is.read(bys); // 阻塞式方法 14 String str = new String(bys, 0, len); 15 16 String ip = s.getInetAddress().getHostAddress(); 17 18 System.out.println(ip + "---" + str); 19 20 // 释放资源 21 s.close(); 22 // ss.close(); //这个不应该关闭
1 public static void main(String[] args) throws IOException { 2 // 创建发送端的Socket对象 3 // Socket(InetAddress address, int port) 4 // Socket(String host, int port) 5 // Socket s = new Socket(InetAddress.getByName("192.168.12.92"), 8888); 6 Socket s = new Socket("192.168.12.92", 8888); 7 8 // 获取输出流,写数据 9 // public OutputStream getOutputStream() 10 OutputStream os = s.getOutputStream(); 11 os.write("hello,tcp,我来了".getBytes()); 12 13 // 释放资源 14 s.close(); 15 }
* 客户端键盘录入,服务器输出到控制台
1 public class ServerDemo { 2 public static void main(String[] args) throws IOException { 3 // 创建服务器Socket对象 4 ServerSocket ss = new ServerSocket(22222); 5 6 // 监听客户端连接 7 Socket s = ss.accept(); 8 9 // 包装通道内容的流 10 BufferedReader br = new BufferedReader(new InputStreamReader( 11 s.getInputStream())); 12 String line = null; 13 while ((line = br.readLine()) != null) { 14 System.out.println(line); 15 } 16 17 // br.close(); 18 s.close(); 19 // ss.close(); 20 } 21 }
1 public class ClientDemo { 2 public static void main(String[] args) throws IOException { 3 // 创建客户端Socket对象 4 Socket s = new Socket("192.168.12.92", 22222); 5 6 // 键盘录入数据 7 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); 8 // 把通道内的流给包装一下 9 BufferedWriter bw = new BufferedWriter(new OutputStreamWriter( 10 s.getOutputStream())); 11 12 String line = null; 13 while ((line = br.readLine()) != null) { 14 // 键盘录入数据要自定义结束标记 15 if ("886".equals(line)) { 16 break; 17 } 18 bw.write(line); 19 bw.newLine(); 20 bw.flush(); 21 } 22 23 // 释放资源 24 // bw.close(); 25 // br.close(); 26 s.close(); 27 } 28 }
* 客户端键盘录入,服务器输出文本文件
1 public class ServerDemo { 2 public static void main(String[] args) throws IOException { 3 // 创建服务器Socket对象 4 ServerSocket ss = new ServerSocket(23456); 5 6 // 监听客户端连接 7 Socket s = ss.accept(); 8 9 // 封装通道内的数据 10 BufferedReader br = new BufferedReader(new InputStreamReader( 11 s.getInputStream())); 12 // 封装文本文件 13 BufferedWriter bw = new BufferedWriter(new FileWriter("a.txt")); 14 15 String line = null; 16 while ((line = br.readLine()) != null) { 17 bw.write(line); 18 bw.newLine(); 19 bw.flush(); 20 } 21 22 bw.close(); 23 // br.close(); 24 s.close(); 25 // ss.close(); 26 } 27 }
1 public class ClientDemo { 2 public static void main(String[] args) throws IOException { 3 // 创建客户端Socket对象 4 Socket s = new Socket("192.168.12.92", 23456); 5 6 // 封装键盘录入 7 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); 8 // 封装通道内的数据 9 BufferedWriter bw = new BufferedWriter(new OutputStreamWriter( 10 s.getOutputStream())); 11 12 String line = null; 13 while ((line = br.readLine()) != null) { 14 if ("over".equals(line)) { 15 break; 16 } 17 18 bw.write(line); 19 bw.newLine(); 20 bw.flush(); 21 } 22 23 // bw.close(); 24 // br.close(); 25 s.close(); 26 } 27 }
* 客户端文本文件,服务器输出到控制台
1 public class ServerDemo { 2 public static void main(String[] args) throws IOException { 3 // 创建服务器Socket对象 4 ServerSocket ss = new ServerSocket(34567); 5 6 // 监听客户端连接 7 Socket s = ss.accept(); 8 9 // 封装通道内的流 10 BufferedReader br = new BufferedReader(new InputStreamReader( 11 s.getInputStream())); 12 13 String line = null; 14 while ((line = br.readLine()) != null) { 15 System.out.println(line); 16 } 17 18 19 s.close(); 20 } 21 }
1 public class ClientDemo { 2 public static void main(String[] args) throws IOException { 3 // 创建Socket对象 4 Socket s = new Socket("192.168.12.92", 34567); 5 6 // 封装文本文件 7 BufferedReader br = new BufferedReader(new FileReader( 8 "InetAddressDemo.java")); 9 // 封装通道内的流 10 BufferedWriter bw = new BufferedWriter(new OutputStreamWriter( 11 s.getOutputStream())); 12 13 String line = null; 14 while ((line = br.readLine()) != null) { 15 bw.write(line); 16 bw.newLine(); 17 bw.flush(); 18 } 19 20 br.close(); 21 s.close(); 22 } 23 }
* 按照我们正常的思路加入反馈信息,结果却没反应。为什么呢?
* 读取文本文件是可以以null作为结束信息的,但是呢,通道内是不能这样结束信息的。
* 所以,服务器根本就不知道你结束了。而你还想服务器给你反馈。所以,就相互等待了。
*
* 如何解决呢?
* A:在多写一条数据,告诉服务器,读取到这条数据说明我就结束,你也结束吧。
* 这样做可以解决问题,但是不好。
* B:Socket对象提供了一种解决方案
* public void shutdownOutput()
1 public class UploadServer { 2 public static void main(String[] args) throws IOException { 3 // 创建服务器端的Socket对象 4 ServerSocket ss = new ServerSocket(11111); 5 6 // 监听客户端连接 7 Socket s = ss.accept();// 阻塞 8 9 // 封装通道内的流 10 BufferedReader br = new BufferedReader(new InputStreamReader( 11 s.getInputStream())); 12 // 封装文本文件 13 BufferedWriter bw = new BufferedWriter(new FileWriter("Copy.java")); 14 15 String line = null; 16 while ((line = br.readLine()) != null) { // 阻塞 17 // if("over".equals(line)){ 18 // break; 19 // } 20 bw.write(line); 21 bw.newLine(); 22 bw.flush(); 23 } 24 25 // 给出反馈 26 BufferedWriter bwServer = new BufferedWriter(new OutputStreamWriter( 27 s.getOutputStream())); 28 bwServer.write("文件上传成功"); 29 bwServer.newLine(); 30 bwServer.flush(); 31 32 // 释放资源 33 bw.close(); 34 s.close(); 35 } 36 }
1 public class UploadClient { 2 public static void main(String[] args) throws IOException { 3 // 创建客户端Socket对象 4 Socket s = new Socket("192.168.12.92", 11111); 5 6 // 封装文本文件 7 BufferedReader br = new BufferedReader(new FileReader( 8 "InetAddressDemo.java")); 9 // 封装通道内流 10 BufferedWriter bw = new BufferedWriter(new OutputStreamWriter( 11 s.getOutputStream())); 12 13 String line = null; 14 while ((line = br.readLine()) != null) { // 阻塞 15 bw.write(line); 16 bw.newLine(); 17 bw.flush(); 18 } 19 20 //自定义一个结束标记 21 // bw.write("over"); 22 // bw.newLine(); 23 // bw.flush(); 24 25 //Socket提供了一个终止,它会通知服务器你别等了,我没有数据过来了 26 s.shutdownOutput(); 27 28 // 接收反馈 29 BufferedReader brClient = new BufferedReader(new InputStreamReader( 30 s.getInputStream())); 31 String client = brClient.readLine(); // 阻塞 32 System.out.println(client); 33 34 // 释放资源 35 br.close(); 36 s.close(); 37 } 38 }
上传图片:
1 public class UploadServer { 2 public static void main(String[] args) throws IOException { 3 // 创建服务器Socket对象 4 ServerSocket ss = new ServerSocket(19191); 5 6 // 监听客户端连接 7 Socket s = ss.accept(); 8 9 // 封装通道内流 10 BufferedInputStream bis = new BufferedInputStream(s.getInputStream()); 11 // 封装图片文件 12 BufferedOutputStream bos = new BufferedOutputStream( 13 new FileOutputStream("mn.jpg")); 14 15 byte[] bys = new byte[1024]; 16 int len = 0; 17 while ((len = bis.read(bys)) != -1) { 18 bos.write(bys, 0, len); 19 bos.flush(); 20 } 21 22 // 给一个反馈 23 OutputStream os = s.getOutputStream(); 24 os.write("图片上传成功".getBytes()); 25 26 bos.close(); 27 s.close(); 28 } 29 }
1 public class UploadClient { 2 public static void main(String[] args) throws IOException { 3 // 创建客户端Socket对象 4 Socket s = new Socket("192.168.12.92", 19191); 5 6 // 封装图片文件 7 BufferedInputStream bis = new BufferedInputStream(new FileInputStream( 8 "林青霞.jpg")); 9 // 封装通道内的流 10 BufferedOutputStream bos = new BufferedOutputStream(s.getOutputStream()); 11 12 byte[] bys = new byte[1024]; 13 int len = 0; 14 while ((len = bis.read(bys)) != -1) { 15 bos.write(bys, 0, len); 16 bos.flush(); 17 } 18 19 s.shutdownOutput(); 20 21 // 读取反馈 22 InputStream is = s.getInputStream(); 23 byte[] bys2 = new byte[1024]; 24 int len2 = is.read(bys2); 25 String client = new String(bys2, 0, len2); 26 System.out.println(client); 27 28 // 释放资源 29 bis.close(); 30 s.close(); 31 } 32 }
文件上传多线程版:
1 public class UserThread implements Runnable { 2 private Socket s; 3 4 public UserThread(Socket s) { 5 this.s = s; 6 } 7 8 @Override 9 public void run() { 10 try { 11 // 封装通道内的流 12 BufferedReader br = new BufferedReader(new InputStreamReader( 13 s.getInputStream())); 14 // 封装文本文件 15 // BufferedWriter bw = new BufferedWriter(new 16 // FileWriter("Copy.java")); 17 18 // 为了防止名称冲突 19 String newName = System.currentTimeMillis() + ".java"; 20 BufferedWriter bw = new BufferedWriter(new FileWriter(newName)); 21 22 String line = null; 23 while ((line = br.readLine()) != null) { // 阻塞 24 bw.write(line); 25 bw.newLine(); 26 bw.flush(); 27 } 28 29 // 给出反馈 30 BufferedWriter bwServer = new BufferedWriter( 31 new OutputStreamWriter(s.getOutputStream())); 32 bwServer.write("文件上传成功"); 33 bwServer.newLine(); 34 bwServer.flush(); 35 36 // 释放资源 37 bw.close(); 38 s.close(); 39 } catch (IOException e) { 40 e.printStackTrace(); 41 } 42 } 43 44 }
1 public class UploadServer { 2 public static void main(String[] args) throws IOException { 3 // 创建服务器Socket对象 4 ServerSocket ss = new ServerSocket(11111); 5 6 while (true) { 7 Socket s = ss.accept(); 8 new Thread(new UserThread(s)).start(); 9 } 10 } 11 }
1 public class UploadClient { 2 public static void main(String[] args) throws IOException { 3 // 创建客户端Socket对象 4 Socket s = new Socket("192.168.12.92", 11111); 5 6 // 封装文本文件 7 // BufferedReader br = new BufferedReader(new FileReader( 8 // "InetAddressDemo.java")); 9 BufferedReader br = new BufferedReader(new FileReader( 10 "ReceiveDemo.java")); 11 // 封装通道内流 12 BufferedWriter bw = new BufferedWriter(new OutputStreamWriter( 13 s.getOutputStream())); 14 15 String line = null; 16 while ((line = br.readLine()) != null) { // 阻塞 17 bw.write(line); 18 bw.newLine(); 19 bw.flush(); 20 } 21 22 // Socket提供了一个终止,它会通知服务器你别等了,我没有数据过来了 23 s.shutdownOutput(); 24 25 // 接收反馈 26 BufferedReader brClient = new BufferedReader(new InputStreamReader( 27 s.getInputStream())); 28 String client = brClient.readLine(); // 阻塞 29 System.out.println(client); 30 31 // 释放资源 32 br.close(); 33 s.close(); 34 } 35 }