草庐IT

网络编程之TCP

小魏爱炸毛 2023-09-09 原文

hi,大家好,今天为大家带来TCP协议的相关知识

这里写目录标题

认识TCP的相关方法

实现TCP版本的回显服务器

实现多线程版本的TCP回显服务器

实现线程池版本的TCP回显服务器

认识TCP方法

TCP也有两个核心的类
Socket和SeverSocket
SeverSocket是给服务器用的
Socket的话,客户端可以用,服务器也可以用

先来看看ServerSocket

ServerSocket() 创建未绑定的服务器套接字。
ServerSocket(int port) 创建绑定到指定端口的服务器套接字。

但我们一般在写服务器的时候都会指定服务器端口号
就像饭店,也得有一个固定位置

Socket accept() 侦听要连接到此套接字并接受它。

accept就是接收的意思,客户端向服务器发起连接请求,在内核中进行连接,accept这里是应用程序层面的接受,就是把连接好的连接拿出来让应用程序连起来,这里先简单的认为是连接,后面讲到TCP的三次握手四次招手再具体介绍
ServerSocket是创建TCP服务端的API
下面再来看看Socket

Socket() 创建一个未连接的套接字,并使用系统默认类型的SocketImplort。
Socket(InetAddress address, int port) 创建流套接字并将其连接到指定IP地址的指定端口号。
public Socket(String host,
int port)
throws UnknownHostException,
IOException
创建流套接字并将其连接到指定主机上的指定端口号。

用于从该套接字读取字节的输入流。

用于将字节写入此套接字的输出流。
InputStream和OutputStream是字节流,TCP是面对字节流的
InputStream是读数据,也就是从网卡接收
OutputStream是写数据,也就是从网卡发送

方法就认识这么多,下面来写TCP版本的回显程序

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;



/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: WHY
 * Date: 2023-04-07
 * Time: 20:43
 */
public class TCPEchoSever {
    //severSocket就是外场拉客的小哥
    //clientSocket就是内场服务的小姐姐
    //severSocket只有一个,clientSocket会给每个客户端都分配一个

    private ServerSocket serverSocket=null;

    public TCPEchoSever(int port) throws IOException {
        serverSocket=new ServerSocket(port);
    }

    public void start() throws IOException {
        System.out.println("服务器启动!");
        while(true){
            Socket clientSocket=serverSocket.accept();
            processConnection(clientSocket);
        }
    }
    //通过这个方法处理一个连接
    //读取请求
    //根据请求计算响应
    //把响应返回给客户端

    private void processConnection(Socket clientSocket) throws IOException {
        System.out.printf("[%s:%d] 客户端上线\n",clientSocket.getInetAddress().toString(),
                clientSocket.getPort());//得到IP和端口号
        //try()这种写法,括号中允许有多个流对象,用;来分割
        try(InputStream inputStream=clientSocket.getInputStream();
            OutputStream outputStream=clientSocket.getOutputStream()) {
            Scanner scanner=new Scanner(inputStream);
            PrintWriter printWriter=new PrintWriter(outputStream);
            while(true){
                //1.读取请求
                if(!scanner.hasNext()){
                    //读取的流到了结尾(对端关闭)
                    System.out.printf("[%s:%d] 客户端下线!\n",clientSocket.getInetAddress(),
                            clientSocket.getPort());
                    break;
                }
                //直接使用scanner读取一段字符串
                String request=scanner.next();
                //2.根据请求响应
                String response=process(request);
                //3.把响应写回到客户端,不要忘了响应也要戴上换行
                printWriter.println(response);
                System.out.println(response);
                System.out.printf("[%s:%d] req: %s; resp: %s\n",clientSocket.getInetAddress(),
                        clientSocket.getPort(),request,response);
            }


        } catch (IOException e) {
            throw new RuntimeException(e);
        }finally {
            clientSocket.close();
        }

    }

    private String process(String request) {
        return request;
    }

    public static void main(String[] args) throws IOException {
        TCPEchoSever tcpEchoSever=new TCPEchoSever(9090);
        tcpEchoSever.start();
    }

}

我们来运行一下程序
先启动服务器


看到客户端也启动了,但是输入却没有响应,这是有问题的
为什么客户端输入消息,但是客户端服务器没有任何响应呢
原因就是我们进行TCP的通信媒介是网卡,网卡读取速度比硬盘还要慢,因此为了提高IO效率,我们一般先把数据放到缓冲区上,然后再手动刷新,再到网卡上

