一、 网络编程的常识

目前主流的网络通讯软件有:微信、QQ、飞信、阿里旺旺、陌陌、探探、…

二、 七层网络模型

OSI(Open System Interconnect),即开放式系统互联,是ISO(国际标准化组织)组织在1985
年研究的网络互连模型。
OSI七层模型和TCP/IP五层模型的划分如下:
01 网络模型.png

  • 当发送数据时,需要对发送的内容按照上述七层模型进行层层加包后发送出去。
  • 当接收数据时,需要对接收的内容按照上述七层模型相反的次序层层拆包并显示出来。

image.png
好比买一个小米手机,下单后,先是层层包装;
寄送到后,就开始拆包装,拿到手机。

加包的过程不需要程序员去考虑,直接使用API就好了。

三、相关的协议(笔试)

(1)协议的概念

计算机在网络中实现通信就必须有一些约定或者规则,这种约定和规则就叫做通信协议,通信协议
可以对速率、传输代码、代码结构、传输控制步骤、出错控制等制定统一的标准。

(2)TCP协议

  • 传输控制协议(Transmission Control Protocol),是一种面向连接的协议,类似于打电话。
  • 建立连接 => 进行通信 => 断开连接
  • 在传输前采用”三次握手”方式。

02 TCP三次握手.png
{
第一次握手,发送连接请求,“你好,我是林一”
第二次握手,服务器表示收到请求,“好的,我是服务器”
第三次握手,客户端告诉服务器,“好的,我知道你知道我是林一了”
}

  1. 为什么要三次握⼿
  2. 三次握⼿的⽬的是建⽴可靠的通信信道,说到通讯,简单来说就是数据的发送与接收,⽽三次握
  3. ⼿最主要的⽬的就是双⽅确认⾃⼰与对⽅的发送与接收是正常的。
  4. 第⼀次握⼿:Client 什么都不能确认;Server 确认了对⽅发送正常,⾃⼰接收正常
  5. 第⼆次握⼿:Client 确认了:⾃⼰发送、接收正常,对⽅发送、接收正常;Server 确认了:对⽅
  6. 发送正常,⾃⼰接收正常
  7. 第三次握⼿:Client 确认了:⾃⼰发送、接收正常,对⽅发送、接收正常;Server 确认了:⾃⼰
  8. 发送、接收正常,对⽅发送、接收正常
  9. 所以三次握⼿就能确认双发收发功能都正常,缺⼀不可。
  • 在通信的整个过程中全程保持连接,形成数据传输通道。
  • 保证了数据传输的可靠性和有序性。
  • 是一种全双工的字节流通信方式,可以进行大数据量的传输。 (半双工,只有一方能说,全双工,两边都能收)
  • 传输完毕后需要释放已建立的连接,发送数据的效率比较低。 (没听清还得再说一遍)

四次挥手:
03 TCP四次挥手.png
{
第一次挥手:客户端请求分手,“我们分手吧,你可真懒”
第二次挥手:服务器端说,“你居然说我懒,那我收拾东西走了”
第三次挥手:服务器端说,“我收拾好了, 我走了”
第四次挥手:客户端,“走吧,拜拜”
}

  1. 为什么要四次挥⼿
  2. 任何⼀⽅都可以在数据传送结束后发出连接释放的通知,待对⽅确认后进⼊半关闭状态。当另⼀
  3. ⽅也没有数据再发送的时候,则发出连接释放通知,对⽅确认后就完全关闭了TCP连接。
  4. 举个例⼦:
  5. A 和 B 打电话,通话即将结束后,A 说“我没啥要说的了”,B回答“我知道了”,但是 B
  6. 可能还会有要说的话,A 不能要求 B 跟着⾃⼰的节奏结束通话,于是 B 可能⼜巴拉巴拉说了⼀
  7. 通,最后 B 说“我说完了”,A 回答“知道了”,这样通话才算结束。
  8. 上⾯讲的⽐᫾概括,推荐⼀篇讲的⽐᫾细致的⽂
  9. 章:https://blog.csdn.net/qzcsu/article/details/72861891

(3)UDP协议

  • 用户数据报协议(User Datagram Protocol),是一种非面向连接的协议,类似于写信。
  • 在通信的整个过程中不需要保持连接,其实是不需要建立连接。
  • 不保证数据传输的可靠性和有序性。
  • 是一种全双工的数据报通信方式,每个数据报的大小限制在64K内。
  • 发送数据完毕后无需释放资源,开销小,发送数据的效率比较高,速度快。(优势)

(4)TCP 和 UDP的区别

