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服务器

package 网络编程.udp;

import 网络编程.Log;

import java.io.IOException;
import java.net.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

public class TranslateServer {
    //公开的IP和端口
    public final static int port = 9999;
    private static HashMap<String, String> map = new HashMap<>();
    static {
        map.put("apple","苹果");
        map.put("book","书");
        map.put("bicycle","自行车");
        Log.println("字典初始化完成");
    }

    public static void main(String[] args) throws Exception {
        Log.println("准备创建UDP Socklet ,端口是:" + port);
        DatagramSocket socket = new DatagramSocket(port);//参加Socket关键字
        Log.println("UDP Socket 创建完成");
        //服务器接受请求,被动接收,所以是一直处在运行态
        while (true){
            //1,参加Socket
            //参加容器接收数据,1024缓冲的大小
            byte[] buf = new byte[1024];
            //参加获取请求的数据包
            DatagramPacket receivePacket = new DatagramPacket(buf,buf.length);
            //获取从客户端发来的请求,如果客户端没有发送请求,就会一直再次阻塞

            //2,获取请求的信息
            Log.println("准备接收请求,容器大小" + buf.length);
            socket.receive(receivePacket);
            Log.println("接受到请求");
            //获取对方的IP和port
            InetAddress address = receivePacket.getAddress();
            int port1 = receivePacket.getPort();
            Log.println("请求方的端口 : " + port1);
            Log.println("请求方的IP : " + address);
            //获取对方的IP+port
            SocketAddress socketAddress = receivePacket.getSocketAddress();
            //获取请求内容
            byte[] data = receivePacket.getData();
            //获取接收数据的大小
            int length = receivePacket.getLength();
            Log.println("接收到请求的长度 : " + length);
            //3,解析请求
            //字符集解码
            String request = new String(data,0,length,"UTF-8");
            String engWord = request;
            Log.println("接受到请求的数据为 : " + engWord);

            //4,执行业务
            String chiWord = tranlaste(engWord);
            Log.println("翻译后的结果为: " + chiWord);
            //5,封装响应
            String response = chiWord;
            byte[] sendBuf = response.getBytes("UTF-8");
            //发送数据,要将接收方的信息一并封装到包中socketAddress
            DatagramPacket responsePacket = new DatagramPacket(sendBuf,0,sendBuf.length,socketAddress);
            Log.println("接收方发送响应 ... ");

            //6,发送响应
            socket.send(responsePacket);
            Log.println("接收方发送响应成功,发送的数据为 : " + chiWord);
            //此时请求完成,等待下一次响应
        }
        //服务器关闭时,关闭所有的套接字资源
//        socket.close();
    }

    private static String tranlaste(String engWord) {
        String chiWord = map.getOrDefault(engWord,"查无此词");
        return chiWord;
    }
}

UDP客户端

package 网络编程.udp;

import 网络编程.Log;

import java.io.UnsupportedEncodingException;
import java.net.*;
import java.util.Scanner;

public class ClientVersion1 {
    public static final int port = 8888;
    public static void main(String[] args) throws Exception {
        //1,创建DatagramSocket
        Log.println("开始创建UDP Socket");
        DatagramSocket socket = new DatagramSocket(port);
        Log.println("UDP Socket创建完成");
        Scanner sc = new Scanner(System.in);
        System.out.print("输入要翻译的单词:");

       while (sc.hasNextLine()){
           //2,创建请求数据包
           String request = sc.nextLine();
           Log.println("请求的数据为" + request);
           byte[] buf = request.getBytes("UTF-8");
           Log.println("准备创建请求数据包");
           DatagramPacket requestPacket = new DatagramPacket(buf,0,buf.length, InetAddress.getLoopbackAddress(),TranslateServer.port);
            Log.println("请求数据包创建完成");

           //3,发送请求
           Log.println("发送请求");
           socket.send(requestPacket);
           Log.println("请求发送成功");

           //4,接收响应
           byte[] bytes = new byte[1024];
           DatagramPacket responsePacket = new DatagramPacket(bytes,bytes.length);
           Log.println("开始接收响应");
           socket.receive(responsePacket);
           byte[] data = responsePacket.getData();
           String res = new String(data,0,responsePacket.getLength(),"UTF-8");
           Log.println("请求结果为:" + res);
           Log.println("对方的IP为" + requestPacket.getAddress());
           Log.println("对方的端口为" + requestPacket.getPort());
           System.out.print("输入要翻译的单词:");
       }

        //5,关闭资源
        socket.close();
       Log.println("关闭Socket");
    }
}

Log日志打印

package 网络编程;

import java.text.DateFormat;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;