这里的打印,是从网卡上读取数据打印的,但是打印是无效的,因为此时数据在缓冲区,需要刷新一下,也就是使用printWriter的flush方法
同样在服务器的
打印响应也要进行刷新
上代码
服务器

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;



/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: WHY
 * Date: 2023-04-07
 * Time: 20:43
 */
public class TCPEchoSever {
    //severSocket就是外场拉客的小哥
    //clientSocket就是内场服务的小姐姐
    //severSocket只有一个,clientSocket会给每个客户端都分配一个

    private ServerSocket serverSocket=null;

    public TCPEchoSever(int port) throws IOException {
        serverSocket=new ServerSocket(port);
    }

    public void start() throws IOException {
        System.out.println("服务器启动!");
        while(true){
            Socket clientSocket=serverSocket.accept();
            processConnection(clientSocket);
        }
    }
    //通过这个方法处理一个连接
    //读取请求
    //根据请求计算响应
    //把响应返回给客户端

    private void processConnection(Socket clientSocket) throws IOException {
        System.out.printf("[%s:%d] 客户端上线\n",clientSocket.getInetAddress().toString(),
                clientSocket.getPort());//得到IP和端口号
        //try()这种写法,括号中允许有多个流对象,用;来分割
        try(InputStream inputStream=clientSocket.getInputStream();
            OutputStream outputStream=clientSocket.getOutputStream()) {
            Scanner scanner=new Scanner(inputStream);
            PrintWriter printWriter=new PrintWriter(outputStream);
            while(true){
                //1.读取请求
                if(!scanner.hasNext()){
                    //读取的流到了结尾(对端关闭)
                    System.out.printf("[%s:%d] 客户端下线!\n",clientSocket.getInetAddress(),
                            clientSocket.getPort());
                    break;
                }
                //直接使用scanner读取一段字符串
                String request=scanner.next();
                //2.根据请求响应
                String response=process(request);
                //3.把响应写回到客户端,不要忘了响应也要戴上换行
                printWriter.println(response);
                printWriter.flush();
                System.out.println(response);
                System.out.printf("[%s:%d] req: %s; resp: %s\n",clientSocket.getInetAddress(),
                        clientSocket.getPort(),request,response);
            }


        } catch (IOException e) {
            throw new RuntimeException(e);
        }finally {
            clientSocket.close();
        }

    }

    private String process(String request) {
        return request;
    }

    public static void main(String[] args) throws IOException {
        TCPEchoSever tcpEchoSever=new TCPEchoSever(9090);
        tcpEchoSever.start();
    }

}

客户端

package network;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: WHY
 * Date: 2023-04-07
 * Time: 21:16
 */
public class TCPEchoClient {
    private Socket socket=null;