image.png
UDP 在传送数据之前不需要先建⽴连接,远地主机在收到 UDP 报⽂后,不需要给出任何确认。
虽然 UDP 不提供可靠交付,但在某些情况下 UDP 确是⼀种最有效的⼯作⽅式(⼀般⽤于即时通
信),⽐如: QQ 语⾳、 QQ 视频 、直播等等

TCP 提供⾯向连接的服务。在传送数据之前必须先建⽴连接,数据传送结束后要释放连接。
TCP 不提供⼴播或多播服务。由于 TCP 要提供可靠的,⾯向连接的传输服务(TCP的可靠体现
在TCP在传递数据之前,会有三次握⼿来建⽴连接,⽽且在数据传递时,有确认、窗⼝、重传、
拥塞控制机制,在数据传完后,还会断开连接⽤来节约系统资源),这⼀难以避免增加了许多开
销,如确认,流量控制,计时器以及连接管理等。这不仅使协议数据单元的⾸部增⼤很多,还要
占⽤许多处理机资源。TCP ⼀般⽤于⽂件传输、发送和接收邮件、远程登录等场景。

四、IP地址(重点)

  • 192.168.1.1 - 是绝大多数路由器的登录地址,主要配置用户名和密码以及Mac过滤。 (Mac地址,硬件网卡的地址)
  • IP地址是互联网中的唯一地址标识,本质上是由32位二进制组成的整数,叫做IPv4,当然也有128
  • 位二进制组成的整数,叫做IPv6,目前主流的还是IPv4。
  • 日常生活中采用点分十进制表示法来进行IP地址的描述,将每个字节的二进制转化为一个十进制整
  • 数,不同的整数之间采用小数点隔开。
  • 如:
  • 0x01020304 => 1.2.3.4
  • 查看IP地址的方式:
  • Windows系统:在dos窗口中使用ipconfig或ipconfig/all命令即可
  • Unix/linux系统:在终端窗口中使用ifconfig或/sbin/ifconfig命令即可
  • 特殊的地址
  • 本地回环地址(hostAddress):127.0.0.1 主机名(hostName):localhost

五、端口(重点)

  • IP地址 - 可以定位到具体某一台设备。
  • 端口号 - 可以定位到该设备中具体某一个进程。
  • 端口号本质上是16位二进制组成的整数,表示范围是:0 ~ 65535,其中0 ~ 1024之间的端口号通
  • 常被系统占用,建议编程从1025开始使用。
  • 特殊的端口:
  • HTTP:80 FTP:21 Oracle:1521 MySQL:3306 Tomcat:8080
  • 网络编程需要提供:IP地址 + 端口号,组合在一起叫做网络套接字:Socket。

六、基于TCP的编程模型

(1) C/S架构的简介

在C/S模式下客户向服务器发出服务请求,服务器接收请求后提供服务。
例如:在一个酒店中,顾客找服务员点菜,服务员把点菜单通知厨师,厨师按点菜单做好菜后让服
务员端给客户,这就是一种C/S工作方式。如果把酒店看作一个系统,服务员就是客户端,厨师就
是服务器。这种系统分工和协同工作的方式就是C/S的工作方式。
客户端部分:为每个用户所专有的,负责执行前台功能。
服务器部分:由多个用户共享的信息与功能,招待后台服务。
image.png
04 基于tcp协议的编程模型.png

(2) 编程模型

  • 服务器:

(1)创建ServerSocket类型的对象并提供端口号;
(2)等待客户端的连接请求,调用accept()方法;
(3)使用输入输出流进行通信;
(4)关闭Socket;

  • 客户端:

(1)创建Socket类型的对象并提供服务器的IP地址和端口号;
(2)使用输入输出流进行通信;
(3)关闭Socket;

  1. package Ch12_Neuedu;
  2. import java.io.IOException;
  3. import java.net.ServerSocket;
  4. public class MyServer1 {
  5. public static void main(String[] args) throws IOException {
  6. //创建server端
  7. /*
  8. 技术实现
  9. 1,使用Serversocket
  10. 2,调用accept方法
  11. * */
  12. ServerSocket server=new ServerSocket(9977);
  13. System.out.println("等待连接...");
  14. server.accept();//接受客户端连接请求,看源码为监听,会造成阻塞
  15. System.out.println("阻塞解除!");
  16. }
  17. }
  1. package Ch12_Neuedu;
  2. import java.io.IOException;
  3. import java.net.Socket;
  4. public class MyClient1 {
  5. public static void main(String[] args) throws IOException {
  6. //连接指定的设备中的进程
  7. Socket client=new Socket("127.0.0.1",9977);
  8. System.out.println("客户端启动");
  9. client.close();
  10. }
  11. }

(3) 相关类和方法的解析

