网络基本原理(2)

跨主机的进程 to 进程

Server 进程 运行在Linux云服务器(上海)

Client 进程 运行在 我自己的电脑上(自己电脑所在地)

数据的封装过程

  1. os代码对数据进行了封装

  1. 观察封装后的数据(抓包工具)

  1. 数据是被接力到另一台主机上的

网络编程中的业务模型

  1. 写死请求

  1. 由用户输入单词完成翻译

  1. 变成一个命令程序

package udp.dictionary_service;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;

/**
 * @author jiaoer
 **/
public class Client {
    public static void main(String[] args)throws IOException {
        //目前服务器在本机:127.0.0.1
        //目前服务器的端口是:8080
        DatagramSocket socket = new DatagramSocket(9999);

        //目前只发依次请求
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNextInt()) {
            String word = scanner.nextLine();
            //准备发送请求
            String request = "我是Java19班的\r\n" + word + "\r\n";
            byte[] bytes = request.getBytes("UTF-8");
            DatagramPacket sent = new DatagramPacket(
                    bytes,
                    0,
                    bytes.length,
                    InetAddress.getLoopbackAddress(),
                    8080
            );
            socket.send(sent);
            //接受方取接受数据
            byte[] buf = new byte[1024];
            DatagramPacket received = new DatagramPacket(buf, buf.length);

            socket.receive(received);  //服务器会阻塞
            int n = received.getLength();
            String response = new String(buf, 0, n, "UTF-8");
            System.out.println(response);
        }
    }
}

TCP流套接字编程

和刚才UDP类似. 实现一个简单的英译汉的功能

ServerSocket API

ServerSocket 是创建TCP服务端Socket的API。

ServerSocket 构造方法:

ServerSocket 方法:

Socket API

Socket 是客户端Socket,或服务端中接收到客户端建立连接(accept方法)的请求后,返回的服务端

Socket。

不管是客户端还是服务端Socket,都是双方建立连接以后,保存的对端信息,及用来与对方收发数据

的。

Socket 构造方法

Socket 方法:

服务器

package tcp.dictionary_service;
//UDP:无连接     写信
//TCP:有链接     打电话

import com.sun.xml.internal.ws.policy.privateutil.PolicyUtils;

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

/**
 * @author jiaoer
 **/
public class Server {
    private static final HashMap<String,String> map = new HashMap<>();
    static {
        map.put("apple","苹果");
        map.put("banana","香蕉");

    }
    public static void main(String[] args) throws IOException {
        //1.开店(创建 socket)
        Log.println("服务器启动,监听在 TCP:8080端口");
        ServerSocket serverSocket = new ServerSocket(8080);
        //进行循环
        while (true) {
            //1.接通链接(电话) -- accept
            Log.println("等待新的客户端链接上来");
            Socket socket = serverSocket.accept();//阻塞
            Log.println("有等待新的客户端链接上来");
            InetAddress inetAddress = socket.getInetAddress();
            Log.println("对方的地址:" + inetAddress);
            int port = socket.getPort();
            Log.println("对方的端口:" + port);
            //is: 用于读数据
            InputStream is = socket.getInputStream();
            //os:用于写数据
            OutputStream os = socket.getOutputStream();
            //2.读取请求
            Scanner scanner = new Scanner(is,"UTF-8");
            String header = scanner.nextLine();//我是Java19班的
            //TODO:做请求格式检查
            String englishWord = scanner.nextLine();
            Log.println("英文单词:" + englishWord);
            //3.处理业务
            String chineseWord = map.getOrDefault(englishWord,"不认识");
            Log.println("中文单词:" + chineseWord);
            //4.发送请求
            //好的\r\n苹果\r\n
            //OutputStream -> OutputStreamWriter -> PrintWriter
            OutputStreamWriter writer = new OutputStreamWriter(os,"UTF-8");
            PrintWriter printWriter = new PrintWriter(writer);
            Log.println("服务器进行发送");
            printWriter.printf("好的\r\n%s\r\n", chineseWord);
            printWriter.flush();  //不要忘记flush
            Log.println("服务器发送成功");
            socket.close();
        }
    }
}

客户端

package tcp.dictionary_service;

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

/**
 * @author jiaoer
 **/
public class Client {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("127.0.0.1", 8080);
        InputStream is = socket.getInputStream();
        Scanner scanner = new Scanner(is,"UTF-8");
        OutputStream os = socket.getOutputStream();
        OutputStreamWriter writer = new OutputStreamWriter(os,"UTF-8");
        PrintWriter printWriter = new PrintWriter(writer);
        //发送请求
        printWriter.printf("我是Java19班的\r\napple\r\n");
        printWriter.flush();
        //读取响应
        String header = scanner.nextLine(); //好的
        String word = scanner.nextLine();//苹果
        System.out.println(word);
        socket.close();

        }
    }

小Tips:

若在运行服务器时遇到以下情况,则表示创建失败地址已经被使用,说明该端口(TCP8080)已经被其他进程占用了

解决办法有两个:

  1. 找到哪个进程占用的此端口,判断这个进程是否重要,如果不重要,就把对应的进程关掉,把端口让出来。

如何找到哪个进程占用的端口

windows上可以这样找:

(1)通过一个命令行程序,明确哪个pid占用的端口

(2)根据pid,看进程的具体信息 via 任务管理器

  1. 换个端口重新试试。

长连接 VS 短连接

长连接:拨通电话 -> 请求 -> 响应 -> 请求 -> 响应 -> … -> 请求 -> 响应 -> 挂断电话

长连接模式下,由于TCP是面向字节流的

客户端:[请求1] [请求2] [请求3]

服务器:[请求1 + 请求2前一半] [请求2后一半 + 请求3]

所以在进行应用层协议设计时需要考虑:请求中需要有明确的分界线,帮助服务器划分出不同的请求

具体方法:

1.采用定长的方式:比如,请求的长度固定是十个字节

2.采用变长的方式:(1)先发送长度 + 真正的请求

(2)使用特殊字符区分不同的请求

长连接版本继续进化:客户端的单词来自用户输入,什么时候结束也来自用户输入

package tcp.dictionary_service;
import java.io.*;
import java.net.Socket;
import java.util.Scanner;

/**
 * @author jiaoer
 **/
public class 长连接之用户输入Client {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("127.0.0.1", 8080);//拨号
        Scanner systemInScanner = new Scanner(System.in);
        while (systemInScanner.hasNextLine()){
            String w = systemInScanner.nextLine();
            InputStream is = socket.getInputStream();
            Scanner socketScanner = new Scanner(is, "UTF-8");
            OutputStream os = socket.getOutputStream();
            OutputStreamWriter writer = new OutputStreamWriter(os, "UTF-8");
            PrintWriter printWriter = new PrintWriter(writer);
            //发送请求
            printWriter.printf("我是Java19班的\r\n%s\r\n",w);
            printWriter.flush();
            //读取响应
            String header = socketScanner.nextLine(); //好的
            String word = socketScanner.nextLine();//苹果
            System.out.println(word);
        }
        //socket.close();

    }
}

短链接:拨通电话 -> 请求 -> 响应 ->挂断电话 -> 拨通电话 -> 请求 -> 响应 ->挂断电话…

TCP与UDP的其中一个区别

TCP:面向字节流 (让A送信,A是一天送一回,可能累计数据一起发送)[abc][def][ghi] -> [abcde][fghi]

UDP:面向数据报文 (让A送信,给到A之后,A立即去送)[abc] -> [abc]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值