    public TCPEchoClient(String serverIp,int port) throws IOException {
        //这个操作相当于让客户端和服务器建立tcp连接
        //这里的连接连上了,服务器的accept就会返回
        socket=new Socket(serverIp,port);

    }
    public void start(){
        Scanner scanner=new Scanner(System.in);
        try(InputStream inputStream=socket.getInputStream();
            OutputStream outputStream=socket.getOutputStream()){
            PrintWriter printWriter=new PrintWriter(outputStream);
            Scanner scannerFromSocket=new Scanner(inputStream);
            while(true){
                //1.从键盘上读取用户输入的内容
                System.out.print("->");
                String request=scanner.next();
                //2.把读取的内容构造成请求,发送给服务器
                //注意,这里的发送,是带有换行的
                printWriter.println(request);
                printWriter.flush();
                //3.从服务器读取响应
                String response=scannerFromSocket.next();
                //4.把响应结果显示到控制台上
                System.out.printf("req: %S; resp: %s\n",request,response );
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    public static void main(String[] args) throws IOException {
        TCPEchoClient tcpEchoClient=new TCPEchoClient("127.0.0.1",9090);
        tcpEchoClient.start();

    }

}

运行结果


现在就正常了
我们来总结一下TCP通信过程
1.首先要开启服务器,start这里,然后运行到accept这里,阻塞等待
等待客户端的请求
2.客户端启动时,调用Socket方法,和服务器在内核中建立连接,连接成功以后,服务器的accept就返回了
3.服务器这边进入processConnection方法,尝试从客户端读取请求,但是由于此时用户还没有输入,所以读取操作也会阻塞等待
3.客户端这边往下执行时,从控制台读取用户输入,也会阻塞,因为用户可能不会立即输入
4.当用户输入,客户端发请求出去,客户端代码继续执行,到读取服务器响应时,再次阻塞
5.服务器收到客户端的请求,从next这里返回,执行process方法,执行println,把响应写回给客户端

6.客户端这边收到服务器的响应打印到控制台上,同时进入下一次循环,等待用户输入
6.服务器这边回到循环开始的地方,继续尝试获取客户端的请求,然后阻塞等待

对于缓冲区和网卡之间的切换再来说一说
在发数据的时候,必须通过网卡发送,应用程序层面无法直接通过网卡发送数据
接收数据的时候,应用软件无法直接通过网卡接收
那么怎么办呢,在tcp通信中,是存在缓冲区这样的概念的,我们通过缓冲区进行发送和接收
下面来画个图
我们先来画跨主机通信的,那么就需要两个网卡

传输的数据是需要套接socket的,可以把socket当做一个文件,套接tcp数据报进行传输,就像水是在水管里运输的,水管就是相当于socket,水相当于要传输的数据
send:
客户端发出的数据,要先通过socket进行包装,通过socket对象的getOutputStream方法获取与这个socket相关的outputStream对象,使用write()方法写入到缓冲区,然后将发送缓冲区的数据送到网卡,网卡将数据发到服务器端的接收缓冲区,再发给服务器
receive:
服务器在收到数据的时候,处理完数据,就又通过socket,发送到服务器这边的发送缓冲区,发送缓冲区发给网卡,然后交给客户端这边,客户端通过socket对象的getInputStream方法获取与这个socket相关的inputStream对象,客户端就可以通过read()方法从接收缓冲区读取数据
下图是在同一个主机通信的,就使用一个环回网卡就行!!!


发送数据和接收数据过程是一样,就不赘述了
现在这个程序是没有问题了,但是在互联网的世界中,我们知道肯定是有很多客户端要访问服务器
那么这个代码这样写是不支持多个客户端同时访问的,我们来看看

在idea勾选,就可以在idea同时运行很多个客户端



可以看到,我们在客户端1输入请求,是有响应的,在其他的客户端上输入请求,是没有响应的
这里其实是一个bug.是代码的bug
我们来分析一下

当客户端1来的时候,操作系统从内核拿出连接,然后接受连接,调用processConnectino方法,进入processConnecion方法那么只有当客户端1下线其他客户端才能相继连接,我们想要循环处理客户端1的数据,还要循环accept,接受其他的客户端请求,那么我们要咋样解决这个问题呢?
用多线程

这样写就可以实现一边循环处理原来客户端的请求,还能循环接收其他的客户端进行accept
注意:这里的线程是一个一个创建的,就是串行创建,而在执行的时候是并发的
咋样通俗理解呢
拿售楼来说,一个小哥拉来客户1号,交给一个小姐姐1,进行服务,在小姐姐1服务客户1的时候,这个小哥又拉来一个客户2,再来一个小姐姐2,服务,依次类推,这些客户之间,小姐姐之间是互不影响的,是一起执行的,就是这样理解的了

我们把完整代码给大家
客户端

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;



/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: WHY
 * Date: 2023-04-07
 * Time: 20:43
 */
public class TCPEchoSever {
    //severSocket就是外场拉客的小哥
    //clientSocket就是内场服务的小姐姐
    //severSocket只有一个,clientSocket会给每个客户端都分配一个

    private ServerSocket serverSocket=null;

    public TCPEchoSever(int port) throws IOException {
        serverSocket=new ServerSocket(port);
    }

    public void start() throws IOException {
        System.out.println("服务器启动!");
        while(true){
            Socket clientSocket=serverSocket.accept();
            Thread thread=new Thread(()->{
                try {
                    processConnection(clientSocket);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            });


        }
    }
    //通过这个方法处理一个连接
    //读取请求
    //根据请求计算响应
    //把响应返回给客户端

    private void processConnection(Socket clientSocket) throws IOException {
        System.out.printf("[%s:%d] 客户端上线\n",clientSocket.getInetAddress().toString(),
                clientSocket.getPort());//得到IP和端口号
        //try()这种写法,括号中允许有多个流对象,用;来分割
        try(InputStream inputStream=clientSocket.getInputStream();
            OutputStream outputStream=clientSocket.getOutputStream()) {
            Scanner scanner=new Scanner(inputStream);
            PrintWriter printWriter=new PrintWriter(outputStream);
            while(true){
                //1.读取请求
                if(!scanner.hasNext()){
                    //读取的流到了结尾(对端关闭)
                    System.out.printf("[%s:%d] 客户端下线!\n",clientSocket.getInetAddress(),
                            clientSocket.getPort());
                    break;
                }
                //直接使用scanner读取一段字符串
                String request=scanner.next();
                //2.根据请求响应
                String response=process(request);
                //3.把响应写回到客户端,不要忘了响应也要戴上换行
                printWriter.println(response);
                printWriter.flush();
                System.out.println(response);
                System.out.printf("[%s:%d] req: %s; resp: %s\n",clientSocket.getInetAddress(),
                        clientSocket.getPort(),request,response);
            }


        } catch (IOException e) {
            throw new RuntimeException(e);
        }finally {
            clientSocket.close();
        }

    }

    private String process(String request) {
        return request;
    }

    public static void main(String[] args) throws IOException {
        TCPEchoSever tcpEchoSever=new TCPEchoSever(9090);
        tcpEchoSever.start();
    }

}

服务器

import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: WHY
 * Date: 2023-04-07
 * Time: 21:16
 */
public class TCPEchoClient {
    private Socket socket=null;

    public TCPEchoClient(String serverIp,int port) throws IOException {
        //这个操作相当于让客户端和服务器建立tcp连接
        //这里的连接连上了,服务器的accept就会返回
        socket=new Socket(serverIp,port);

    }
    public void start(){
        Scanner scanner=new Scanner(System.in);
        try(InputStream inputStream=socket.getInputStream();
            OutputStream outputStream=socket.getOutputStream()){
            PrintWriter printWriter=new PrintWriter(outputStream);
            Scanner scannerFromSocket=new Scanner(inputStream);
            while(true){
                //1.从键盘上读取用户输入的内容
                System.out.print("->");
                String request=scanner.next();
                //2.把读取的内容构造成请求,发送给服务器
                //注意,这里的发送,是带有换行的
                printWriter.println(request);
                printWriter.flush();
                //3.从服务器读取响应
                String response=scannerFromSocket.next();
                //4.把响应结果显示到控制台上
                System.out.printf("req: %S; resp: %s\n",request,response );
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    public static void main(String[] args) throws IOException {
        TCPEchoClient tcpEchoClient=new TCPEchoClient("127.0.0.1",9090);
        tcpEchoClient.start();

    }

}

线程频繁的创建和销毁是耗费资源的,我们升级代码,采用线程池,避免线程销毁,用完放到线程池里面,更加高效

看看完整代码
客户端

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: WHY
 * Date: 2023-04-07
 * Time: 20:43
 */
public class TCPEchoSever {
    //severSocket就是外场拉客的小哥
    //clientSocket就是内场服务的小姐姐
    //severSocket只有一个,clientSocket会给每个客户端都分配一个

    private ServerSocket serverSocket=null;

    public TCPEchoSever(int port) throws IOException {
        serverSocket=new ServerSocket(port);
    }

    public void start() throws IOException {
        ExecutorService executorService = Executors.newCachedThreadPool();
        System.out.println("服务器启动!");
        while(true){

            Socket clientSocket=serverSocket.accept();

                executorService.submit(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            processConnection(clientSocket);
                        } catch (IOException e) {
                            throw new RuntimeException(e);
                        }
                    }
                });
                   



        }
    }
    //通过这个方法处理一个连接
    //读取请求
    //根据请求计算响应
    //把响应返回给客户端

    private void processConnection(Socket clientSocket) throws IOException {
        System.out.printf("[%s:%d] 客户端上线\n",clientSocket.getInetAddress().toString(),
                clientSocket.getPort());//得到IP和端口号
        //try()这种写法,括号中允许有多个流对象,用;来分割
        try(InputStream inputStream=clientSocket.getInputStream();
            OutputStream outputStream=clientSocket.getOutputStream()) {
            Scanner scanner=new Scanner(inputStream);
            PrintWriter printWriter=new PrintWriter(outputStream);
            while(true){
                //1.读取请求
                if(!scanner.hasNext()){
                    //读取的流到了结尾(对端关闭)
                    System.out.printf("[%s:%d] 客户端下线!\n",clientSocket.getInetAddress(),
                            clientSocket.getPort());
                    break;
                }
                //直接使用scanner读取一段字符串
                String request=scanner.next();
                //2.根据请求响应
                String response=process(request);
                //3.把响应写回到客户端,不要忘了响应也要戴上换行
                printWriter.println(response);
                printWriter.flush();
                System.out.println(response);
                System.out.printf("[%s:%d] req: %s; resp: %s\n",clientSocket.getInetAddress(),
                        clientSocket.getPort(),request,response);
            }


        } catch (IOException e) {
            throw new RuntimeException(e);
        }finally {
            clientSocket.close();
        }

    }

    private String process(String request) {
        return request;
    }

    public static void main(String[] args) throws IOException {
        TCPEchoSever tcpEchoSever=new TCPEchoSever(9090);
        tcpEchoSever.start();
    }

}

服务器

package network;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: WHY
 * Date: 2023-04-07
 * Time: 21:16
 */
public class TCPEchoClient {
    private Socket socket=null;

    public TCPEchoClient(String serverIp,int port) throws IOException {
        //这个操作相当于让客户端和服务器建立tcp连接
        //这里的连接连上了,服务器的accept就会返回
        socket=new Socket(serverIp,port);

    }
    public void start(){
        Scanner scanner=new Scanner(System.in);
        try(InputStream inputStream=socket.getInputStream();
            OutputStream outputStream=socket.getOutputStream()){
            PrintWriter printWriter=new PrintWriter(outputStream);
            Scanner scannerFromSocket=new Scanner(inputStream);
            while(true){
                //1.从键盘上读取用户输入的内容
                System.out.print("->");
                String request=scanner.next();
                //2.把读取的内容构造成请求,发送给服务器
                //注意,这里的发送,是带有换行的
                printWriter.println(request);
                printWriter.flush();
                //3.从服务器读取响应
                String response=scannerFromSocket.next();
                //4.把响应结果显示到控制台上
                System.out.printf("req: %S; resp: %s\n",request,response );
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    public static void main(String[] args) throws IOException {
        TCPEchoClient tcpEchoClient=new TCPEchoClient("127.0.0.1",9090);
        tcpEchoClient.start();

    }

}

补充:

短连接:每次接收到数据并返回响应后,都关闭连接,即是短连接。也就是说,短连接只能一次收发数据
长连接:不关闭连接,一直保持连接状态,双方不停的收发数据,即是长连接。也就是说,长连接可以 多次收发数据


这就是这期的所有内容了,我们下期再见,886!🌸🌸🌸

有关网络编程之TCP的更多相关文章

  1. ruby - 寻找通过阅读代码确定编程语言的ruby gem? - 2

    几个月前,我读了一篇关于ruby​​gem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:

  2. ruby - 用 Ruby 编写一个简单的网络服务器 - 2

    我想在Ruby中创建一个用于开发目的的极其简单的Web服务器(不,不想使用现成的解决方案)。代码如下:#!/usr/bin/rubyrequire'socket'server=TCPServer.new('127.0.0.1',8080)whileconnection=server.acceptheaders=[]length=0whileline=connection.getsheaders想法是从命令行运行这个脚本,提供另一个脚本,它将在其标准输入上获取请求,并在其标准输出上返回完整的响应。到目前为止一切顺利,但事实证明这真的很脆弱,因为它在第二个请求上中断并出现错误:/usr/b

  3. 网络编程套接字 - 2

    网络编程套接字网络编程基础知识理解源`IP`地址和目的`IP`地址理解源MAC地址和目的MAC地址认识端口号理解端口号和进程ID理解源端口号和目的端口号认识`TCP`协议认识`UDP`协议网络字节序socket编程接口`sockaddr``UDP`网络程序服务器端代码逻辑:需要用到的接口服务器端代码`udp`客户端代码逻辑`udp`客户端代码`TCP`网络程序服务器代码逻辑多个版本服务器单进程版本多进程版本多线程版本线程池版本服务器端代码客户端代码逻辑客户端代码TCP协议通讯流程TCP协议的客户端/服务器程序流程三次握手(建立连接)数据传输四次挥手(断开连接)TCP和UDP对比网络编程基础知识

  4. ruby - 我正在学习编程并选择了 Ruby。我应该升级到 Ruby 1.9 吗? - 2

    我完全不是程序员,正在学习使用Ruby和Rails框架进行编程。我目前正在使用Ruby1.8.7和Rails3.0.3,但我想知道我是否应该升级到Ruby1.9,因为我真的没有任何升级的“遗留”成本。缺点是什么?我是否会遇到与普通gem的兼容性问题,或者甚至其他我不太了解甚至无法预料的问题? 最佳答案 你应该升级。不要坚持从1.8.7开始。如果您发现不支持1.9.2的gem,请避免使用它们(因为它们很可能不被维护)。如果您对gem是否兼容1.9.2有任何疑问,您可以在以下位置查看:http://www.railsplugins.or

  5. ruby - 如何以编程方式删除实例上的 "singleton information"以使其编码(marshal)? - 2

    我创建了一个由于“在运行时执行的单例元类定义”而无法编码的对象(这段代码的描述是否正确?)。这是通过以下代码执行的:#defineclassXthatmyusesingletonclassmetaprogrammingfeatures#throughcallofmethod:break_marshalling!classXdefbreak_marshalling!meta_class=class我该怎么做才能使对象编码正确?是否可以从对象instance_of_x的classX中“移除”单例组件?我真的需要一个建议,因为我们的一些对象需要通过Marshal.dump序列化机制进行缓存。

  6. Ruby 元编程问题 - 2

    我正在查看Ruby日志记录库Logging.logger方法并从sourceatgithub提出问题与这段代码有关:logger=::Logging::Logger.new(name)logger.add_appendersappenderlogger.additive=falseclass我知道类 最佳答案 这实际上删除了方法(当它实际被执行时)。这是确保close不会被调用两次的保障措施。看起来好像有嵌套的“class 关于Ruby元编程问题,我们在StackOverflow上找到一

  7. ruby - Paperclip:以编程方式分配图像并设置其名称 - 2

    使用Paperclip,我想从这样的URL抓取图像:require'open-uri'user.photo=open(url)问题是我最后得到一个像“open-uri20110915-4852-1o7k5uw”这样的文件名。有什么方法可以更改user.photo上的文件名?作为一个额外的变化,Paperclip将我的文件存储在S3上,所以如果我可以在初始分配中设置我想要的文件名就更好了,这样图像就会上传到正确的S3key。像这样:user.photo=open(url),:filename=>URI.parse(url).path 最佳答案

  8. ruby - 如何以编程方式检查证书是否已被吊销? - 2

    我正在开发一个xcode自动构建系统。在执行一些预构建验证时,我想检查指定的证书文件是否已被撤销。我了解securityverify-cert验证其他证书属性但不验证吊销。我如何检查撤销?我正在用Ruby编写构建系统,但我对任何语言的想法都持开放态度。我阅读了这个答案(Openssl-Howtocheckifacertificateisrevokedornot),但指向底部的链接(DoesOpenSSLautomaticallyhandleCRLs(CertificateRevocationLists)now?)进入的Material对我的目的来说有点过于复杂(用户上传已撤销的证书是一

  9. ruby - 检查网络文件是否存在,而不下载它? - 2

    是否可以在不实际下载文件的情况下检查文件是否存在?我有这么大的(~40mb)文件,例如:http://mirrors.sohu.com/mysql/MySQL-6.0/MySQL-6.0.11-0.glibc23.src.rpm这与ruby​​不严格相关,但如果发件人可以设置内容长度就好了。RestClient.get"http://mirrors.sohu.com/mysql/MySQL-6.0/MySQL-6.0.11-0.glibc23.src.rpm",headers:{"Content-Length"=>100} 最佳答案

  10. ruby - 404 未找到,但可以从网络浏览器正常访问 - 2

    我在这方面尝试了很多URL,在我遇到这个特定的之前,它们似乎都很好:require'rubygems'require'nokogiri'require'open-uri'doc=Nokogiri::HTML(open("http://www.moxyst.com/fashion/men-clothing/underwear.html"))putsdoc这是结果:/Users/macbookair/.rvm/rubies/ruby-2.0.0-p481/lib/ruby/2.0.0/open-uri.rb:353:in`open_http':404NotFound(OpenURI::HT

随机推荐