(1)ServerSocket类

  • java.net.ServerSocket类主要用于描述服务器套接字信息(大插排)

常见方法:
image.png

(2)Socket类

  • java.net.Socket类主要用于描述客户端套接字,是两台机器间通信的端点(小插排)。

常见方法:
image.png
注意事项:

  • 客户端 Socket 与服务器端 Socket 对应, 都包含输入和输出流。
  • 客户端的socket.getInputStream() 连接于服务器socket.getOutputStream()。
  • 客户端的socket.getOutputStream()连接于服务器socket.getInputStream()

(3)实现客户端向服务器传送数据

  1. package Ch12_Neuedu;
  2. import java.io.BufferedReader;
  3. import java.io.IOException;
  4. import java.io.InputStreamReader;
  5. import java.net.ServerSocket;
  6. import java.net.Socket;
  7. public class MyServer1 {
  8. public static void main(String[] args) throws IOException {
  9. //创建server端
  10. /*
  11. 技术实现
  12. 1,使用Serversocket
  13. 2,调用accept方法
  14. * */
  15. ServerSocket server=null;
  16. BufferedReader br=null;
  17. try {
  18. //1,创建ServerSocket的对象,并提供端口号
  19. server=new ServerSocket(9977);
  20. System.out.println("等待连接...");
  21. Socket s = server.accept();//接受客户端连接请求,看源码为监听,会造成阻塞
  22. System.out.println("连接成功!");
  23. //2,通过输入输出流实现通信
  24. //实现对客户端发来的内容进行接收并打印
  25. br=new BufferedReader(new InputStreamReader(s.getInputStream()));
  26. String s1=br.readLine();
  27. System.out.println(s1);
  28. } catch (IOException e) {
  29. e.printStackTrace();
  30. } finally {
  31. if (null!=server){
  32. server.close();
  33. }
  34. if (null!=br){
  35. br.close();
  36. }
  37. }
  38. }
  39. }
  1. package Ch12_Neuedu;
  2. import java.io.IOException;
  3. import java.io.PrintStream;
  4. import java.net.Socket;
  5. public class MyClient1 {
  6. public static void main(String[] args) throws IOException {
  7. Socket client=null;
  8. PrintStream pw=null;
  9. try {
  10. //1,连接指定设备中的指定的进程
  11. client=new Socket("127.0.0.1",9977);
  12. System.out.println("客户端启动");
  13. //2,实现输入输出流间的通信,向服务器端发送数据,发送字符串“hello”
  14. Thread.sleep(10);
  15. pw=new PrintStream(client.getOutputStream()); //创建对象
  16. pw.println("hello");
  17. System.out.println("客户端发送内容成功!");
  18. } catch (IOException e) {
  19. e.printStackTrace();
  20. } catch (InterruptedException e) {
  21. e.printStackTrace();
  22. } finally {
  23. if (null!=client){
  24. client.close();
  25. }
  26. if (null!=pw){
  27. pw.close();
  28. }
  29. }
  30. }
  31. }

(4)服务器端回发数据

  1. package Ch12_Neuedu;
  2. import java.io.*;
  3. import java.net.ServerSocket;
  4. import java.net.Socket;
  5. public class MyServer1 {
  6. public static void main(String[] args) throws IOException {
  7. //创建server端
  8. /*
  9. 技术实现
  10. 1,使用Serversocket
  11. 2,调用accept方法
  12. * */
  13. ServerSocket server=null;
  14. BufferedReader br=null;
  15. PrintStream pw=null;
  16. try {
  17. //1,创建ServerSocket的对象,并提供端口号
  18. server=new ServerSocket(9977);
  19. System.out.println("等待连接...");
  20. Socket s = server.accept();//接受客户端连接请求,看源码为监听,会造成阻塞
  21. System.out.println("连接成功!");
  22. //2,通过输入输出流实现通信
  23. //实现对客户端发来的内容进行接收并打印
  24. br=new BufferedReader(new InputStreamReader(s.getInputStream()));
  25. String s1=br.readLine();
  26. System.out.println(s1);
  27. //实现服务器向客户端回发数据
  28. pw=new PrintStream(s.getOutputStream());
  29. pw.println("I received!");
  30. System.out.println("服务器发送数据成功!");
  31. } catch (IOException e) {
  32. e.printStackTrace();
  33. } finally {
  34. if (null!=server){
  35. server.close();
  36. }
  37. if (null!=br){
  38. br.close();
  39. }
  40. if(null!=pw){
  41. pw.close();
  42. }
  43. }
  44. }
  45. }
  1. package Ch12_Neuedu;
  2. import java.io.BufferedReader;
  3. import java.io.IOException;
  4. import java.io.InputStreamReader;
  5. import java.io.PrintStream;
  6. import java.net.Socket;
  7. import java.util.Scanner;
  8. public class MyClient1 {
  9. public static void main(String[] args) throws IOException {
  10. Socket client=null;
  11. PrintStream pw=null;
  12. Scanner str=null;
  13. try {
  14. //1,连接指定设备中的指定的进程
  15. client=new Socket("127.0.0.1",9977);
  16. System.out.println("客户端启动");
  17. //2,实现输入输出流间的通信,向服务器端发送数据,发送字符串“hello”
  18. Thread.sleep(10);
  19. pw=new PrintStream(client.getOutputStream()); //创建对象
  20. str=new Scanner(System.in);
  21. String str1=str.next();
  22. pw.println(str1);
  23. System.out.println("客户端发送内容成功!");
  24. //客户端收到消息并读取
  25. BufferedReader br2=new BufferedReader(new InputStreamReader(client.getInputStream()));
  26. String str2= br2.readLine();
  27. System.out.println("服务器端回发的消息:"+str2);
  28. } catch (IOException e) {
  29. e.printStackTrace();
  30. } catch (InterruptedException e) {
  31. e.printStackTrace();
  32. } finally {
  33. if (null!=client){
  34. client.close();
  35. }
  36. if (null!=pw){
  37. pw.close();
  38. }
  39. }
  40. }
  41. }

