• Socket网络编程,网络原理基本概念


    一,传输层的两个协议:

    UDP:User Datagram Protocol    用户报文协议

    TCP:Transmission Control Protocol     传输控制协议

    联系与区别

    相同点:都是传输层协议,都是进程与进程之间的通信

    不同点:

    UDP没有做任何处理,保持网络的原生态(不可靠)

    TCP对网络传输做了一些控制,是通信变得可靠(可靠)

    所以将

    UDP称为:不可靠,无连接,面向数据报文(字符流)的一种协议。

    TCP可靠的有连接的面向字节流的协议。

    二,网络层的重要协议

    网络层主要协议:IP协议 : Internet Protocol

    网络层的重要概念:IP地址

    传输层的重要概念:Port端口   0~65535 一个进程可以拥有很多端口,一个端口只能一个进程

    数据链路层的重要概念:MAC地址

    IP地址+端口 + 传输层协议  =》 五元组信息

    五元组:

    1,站在通信数据角度:源IP,源端口,目的IP,目的端口,传输层协议(UDP,TCP)

    2,站在通信双方:本地IP,本地端口,远端IP,远端端口,传输层协议(TCP,UDP)

    套接字(Socket):

    站在应用层角度,要实现进程之间的通信,需要使用传入层提供的服务,传输层向应用层提供Socket,用来接收应用层发来的数据,

    类比传输层有两家公司(TCP,UDP)应用层(用户)要向传输层沟通,只能通过传输层提供的Socket接口,实现沟通。

    Socket的构造方法:

    UDP协议相关的类

    DatagramSocket(报文套接字)

     

    DatagramSocket():未指定端口,系统会分配一个端口

    DatagramSocket(int port):绑定指定端口,方便客户端通信,存在端口号被占用的风险

    关闭Socket连接:

     发送数据报文:

     接收数据报文:

     一旦UDP在逻辑上通信成功,双方都可以发送和接收数据,双方地位平等(P2P模式)通信结束后,双方都应该调用close()方法,进行资源回收。

    数据包:DatagramPacket类

     构造方法

     接收方:在接收数据时,只需要提供数据的容器和长度(byte[] buf,int length)

    获取发送的数据包

    Byte[] getData();接收方获取接收到的数据的内容

    作为发送方:在发送数据时,需要提供发送的数据包,长度,偏移,目的地址/目的端口(byte[] buf + int offset + int lenght + (InternetAddress aimAdd  +   int port) -> 会被封装成SocketAddress对象);

    服务器获取客户端的地址和端口

     

     服务器与客户端的通信模式

    1,请求响应模式(客户端请求一次,服务器响应一次)

    2,订阅推送模式(客户端订阅一次请求,服务器在今后每次有此类请求的更新时,服务器就会主动推送给客户端)

    P2P模式(不区分客户端和服务器)

     客户端服务器模式:

    UDP演示网络编程

            UDP协议不区分客户端与服务器,因此双方都可以作为发送方和接收方,所以可以双方都采用DatagramSocket类创建对象,通过双方的端口号建立通信,将发送和接收的数据分别存在DatagramPacket对象中,在通过字符编码的形式,转换为正确的数据报。

    UDP服务器

    1. package 网络编程.udp;
    2. import 网络编程.Log;
    3. import java.io.IOException;
    4. import java.net.*;
    5. import java.util.HashMap;
    6. import java.util.Map;
    7. import java.util.Scanner;
    8. public class TranslateServer {
    9. //公开的IP和端口
    10. public final static int port = 9999;
    11. private static HashMap map = new HashMap<>();
    12. static {
    13. map.put("apple","苹果");
    14. map.put("book","书");
    15. map.put("bicycle","自行车");
    16. Log.println("字典初始化完成");
    17. }
    18. public static void main(String[] args) throws Exception {
    19. Log.println("准备创建UDP Socklet ,端口是:" + port);
    20. DatagramSocket socket = new DatagramSocket(port);//参加Socket关键字
    21. Log.println("UDP Socket 创建完成");
    22. //服务器接受请求,被动接收,所以是一直处在运行态
    23. while (true){
    24. //1,参加Socket
    25. //参加容器接收数据,1024缓冲的大小
    26. byte[] buf = new byte[1024];
    27. //参加获取请求的数据包
    28. DatagramPacket receivePacket = new DatagramPacket(buf,buf.length);
    29. //获取从客户端发来的请求,如果客户端没有发送请求,就会一直再次阻塞
    30. //2,获取请求的信息
    31. Log.println("准备接收请求,容器大小" + buf.length);
    32. socket.receive(receivePacket);
    33. Log.println("接受到请求");
    34. //获取对方的IP和port
    35. InetAddress address = receivePacket.getAddress();
    36. int port1 = receivePacket.getPort();
    37. Log.println("请求方的端口 : " + port1);
    38. Log.println("请求方的IP : " + address);
    39. //获取对方的IP+port
    40. SocketAddress socketAddress = receivePacket.getSocketAddress();
    41. //获取请求内容
    42. byte[] data = receivePacket.getData();
    43. //获取接收数据的大小
    44. int length = receivePacket.getLength();
    45. Log.println("接收到请求的长度 : " + length);
    46. //3,解析请求
    47. //字符集解码
    48. String request = new String(data,0,length,"UTF-8");
    49. String engWord = request;
    50. Log.println("接受到请求的数据为 : " + engWord);
    51. //4,执行业务
    52. String chiWord = tranlaste(engWord);
    53. Log.println("翻译后的结果为: " + chiWord);
    54. //5,封装响应
    55. String response = chiWord;
    56. byte[] sendBuf = response.getBytes("UTF-8");
    57. //发送数据,要将接收方的信息一并封装到包中socketAddress
    58. DatagramPacket responsePacket = new DatagramPacket(sendBuf,0,sendBuf.length,socketAddress);
    59. Log.println("接收方发送响应 ... ");
    60. //6,发送响应
    61. socket.send(responsePacket);
    62. Log.println("接收方发送响应成功,发送的数据为 : " + chiWord);
    63. //此时请求完成,等待下一次响应
    64. }
    65. //服务器关闭时,关闭所有的套接字资源
    66. // socket.close();
    67. }
    68. private static String tranlaste(String engWord) {
    69. String chiWord = map.getOrDefault(engWord,"查无此词");
    70. return chiWord;
    71. }
    72. }

    UDP客户端

    1. package 网络编程.udp;
    2. import 网络编程.Log;
    3. import java.io.UnsupportedEncodingException;
    4. import java.net.*;
    5. import java.util.Scanner;
    6. public class ClientVersion1 {
    7. public static final int port = 8888;
    8. public static void main(String[] args) throws Exception {
    9. //1,创建DatagramSocket
    10. Log.println("开始创建UDP Socket");
    11. DatagramSocket socket = new DatagramSocket(port);
    12. Log.println("UDP Socket创建完成");
    13. Scanner sc = new Scanner(System.in);
    14. System.out.print("输入要翻译的单词:");
    15. while (sc.hasNextLine()){
    16. //2,创建请求数据包
    17. String request = sc.nextLine();
    18. Log.println("请求的数据为" + request);
    19. byte[] buf = request.getBytes("UTF-8");
    20. Log.println("准备创建请求数据包");
    21. DatagramPacket requestPacket = new DatagramPacket(buf,0,buf.length, InetAddress.getLoopbackAddress(),TranslateServer.port);
    22. Log.println("请求数据包创建完成");
    23. //3,发送请求
    24. Log.println("发送请求");
    25. socket.send(requestPacket);
    26. Log.println("请求发送成功");
    27. //4,接收响应
    28. byte[] bytes = new byte[1024];
    29. DatagramPacket responsePacket = new DatagramPacket(bytes,bytes.length);
    30. Log.println("开始接收响应");
    31. socket.receive(responsePacket);
    32. byte[] data = responsePacket.getData();
    33. String res = new String(data,0,responsePacket.getLength(),"UTF-8");
    34. Log.println("请求结果为:" + res);
    35. Log.println("对方的IP为" + requestPacket.getAddress());
    36. Log.println("对方的端口为" + requestPacket.getPort());
    37. System.out.print("输入要翻译的单词:");
    38. }
    39. //5,关闭资源
    40. socket.close();
    41. Log.println("关闭Socket");
    42. }
    43. }

    Log日志打印

    1. package 网络编程;
    2. import java.text.DateFormat;
    3. import java.time.LocalDateTime;
    4. import java.time.ZoneId;
    5. import java.time.format.DateTimeFormatter;
    6. //日志类
    7. public class Log {
    8. public static void println(Object o){
    9. LocalDateTime localDateTime =LocalDateTime.now(ZoneId.of("Asia/Shanghai"));//该类的构造方法私有,通过静态方法获取时间戳
    10. DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");
    11. String time = format.format(localDateTime);
    12. //所以要打印的信息就是时间+对象信息
    13. System.out.println(time +" : "+ (o == null ? null : o.toString()));
    14. }
    15. public static void main(String[] args) {
    16. println(null);
    17. }
    18. }

    TCP协议相关类

    tcp有连接,需要先建立连接,才能进行通信

    ServerSocket类创建 TCPSocket服务器对象

    构造方法,socket的构造方法是给客户端使用的,因为服务区的socket是通过accept()获取的

     服务器的方法:

    服务器,返回建立连接的socket对象,服务器的socket是通过accept()方法获取的

    关闭连接

     

     一旦建立连接,就只区分发送方和接收方

    返回对方的连接地址

     返回套接字的输入输出流

    客户端的方法:

    长连接:连接建立以后,可以多次发送请求

    短链接:建立连接后只发送一个请求

    TCP网络编程

    因为TCP区分客户端与服务器,

    服务器需要参加SeverSocket对象来获取客户端的Socket连接对象,通过serverSocket.accept()获取Socket,在获取socket的输入输出流,通过输入输出流进行现场通信

    客户端参加链接则需要使用Socket类,将自己的IP和对方的端口号构造一个socket对象,来建立连接,发送请求使用输出流,接收响应使用输入流。

    TCP 长连接多线程服务器:

    1. package 网络编程.tcp;
    2. import 网络编程.Log;
    3. import java.io.InputStream;
    4. import java.io.OutputStream;
    5. import java.io.OutputStreamWriter;
    6. import java.io.PrintWriter;
    7. import java.net.ServerSocket;
    8. import java.net.Socket;
    9. import java.util.HashMap;
    10. import java.util.Scanner;
    11. import java.util.concurrent.LinkedBlockingQueue;
    12. import java.util.concurrent.ThreadPoolExecutor;
    13. import java.util.concurrent.TimeUnit;
    14. public class TCPService {
    15. public static final int port = 9999;
    16. private static HashMap map = new HashMap<>();
    17. static {
    18. map.put("apple","苹果");
    19. map.put("book","书");
    20. map.put("bicycle","自行车");
    21. Log.println("字典初始化完成");
    22. }
    23. private static class Task implements Runnable{
    24. private static Socket socket;
    25. public Task(Socket socket){
    26. this.socket = socket;
    27. }
    28. @Override
    29. public void run() {
    30. try {
    31. Log.println("准备获取请求方法信息");
    32. Log.println("请求方IP:" + socket.getInetAddress());//获取客户端的IP)
    33. Log.println("请求方端口:" + socket.getPort());//获取客户端的端口
    34. Log.println("请求方IP + port :" + socket.getRemoteSocketAddress());//获取客户端的IP+port
    35. //3,获取客户端的输入流
    36. InputStream inputStream = socket.getInputStream();
    37. Log.println("请求方的输入流:" + inputStream);
    38. Scanner sc = new Scanner(inputStream);//将得到的输入流封装到Scanner,输入流都用Scanner就可以接受
    39. //5,响应请求
    40. OutputStream outputStream = socket.getOutputStream();
    41. Log.println("请求方的输出流 :" + outputStream);
    42. OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream, "UTF-8");
    43. PrintWriter writer = new PrintWriter(outputStreamWriter);
    44. while (true) {
    45. if (!sc.hasNextLine()) {
    46. break;
    47. }
    48. String request = sc.nextLine();//获取到数据
    49. String engLish = request;
    50. Log.println("请求内容 :" + request);
    51. //4,执行业务
    52. String chiWord = translate(engLish);
    53. String response = chiWord;
    54. //5
    55. writer.printf("%s\r\n", response);
    56. writer.flush();
    57. Log.println("开始响应请求");
    58. Log.println("响应内容:" + response);
    59. }
    60. //6,释放socket资源
    61. socket.close();
    62. Log.println("释放请求方socket");
    63. }catch (Exception e){
    64. throw new RuntimeException(e);
    65. }
    66. }
    67. }
    68. public static void main(String[] args) throws Exception{
    69. //1.创建TCP套接字
    70. ServerSocket serverSocket = new ServerSocket(port);
    71. //2,获取客户端的套接字,没有请求时会阻塞
    72. ThreadPoolExecutor pool = new ThreadPoolExecutor(3,3,10, TimeUnit.SECONDS,
    73. new LinkedBlockingQueue<>(), new ThreadPoolExecutor.AbortPolicy());
    74. while (true){
    75. Log.println("等待连接建立");
    76. Socket socket = serverSocket.accept();
    77. Log.println("客户段连接建立,将新的连接提交到线程池");
    78. //创建新的任务提交到线程池
    79. pool.submit(new Task(socket));
    80. }
    81. }
    82. private static String translate(String engWord) {
    83. String chiWord = map.getOrDefault(engWord,"查无此词");
    84. return chiWord;
    85. }
    86. }

    TCP客户端

    1. package 网络编程.tcp;
    2. import 网络编程.Log;
    3. import java.io.*;
    4. import java.net.InetAddress;
    5. import java.net.ServerSocket;
    6. import java.net.Socket;
    7. import java.util.Scanner;
    8. public class TCPClient {
    9. public static void main(String[] args) throws Exception{
    10. //客户端不需要SeverSocket,直接创建Socket对象
    11. //1,创建Socket并将其连接到指定的IP下的指定端口,即连接到服务器
    12. Log.println("准备与服务器创建连接");
    13. Socket socket = new Socket("127.0.0.1",9999);
    14. InputStream inputStream = socket.getInputStream();
    15. Scanner socInput = new Scanner(inputStream,"UTF-8");
    16. OutputStream outputStream = socket.getOutputStream();
    17. OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream,"UTF-8");
    18. PrintWriter writer = new PrintWriter(outputStreamWriter);
    19. Log.println("连接建立成功,请输入英文:");
    20. //2,输入请求
    21. Scanner sc = new Scanner(System.in);
    22. while (true){
    23. if (!sc.hasNextLine()){
    24. break;
    25. }
    26. String engWord = sc.nextLine();
    27. String request = engWord + "\r\n";
    28. //3,将请求保存到输出流,发送请求
    29. Log.println("准备发送请求");
    30. writer.write(request);
    31. writer.flush();
    32. Log.println("请求发送成功");
    33. //4,接受响应
    34. Log.println("准备接受响应");
    35. String response = socInput.nextLine();//nextLine返回的值,直接将换行去掉
    36. Log.println("请求:" + engWord);
    37. Log.println("响应:" + response);
    38. }
    39. //5,关闭资源
    40. socket.close();
    41. Log.println("关闭资源");
    42. }
    43. }

     

  • 相关阅读:
    TCP协议学习记录
    如何关闭 Visual Studio 双击异常高亮
    c++初始化列表
    MySQL常见报错
    二叉搜索树+二叉进阶oj
    Mybatis,其中难点问题做了详细解释
    基于JavaSwing开发五子棋游戏 课程设计 大作业
    扬帆际海—为什么要做shopee跨境本土店
    Spring Cloud 微服务系列文章合集,一次性看个够!
    Netty(13)源码分析(一)
  • 原文地址:https://blog.csdn.net/qq_52655865/article/details/126012087