//日志类
public class Log {
    public static void println(Object o){
        LocalDateTime localDateTime =LocalDateTime.now(ZoneId.of("Asia/Shanghai"));//该类的构造方法私有,通过静态方法获取时间戳
        DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");
        String time = format.format(localDateTime);
        //所以要打印的信息就是时间+对象信息
        System.out.println(time +" : "+ (o == null ? null : o.toString()));
    }

    public static void main(String[] args) {
        println(null);
    }
}

TCP协议相关类

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

ServerSocket类创建 TCPSocket服务器对象

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

 服务器的方法:

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

关闭连接

 

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

返回对方的连接地址

 返回套接字的输入输出流

客户端的方法:

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

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

TCP网络编程

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

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

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

TCP 长连接多线程服务器:

package 网络编程.tcp;

import 网络编程.Log;

import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Scanner;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class TCPService {
    public static final int port = 9999;
    private static HashMap<String, String> map = new HashMap<>();
    static {
        map.put("apple","苹果");
        map.put("book","书");
        map.put("bicycle","自行车");
        Log.println("字典初始化完成");
    }

    private static class Task implements Runnable{
        private static Socket socket;
        public Task(Socket socket){
            this.socket = socket;
        }
        @Override
        public void run() {
            try {
                Log.println("准备获取请求方法信息");
                Log.println("请求方IP:" + socket.getInetAddress());//获取客户端的IP)
                Log.println("请求方端口:" + socket.getPort());//获取客户端的端口
                Log.println("请求方IP + port :" + socket.getRemoteSocketAddress());//获取客户端的IP+port

                //3,获取客户端的输入流
                InputStream inputStream = socket.getInputStream();
                Log.println("请求方的输入流:" + inputStream);
                Scanner sc = new Scanner(inputStream);//将得到的输入流封装到Scanner,输入流都用Scanner就可以接受

                //5,响应请求
                OutputStream outputStream = socket.getOutputStream();
                Log.println("请求方的输出流 :" + outputStream);
                OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream, "UTF-8");
                PrintWriter writer = new PrintWriter(outputStreamWriter);
                while (true) {
                    if (!sc.hasNextLine()) {
                        break;
                    }
                    String request = sc.nextLine();//获取到数据
                    String engLish = request;
                    Log.println("请求内容  :" + request);

                    //4,执行业务
                    String chiWord = translate(engLish);
                    String response = chiWord;

                    //5
                    writer.printf("%s\r\n", response);
                    writer.flush();
                    Log.println("开始响应请求");
                    Log.println("响应内容:" + response);
                }
                //6,释放socket资源
                socket.close();
                Log.println("释放请求方socket");
            }catch (Exception e){
                throw  new RuntimeException(e);
            }
        }
    }

    public static void main(String[] args) throws  Exception{

        //1.创建TCP套接字
        ServerSocket serverSocket = new ServerSocket(port);
        //2,获取客户端的套接字,没有请求时会阻塞

        ThreadPoolExecutor pool = new ThreadPoolExecutor(3,3,10, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(), new ThreadPoolExecutor.AbortPolicy());
        while (true){
            Log.println("等待连接建立");
            Socket socket = serverSocket.accept();
            Log.println("客户段连接建立,将新的连接提交到线程池");
            //创建新的任务提交到线程池
            pool.submit(new Task(socket));
        }
    }
    private static String translate(String engWord) {
        String chiWord = map.getOrDefault(engWord,"查无此词");
        return chiWord;
    }
}

TCP客户端

package 网络编程.tcp;

import 网络编程.Log;

import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

public class TCPClient {
    public static void main(String[] args) throws Exception{
        //客户端不需要SeverSocket,直接创建Socket对象
        //1,创建Socket并将其连接到指定的IP下的指定端口,即连接到服务器
        Log.println("准备与服务器创建连接");
        Socket socket = new Socket("127.0.0.1",9999);

        InputStream inputStream = socket.getInputStream();
        Scanner socInput = new Scanner(inputStream,"UTF-8");

        OutputStream outputStream = socket.getOutputStream();
        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream,"UTF-8");
        PrintWriter writer = new PrintWriter(outputStreamWriter);

        Log.println("连接建立成功,请输入英文:");
        //2,输入请求
        Scanner sc = new Scanner(System.in);
        while (true){
            if (!sc.hasNextLine()){
                break;
            }
            String engWord = sc.nextLine();
            String request = engWord + "\r\n";

            //3,将请求保存到输出流,发送请求
            Log.println("准备发送请求");
            writer.write(request);
            writer.flush();
            Log.println("请求发送成功");

            //4,接受响应
            Log.println("准备接受响应");
            String response = socInput.nextLine();//nextLine返回的值,直接将换行去掉
            Log.println("请求:" + engWord);
            Log.println("响应:" + response);
        }
        //5,关闭资源
        socket.close();
        Log.println("关闭资源");
    }
}

 

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值