(5)服务器端和客户端之间不间断通信

  1. package Ch12_Neuedu;
  2. import java.io.*;
  3. import java.net.ServerSocket;
  4. import java.net.Socket;
  5. import java.util.Scanner;
  6. public class MyServer1 {
  7. public static void main(String[] args) throws IOException {
  8. //创建server端
  9. /*
  10. 技术实现
  11. 1,使用Serversocket
  12. 2,调用accept方法
  13. * */
  14. ServerSocket server=null;
  15. BufferedReader br=null;
  16. PrintStream pw=null;
  17. Scanner sc=null;
  18. try {
  19. //1,创建ServerSocket的对象,并提供端口号
  20. server=new ServerSocket(9977);
  21. System.out.println("等待连接...");
  22. //如果客户端没发送数据过来,这里将造成阻塞
  23. Socket s = server.accept();//接受客户端连接请求,看源码为监听,会造成阻塞
  24. System.out.println("连接成功!");
  25. while (true){
  26. //2,通过输入输出流实现通信
  27. //实现对客户端发来的内容进行接收并打印
  28. br=new BufferedReader(new InputStreamReader(s.getInputStream()));
  29. String s1=br.readLine();
  30. //当客户端发来的内容为bye时,聊天结束
  31. System.out.println("客户端:"+s1);
  32. if (s1.equalsIgnoreCase("bye")){
  33. System.out.println("聊天结束!");
  34. break;
  35. }
  36. //实现服务器向客户端回发数据
  37. pw=new PrintStream(s.getOutputStream());
  38. sc=new Scanner(System.in);
  39. String str3=sc.next();
  40. pw.println(str3);
  41. System.out.println("服务器发送数据成功!");
  42. }
  43. } catch (IOException e) {
  44. e.printStackTrace();
  45. } finally {
  46. if (null!=server){
  47. server.close();
  48. }
  49. if (null!=br){
  50. br.close();
  51. }
  52. if(null!=pw){
  53. pw.close();
  54. }
  55. }
  56. }
  57. }
  1. package Ch12_Neuedu;
  2. import java.io.BufferedReader;
  3. import java.io.IOException;
  4. import java.io.InputStreamReader;
  5. import java.io.PrintStream;
  6. import java.net.Socket;
  7. import java.util.Scanner;
  8. public class MyClient1 {
  9. public static void main(String[] args) throws IOException {
  10. Socket client=null;
  11. PrintStream pw=null;
  12. Scanner str=null;
  13. BufferedReader br2=null;
  14. try {
  15. //1,连接指定设备中的指定的进程
  16. client=new Socket("127.0.0.1",9977);
  17. System.out.println("客户端启动");
  18. br2 = new BufferedReader(new InputStreamReader(client.getInputStream()));
  19. while (true){
  20. //2,实现输入输出流间的通信,向服务器端发送数据,发送字符串“hello”
  21. Thread.sleep(10);
  22. while (true) {
  23. pw = new PrintStream(client.getOutputStream()); //创建对象
  24. str = new Scanner(System.in);
  25. String str1 = str.next();
  26. pw.println(str1);
  27. System.out.println("客户端发送内容成功!");
  28. if (str1.equals("bye")) {
  29. System.out.println("聊天结束!!!");
  30. break;
  31. }
  32. //客户端收到消息并读取
  33. String str2 = br2.readLine();
  34. System.out.println("服务器端:" + str2);
  35. }
  36. }
  37. } catch (IOException e) {
  38. e.printStackTrace();
  39. } catch (InterruptedException e) {
  40. e.printStackTrace();
  41. } finally {
  42. if (null!=client){
  43. client.close();
  44. }
  45. if (null!=pw){
  46. pw.close();
  47. }
  48. }
  49. }
  50. }

不能实时互相通信,客户端没发送信息,服务器端仍然是阻塞的。

(6)服务器采用多线程的实现

  1. package com.lagou.task19;
  2. import java.io.BufferedReader;
  3. import java.io.IOException;
  4. import java.io.InputStreamReader;
  5. import java.io.PrintStream;
  6. import java.net.ServerSocket;
  7. import java.net.Socket;
  8. public class ServerStringTest {
  9. public static void main(String[] args) {
  10. ServerSocket ss = null;
  11. Socket s = null;
  12. try {
  13. // 1.创建ServerSocket类型的对象并提供端口号
  14. ss = new ServerSocket(8888);
  15. // 2.等待客户端的连接请求,调用accept方法
  16. while(true) {
  17. System.out.println("等待客户端的连接请求...");
  18. // 当没有客户端连接时,则服务器阻塞在accept方法的调用这里
  19. s = ss.accept();
  20. System.out.println("客户端" + s.getInetAddress() + "连接成功!");
  21. // 每当有一个客户端连接成功,则需要启动一个新的线程为之服务
  22. new ServerThread(s).start();
  23. }
  24. } catch (IOException e) {
  25. e.printStackTrace();
  26. } finally {
  27. // 4.关闭Socket并释放有关的资源
  28. if (null != ss) {
  29. try {
  30. ss.close();
  31. } catch (IOException e) {
  32. e.printStackTrace();
  33. }
  34. }
  35. }
  36. }
  37. }
  1. package com.lagou.task19;
  2. import java.io.BufferedReader;
  3. import java.io.IOException;
  4. import java.io.InputStreamReader;
  5. import java.io.PrintStream;
  6. import java.net.InetAddress;
  7. import java.net.Socket;
  8. public class ServerThread extends Thread {
  9. private Socket s;
  10. public ServerThread(Socket s) {
  11. this.s = s;
  12. }
  13. @Override
  14. public void run() {
  15. BufferedReader br = null;
  16. PrintStream ps = null;
  17. try {
  18. // 3.使用输入输出流进行通信
  19. br = new BufferedReader(new InputStreamReader(s.getInputStream()));
  20. ps = new PrintStream(s.getOutputStream());
  21. while(true) {
  22. // 实现对客户端发来字符串内容的接收并打印
  23. // 当没有数据发来时,下面的方法会形成阻塞
  24. String s1 = br.readLine();
  25. InetAddress inetAddress = s.getInetAddress();
  26. System.out.println("客户端" + inetAddress + "发来的字符串内容是:" + s1);
  27. // 当客户端发来的内容为"bye"时,则聊天结束
  28. if ("bye".equalsIgnoreCase(s1)) {
  29. System.out.println("客户端" + inetAddress + "已下线!");
  30. break;
  31. }
  32. // 实现服务器向客户端回发字符串内容"I received!"
  33. ps.println("I received!");
  34. System.out.println("服务器发送数据成功!");
  35. }
  36. } catch (IOException e) {
  37. e.printStackTrace();
  38. } finally {
  39. if (null != ps) {
  40. ps.close();
  41. }
  42. if (null != br) {
  43. try {
  44. br.close();
  45. } catch (IOException e) {
  46. e.printStackTrace();
  47. }
  48. }
  49. if (null != s) {
  50. try {
  51. s.close();
  52. } catch (IOException e) {
  53. e.printStackTrace();
  54. }
  55. }
  56. }
  57. }
  58. }
  1. package com.lagou.task19;
  2. import com.lagou.task10.StaticOuter;
  3. import java.io.BufferedReader;
  4. import java.io.IOException;
  5. import java.io.InputStreamReader;
  6. import java.io.PrintStream;
  7. import java.net.Socket;
  8. import java.util.Scanner;
  9. public class ClientStringTest {
  10. public static void main(String[] args) {
  11. Socket s = null;
  12. PrintStream ps = null;
  13. Scanner sc = null;
  14. BufferedReader br = null;
  15. try {
  16. // 1.创建Socket类型的对象并提供服务器的主机名和端口号
  17. s = new Socket("127.0.0.1", 8888);
  18. System.out.println("连接服务器成功!");
  19. // 2.使用输入输出流进行通信
  20. sc = new Scanner(System.in);
  21. ps = new PrintStream(s.getOutputStream());
  22. br = new BufferedReader(new InputStreamReader(s.getInputStream()));
  23. while(true) {
  24. //Thread.sleep(10000);
  25. // 实现客户端发送的内容由用户从键盘输入
  26. System.out.println("请输入要发送的数据内容:");
  27. String str1 = sc.next();
  28. // 实现客户端向服务器发送字符串内容"hello"
  29. //ps.println("hello");
  30. ps.println(str1);
  31. System.out.println("客户端发送数据内容成功!");
  32. // 当发送的数据内容为"bye"时,则聊天结束
  33. if ("bye".equalsIgnoreCase(str1)) {
  34. System.out.println("聊天结束!");
  35. break;
  36. }
  37. // 实现接收服务器发来的字符串内容并打印
  38. String str2 = br.readLine();
  39. System.out.println("服务器回发的消息是:" + str2);
  40. }
  41. } catch (IOException /*| InterruptedException*/ e) {
  42. e.printStackTrace();
  43. } finally {
  44. // 3.关闭Socket并释放有关的资源
  45. if (null != br) {
  46. try {
  47. br.close();
  48. } catch (IOException e) {
  49. e.printStackTrace();
  50. }
  51. }
  52. if (null != ps) {
  53. ps.close();
  54. }
  55. if (null != sc) {
  56. sc.close();
  57. }
  58. if (null != s) {
  59. try {
  60. s.close();
  61. } catch (IOException e) {
  62. e.printStackTrace();
  63. }
  64. }
  65. }
  66. }
  67. }

(7)案例

实现群聊功能:
服务器端

  1. package neuedu;
  2. import java.io.*;
  3. import java.net.ServerSocket;
  4. import java.net.Socket;
  5. import java.util.Collections;
  6. import java.util.LinkedList;
  7. import java.util.List;
  8. class ServerThread implements Runnable{
  9. private Socket socket;
  10. public ServerThread(Socket socket) {
  11. this.socket = socket;
  12. }
  13. @Override
  14. public void run() {
  15. BufferedReader bf=null;
  16. //收取数据
  17. while (true){
  18. try {
  19. //7,读取客户端发送过来的信息
  20. bf=new BufferedReader(new InputStreamReader(socket.getInputStream()));
  21. //8,存储在message变量
  22. String message=bf.readLine(); //阻塞,客户端发送过来,我马上读取到
  23. System.out.println("服务端测试:"+message); //9,服务端测试,测试服务器端是否读取到信息
  24. //10,怎么转发? 在集合里拿到套接字
  25. List<Socket> clients=MyServer4.clients;
  26. for (Socket clients2:clients) { //遍历集合中的套接字
  27. if (clients2!=this.socket) //加入判断,避免发回去给本人 所以当该套接字与本人套接字不一样时才进行输出操作
  28. { //转发到客户端,除了本人,if语句已经做了判断
  29. PrintWriter pw=new PrintWriter(new OutputStreamWriter(clients2.getOutputStream()),true);
  30. pw.println("socket说:"+message);
  31. }
  32. }
  33. } catch (IOException e) {
  34. e.printStackTrace();
  35. }
  36. }
  37. }
  38. }
  39. public class MyServer4 {
  40. //2,定义一个集合保存客户端的套接字数据
  41. public static List<Socket> clients=new LinkedList<>();
  42. public static void main(String[] args) throws IOException {
  43. //1,设置服务器端口号
  44. ServerSocket server =new ServerSocket(9200);
  45. //3,给集合加锁,防止数据进进出出,别人遍历的时候,不让添加
  46. Collections.synchronizedList(clients);
  47. while(true){
  48. Socket client=server.accept(); //4,接受套接字
  49. clients.add(client); //5,把新进群的客户端加入套接字集合
  50. //6,创建线程类处理
  51. //开辟一个线程跟这个客户端聊天,其实不是聊天,是收他发过来的数据,转发给其他人
  52. new Thread(new ServerThread(client)).start();
  53. }
  54. }
  55. }

客户端:

  1. package neuedu;
  2. import java.io.*;
  3. import java.net.Socket;
  4. class ClientThread implements Runnable{
  5. private Socket socket;
  6. public ClientThread(Socket socket) {
  7. this.socket = socket;
  8. }
  9. @Override
  10. public void run() {
  11. try {
  12. InputStream inputStream=socket.getInputStream();
  13. BufferedReader br=new BufferedReader(new InputStreamReader(inputStream));
  14. while (true){
  15. String s2=br.readLine();
  16. System.out.println("server"+s2);
  17. }
  18. /* //写数剧----控制台
  19. BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
  20. String str = bf.readLine();
  21. OutputStream outputStream=socket.getOutputStream();
  22. //输出到服务器端
  23. PrintWriter pw=new PrintWriter(new OutputStreamWriter(outputStream),true);
  24. pw.println(str);*/
  25. } catch (IOException e) {
  26. e.printStackTrace();
  27. }
  28. }
  29. }
  30. public class MyClient4 {
  31. public static void main(String[] args) {
  32. //通过套接字与服务器获取连接
  33. Socket socket= null;
  34. try {
  35. socket = new Socket("127.0.0.1",9200);
  36. new Thread(new ClientThread(socket)).start();
  37. OutputStream outputStream=socket.getOutputStream();
  38. while (true){
  39. BufferedReader bf=new BufferedReader(new InputStreamReader(System.in));
  40. String s1=bf.readLine();
  41. PrintWriter pw=new PrintWriter(new OutputStreamWriter(outputStream),true);
  42. pw.println(s1);}
  43. } catch (IOException e) {
  44. e.printStackTrace();
  45. }
  46. }
  47. }

java8新特性 Stream

七、基于UDP协议的编程模型

(1) 编程模型

接收方:
(1)创建DatagramSocket类型的对象并提供端口号;
(2)创建DatagramPacket类型的对象并提供缓冲区;
(3)通过Socket接收数据内容存放到Packet中,调用receive方法;
(4)关闭Socket;
发送方:
(1)创建DatagramSocket类型的对象;
(2)创建DatagramPacket类型的对象并提供接收方的通信地址;
(3)通过Socket将Packet中的数据内容发送出去,调用send方法;
(4)关闭Socket;

(2) 相关类和方法的解析

(1)DatagramSocket类

java.net.DatagramSocket类主要用于描述发送和接收数据报的套接字(邮局)。
换句话说,该类就是包裹投递服务的发送或接收点。
常用的方法如下:
image.png

(2)DatagramPacket类

java.net.DatagramPacket类主要用于描述数据报,数据报用来实现无连接包裹投递服务
常见方法:
image.png

(3)InetAddress类

java.net.InetAddress类主要用于描述互联网通信地址信息。
常见方法:
image.png

(4)案例(基于UDP实现发送和接受)

  1. //接收方
  2. package task19;
  3. import java.io.IOException;
  4. import java.net.DatagramPacket;
  5. import java.net.DatagramSocket;
  6. public class ReceiveTest {
  7. public static void main(String[] args) {
  8. DatagramSocket datagramSocket= null;
  9. try {
  10. //创建DatagramSocket对象并设置端口号
  11. datagramSocket = new DatagramSocket(8888);
  12. //创建DatagramPacket对象,并设置缓冲区
  13. byte [] brr=new byte[1024];
  14. DatagramPacket datagramPacket=new DatagramPacket(brr, brr.length);
  15. //接收对象
  16. datagramSocket.receive(datagramPacket);
  17. System.out.println("收到的数据是:"+new String(brr,0,datagramPacket.getLength())+"!");
  18. } catch (IOException e) {
  19. e.printStackTrace();
  20. } finally {
  21. if (null!=datagramSocket){
  22. //关闭Socket,释放资源
  23. datagramSocket.close();
  24. }
  25. }
  26. }
  27. }
  1. package task19;
  2. import java.io.IOException;
  3. import java.net.DatagramPacket;
  4. import java.net.DatagramSocket;
  5. import java.net.InetAddress;
  6. public class SendTest {
  7. public static void main(String[] args) {
  8. DatagramSocket datagramSocket= null;
  9. try {
  10. //1,创建 DatagramSocket类型的对象
  11. datagramSocket = new DatagramSocket();
  12. //2, 创建DatagramPacket 类型的对象,并提供接收方的地址和发送方的端口号
  13. String message="hello";
  14. byte [] barr=message.getBytes();
  15. DatagramPacket datagramPacket=new DatagramPacket(barr,barr.length, InetAddress.getLocalHost(),8888);
  16. //3,发送
  17. datagramSocket.send(datagramPacket);
  18. } catch (IOException e) {
  19. e.printStackTrace();
  20. } finally {
  21. if (null!=datagramSocket){
  22. //4,关闭并释放资源
  23. datagramSocket.close();
  24. }
  25. }
  26. }
  27. }

(5)回发

  1. package task19;
  2. import java.io.IOException;
  3. import java.net.DatagramPacket;
  4. import java.net.DatagramSocket;
  5. public class ReceiveTest {
  6. public static void main(String[] args) {
  7. DatagramSocket datagramSocket= null;
  8. try {
  9. //1创建DatagramSocket对象并设置端口号
  10. datagramSocket = new DatagramSocket(8888);
  11. //2创建DatagramPacket对象,并设置缓冲区
  12. byte [] brr=new byte[1024];
  13. DatagramPacket datagramPacket=new DatagramPacket(brr, brr.length);
  14. //3接收对象
  15. datagramSocket.receive(datagramPacket);
  16. System.out.println("收到的数据是:"+new String(brr,0,datagramPacket.getLength())+"!");
  17. //回发数据
  18. //2-1 设置缓冲区,并提供接收方的接收地址和端口号
  19. byte [] bye2="收到".getBytes();
  20. DatagramPacket dp=new DatagramPacket(bye2, bye2.length,datagramPacket.getAddress(),datagramPacket.getPort());
  21. //2-2发送
  22. datagramSocket.send(dp);
  23. System.out.println("回发成功!");
  24. } catch (IOException e) {
  25. e.printStackTrace();
  26. } finally {
  27. if (null!=datagramSocket){
  28. //4关闭Socket,释放资源
  29. datagramSocket.close();
  30. }
  31. }
  32. }
  33. }
  1. package task19;
  2. import java.io.IOException;
  3. import java.net.DatagramPacket;
  4. import java.net.DatagramSocket;
  5. import java.net.InetAddress;
  6. public class SendTest {
  7. public static void main(String[] args) {
  8. DatagramSocket datagramSocket= null;
  9. try {
  10. //1,创建 DatagramSocket类型的对象
  11. datagramSocket = new DatagramSocket();
  12. //2, 创建DatagramPacket 类型的对象,并提供接收方的地址和发送方的端口号
  13. String message="hello";
  14. byte [] barr=message.getBytes();
  15. DatagramPacket datagramPacket=new DatagramPacket(barr,barr.length, InetAddress.getLocalHost(),8888);
  16. //3,发送
  17. datagramSocket.send(datagramPacket);
  18. //接收回发的内容
  19. byte [] bytes2=new byte[1024];
  20. DatagramPacket datagramPacket1=new DatagramPacket(bytes2,bytes2.length);
  21. datagramSocket.receive(datagramPacket1);
  22. System.out.println("接收来自回发的数据成功!"+new String(bytes2,0, bytes2.length));
  23. } catch (IOException e) {
  24. e.printStackTrace();
  25. } finally {
  26. if (null!=datagramSocket){
  27. //4,关闭并释放资源
  28. datagramSocket.close();
  29. }
  30. }
  31. }
  32. }

八、URL类(熟悉)

(1) 基本概念

java.net.URL(Uniform Resource Identififier)类主要用于表示统一的资源定位器,也就是指向万
维网上“资源”的指针。这个资源可以是简单的文件或目录,也可以是对复杂对象的引用,例如对数
据库或搜索引擎的查询等。
通过URL可以访问万维网上的网络资源,最常见的就是www和ftp站点,浏览器通过解析给定的
URL可以在网络上查找相应的资源。
URL的基本结构如下:
<传输协议>://<主机名>:<端口号>/<资源地址>
常见方法:
image.png

  1. package com.lagou.task19;
  2. import java.io.BufferedReader;
  3. import java.io.IOException;
  4. import java.io.InputStream;
  5. import java.io.InputStreamReader;
  6. import java.net.HttpURLConnection;
  7. import java.net.MalformedURLException;
  8. import java.net.URL;
  9. import java.net.URLConnection;
  10. public class URLTest {
  11. public static void main(String[] args) {
  12. try {
  13. // 1.使用参数指定的字符串来构造对象
  14. URL url = new URL("https://www.lagou.com/");
  15. // 2.获取相关信息并打印出来---基本方法的使用
  16. System.out.println("获取到的协议名称是:" + url.getProtocol());
  17. System.out.println("获取到的主机名称是:" + url.getHost());
  18. System.out.println("获取到的端口号是:" + url.getPort());
  19. // 3.建立连接并读取相关信息打印出来
  20. HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
  21. InputStream inputStream = urlConnection.getInputStream();
  22. BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
  23. String str = null;
  24. while ((str = br.readLine()) != null) {
  25. System.out.println(str);
  26. }
  27. br.close();
  28. // 断开连接
  29. urlConnection.disconnect();
  30. } catch (MalformedURLException e) {
  31. e.printStackTrace();
  32. } catch (IOException e) {
  33. e.printStackTrace();
  34. }
  35. }
  36. }

(2) URLConnection类

(1)基本概念

java.net.URLConnection类是个抽象类,该类表示应用程序和URL之间的通信链接的所有类的超
类,主要实现类有支持HTTP特有功能的HttpURLConnection类。

(2)HttpURLConnection类的常用方法

image.png