使用Socket實現(xiàn)TCP編程及聊天室的編寫

title: 『 Socket 』使用Socket實現(xiàn)TCP編程及聊天室的編寫
tags: socket
categories: socket


若圖片無法顯示堕战,請前往我的博客查看墓赴,相應(yīng)文章鏈接:http://codingxiaxw.cn/2016/11/29/57-socket-chatroom/#more

本篇文章分為兩個部分丹允,一個部分是總結(jié)使用Socket實現(xiàn)TCP的編程的知識,主要就是完成服務(wù)器端和客戶端兩個對象的代碼編寫携茂;另一個部分是通過Java寫一個聊天室來對我們的Socket編程進行鞏固你踩。

文章結(jié)構(gòu):首先是對TCP的簡單介紹,然后在分析Socket通信的模型后進行Java Socket實現(xiàn)TCP編程的代碼編寫讳苦,最后是利用Socket的知識編寫一個簡單的聊天室带膜。

1.TCP簡介

TCP(Transmission Control Protocol 傳輸控制協(xié)議)是一種面向連接的、可靠的鸳谜、基于字節(jié)流的傳輸層通信協(xié)議膝藕,由IETF的RFC 793定義。在簡化的計算機網(wǎng)絡(luò)OSI模型中咐扭,它完成第四層傳輸層所指定的功能芭挽,用戶數(shù)據(jù)報協(xié)議(UDP,本篇文章不介紹UDP)是同一層內(nèi)另一個重要的傳輸協(xié)議蝗肪。在因特網(wǎng)協(xié)議族(Internet protocol suite)中袜爪,TCP層是位于IP層之上,應(yīng)用層之下的中間層穗慕。不同主機的應(yīng)用層之間經(jīng)常需要可靠的、像管道一樣的連接妻导,但是IP層不提供這樣的流機制逛绵,而是提供不可靠的包交換。

首先了解一下網(wǎng)絡(luò)模型中的數(shù)據(jù)傳遞:應(yīng)用層向TCP層發(fā)送用于網(wǎng)間傳輸?shù)木缶隆⒂?位字節(jié)表示的數(shù)據(jù)流术浪,然后TCP把數(shù)據(jù)流分區(qū)成適當長度的報文段(通常受該計算機連接的網(wǎng)絡(luò)的數(shù)據(jù)鏈路層的最大傳輸單元(MTU)的限制)。之后TCP把結(jié)果包傳給IP層寿酌,由它來通過網(wǎng)絡(luò)將包傳送給接收端實體的TCP層胰苏。TCP為了保證不發(fā)生丟包,就給每個包一個序號醇疼,同時序號也保證了傳送到接收端實體的包的按序接收硕并。然后接收端實體對已成功收到的包發(fā)回一個相應(yīng)的確認(ACK)法焰;如果發(fā)送端實體在合理的往返時延(RTT)內(nèi)未收到確認,那么對應(yīng)的數(shù)據(jù)包就被假設(shè)為已丟失將會被進行重傳倔毙。TCP用一個校驗和函數(shù)來檢驗數(shù)據(jù)是否有錯誤埃仪;在發(fā)送和接收時都要計算校驗和。

2.Socket通信模型

所謂socket 通常也稱作”套接字“陕赃,用于描述IP地址和端口卵蛉,是一個通信鏈的句柄。應(yīng)用程序通常通過”套接字”向網(wǎng)絡(luò)發(fā)出請求或者應(yīng)答網(wǎng)絡(luò)請求么库。首先我們看看Socket基于TCP的通信模型圖:
[圖片上傳失敗...(image-1149ea-1526380908286)]

圖中通信模型的各個步驟如下:

  1. 在服務(wù)端建立一個ServerSocket,綁定相應(yīng)的端口傻丝,并且在指定的端口進行偵聽,等待客戶端的連接诉儒。
  2. 當客戶端創(chuàng)建連接Socket并且向服務(wù)端發(fā)送請求葡缰。
  3. 服務(wù)器收到請求,并且接受客戶端的請求信息允睹。一旦接收到客戶端的連接請求后运准,會創(chuàng)建一個連接socket,用來與客戶端的socket進行通信缭受。通過相應(yīng)的輸入/輸出流進行數(shù)據(jù)的交換胁澳,數(shù)據(jù)的發(fā)送接收以及數(shù)據(jù)的響應(yīng)等等。
  4. 當客戶端和服務(wù)端通信完畢后米者,需要分別關(guān)閉socket韭畸,結(jié)束通信。

也就是基于服務(wù)器和客戶端的開發(fā)蔓搞,所以在服務(wù)器端和客戶端我們分別需要完成的代碼就是:

對于服務(wù)器端需要完成的工作是:

  1. 創(chuàng)建ServerSocket對象胰丁,綁定監(jiān)聽器
  2. 通過accept()方法監(jiān)聽客戶端請求
  3. 連接建立以后通過讀取客戶端發(fā)送請求消息
  4. 通過輸出流向客戶端發(fā)送響應(yīng)信息
  5. 關(guān)閉資源

ServerSocket類中涉及到的常用方法:

  • ServerSocket(int port)——創(chuàng)建并綁定到特定端口的服務(wù)器套接字
  • accept()——偵聽并接受到此套接字的連接
  • close()——關(guān)閉此套接字
  • getInetAddress()——得到ServerSocket對象綁定的IP地址。如果ServerSocket對象未綁定IP地址喂分,返回0.0.0.0
  • getLocalPort()——返回此套接字在其上偵聽的端口

客戶端需要完成的工作是:

  1. 創(chuàng)建Socket對象锦庸,指明需要連接的服務(wù)器地址和端口號(1023以后的端口,因為0~1023之間的端口號是我們系統(tǒng)需要使用的端口號)
  2. 連接建立后,通過輸出流向服務(wù)器端請求
  3. 通過輸入流獲取服務(wù)器響應(yīng)信息
  4. 關(guān)閉資源

Socket類中常用的方法:

  • Socket(InetAddress address, int port)——創(chuàng)建一個套接字并將其連接到指定ip地址的指定端口號
  • Socket(String host, int port)——創(chuàng)建一個套接字并將其連接到指定主機上的指定端口號
  • close()——關(guān)閉此套接字
  • getInetAddress()——返回套接字連接的地址
  • getInputStream()——返回此套接字的輸入流
  • getOutputStream——返回此套接字的輸出流

好了蒲祈,通過上述的描述甘萧,對Socket的編程就講述的很清楚了,接下來針對上述描述進行我們服務(wù)器和客戶端代碼的編碼工作梆掸。

3.基于Tcp的Socket開發(fā)代碼編寫

首先是服務(wù)器端Server.java的代碼編寫:

/**
 * Created by codingBoy on 16/11/29.
 * 基于TCP協(xié)議的Socket通信扬卷,實現(xiàn)客戶登錄
 */
public class Server
{

    public static void main(String[] args)
    {
        //1.創(chuàng)建一個服務(wù)器Socket,即ServerSocket,指定綁定的端口酸钦,并堅挺
        try {
            ServerSocket serverSocket=new ServerSocket(8888);
            //2怪得,調(diào)用accept()開始監(jiān)聽,等待客戶端的鏈接
            System.out.println("****服務(wù)器即將啟動,等待客戶端的連接****");
            Socket socket=serverSocket.accept();
            //3.獲取輸入流并獲取客戶信息
            InputStream in=socket.getInputStream();

            InputStreamReader isr=new InputStreamReader(in,"utf-8");

            BufferedReader br=new BufferedReader(isr);

            String info;

            StringBuilder sb=new StringBuilder();
            while ((info=br.readLine())!=null)
            {
                sb.append(info);
            }
            System.out.println("我是服務(wù)器徒恋,客戶端發(fā)來的消息為:"+sb);

            socket.shutdownInput();//關(guān)閉輸入流

            //4.獲取輸出流蚕断,用于響應(yīng)客戶端的請求
            OutputStream os=socket.getOutputStream();
            PrintWriter pw=new PrintWriter(os);
            pw.write("歡迎您");
            pw.flush();//將緩沖輸出

            //4.關(guān)閉相關(guān)資源
            pw.close();
            os.close();


            br.close();
            isr.close();
            in.close();
            socket.close();
            serverSocket.close();

        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

然后是客戶端Client.java的代碼編寫:

public class Client {
    public static void main(String[] args)
    {
        //1.創(chuàng)建客戶端Socket,指定服務(wù)器端地址和端口號
        try {
            Socket socket=new Socket("localhost",8888);
            //2.獲取輸出流,用來向服務(wù)器端發(fā)送登錄信息
            OutputStream os=socket.getOutputStream();

            PrintWriter pw=new PrintWriter(os);//將輸出流打包成打印流
            pw.write("用戶名:codingxiaxw;密碼:123");
            pw.flush();//刷新緩存

            socket.shutdownOutput();//關(guān)閉輸出流

            //3.獲取服務(wù)器傳過來的輸入流因谎,讀取服務(wù)器的響應(yīng)信息
            InputStream in=socket.getInputStream();
            BufferedReader br=new BufferedReader(new InputStreamReader(in,"utf-8"));
            String info;

            StringBuilder sb=new StringBuilder();
            while ((info=br.readLine())!=null)
            {
                sb.append(info);
            }
            System.out.println("我是客戶端基括,服務(wù)器給我的信息為:"+sb);


            //3.關(guān)閉資源

            br.close();
            in.close();


            pw.close();
            os.close();
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

此時我們的客戶端和服務(wù)器端的代碼便完成了,首先運行服務(wù)器Server.java财岔,代碼在執(zhí)行到Socket socket=serverSocket.accept();時會在此阻塞风皿,控制臺輸出:

****服務(wù)器即將啟動,等待客戶端的連接****

直到等到客戶端連接到該端口號的服務(wù)器后服務(wù)器的代碼才會向下執(zhí)行匠璧,此時運行客戶端Client.java桐款,客戶端的控制臺輸出:

我是客戶端,服務(wù)器給我的信息為:歡迎您
Process finished with exit code 0

然后此時切換到服務(wù)器的控制臺夷恍,發(fā)現(xiàn)輸出信息:

****服務(wù)器即將啟動魔眨,等待客戶端的連接****
我是服務(wù)器,客戶端發(fā)來的消息為:用戶名:codingxiaxw;密碼:123

Process finished with exit code 0

此時我們便使用Socket完成了一個服務(wù)器和一個客戶端之間的通信酿雪,那么問題來了遏暴,如何實現(xiàn)一個服務(wù)器與多個客戶端之間的通信呢?我們使用多線程服務(wù)器的方式指黎。創(chuàng)建一個ServerThread.java用于編寫服務(wù)器端多線程接收客戶端傳遞過來的信息的代碼編寫朋凉,代碼如下:

public class ServerThread extends Thread
{
    private Socket socket;

    public ServerThread(Socket socket)
    {
        this.socket=socket;
    }

    public void run()
    {
        //3.獲取輸入流并獲取客戶信息
        InputStream in= null;
        InputStreamReader isr=null;
        BufferedReader br=null;
        OutputStream os=null;
        PrintWriter pw=null;

        try {
            in = socket.getInputStream();
            isr=new InputStreamReader(in,"utf-8");
            br=new BufferedReader(isr);

            String info;

            StringBuilder sb=new StringBuilder();
            while ((info=br.readLine())!=null)
            {
                sb.append(info);
            }
            System.out.println("我是服務(wù)器,客戶端發(fā)來的消息為:"+sb);

            socket.shutdownInput();//關(guān)閉輸入流

            //4.獲取輸出流醋安,用于響應(yīng)客戶端的請求
            os=socket.getOutputStream();
            pw=new PrintWriter(os);
            pw.write("歡迎您");
            pw.flush();//將緩沖輸出

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //4.關(guān)閉相關(guān)資源
            try {
                if (pw!=null) pw.close();
                if (os!=null) os.close();
                if (br!=null) br.close();
                if (isr!=null) isr.close();
                if (in!=null) in.close();
                if (socket!=null) socket.close();
            }catch (Exception e)
            {
                e.printStackTrace();
            }
        }
    }
}

實際上我們就是將之前寫在Server.java中獲取客戶端傳遞過來的信息的那部分代碼拿出來寫在了該線程中杂彭,然后修改Server.java中的代碼為:

public class Server
{
    public static void main(String[] args)
    {
        //1.創(chuàng)建一個服務(wù)器Socket,即ServerSocket,指定綁定的端口吓揪,并堅挺
        try {
            ServerSocket serverSocket=new ServerSocket(8888);
            //2亲怠,調(diào)用accept()開始監(jiān)聽,等待客戶端的鏈接
            System.out.println("****服務(wù)器即將啟動柠辞,等待客戶端的連接****");
            //記錄客戶端的數(shù)量
            int count=0;
            while (true) {
                //調(diào)用accept()方法開始監(jiān)聽团秽,等待客戶端的連接
                Socket socket = serverSocket.accept();
                //創(chuàng)建一個新的線程
                ServerThread serverThread=new ServerThread(socket);
                //啟動線程
                serverThread.start();  //如果不要啟動線程的話這里直接調(diào)用run()也行.

                count++;//統(tǒng)計客戶端的數(shù)量
                System.out.println("客戶端的數(shù)量:"+count);

                InetAddress address=socket.getInetAddress();
                System.out.println("當前客戶端的IP:"+address.getHostAddress());
            }

        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

運行Server.java,服務(wù)器控制臺輸出信息:

****服務(wù)器即將啟動叭首,等待客戶端的連接****

然后運行Client.java习勤,客戶端控制臺輸出信息:

我是客戶端,服務(wù)器給我的信息為:歡迎您

此時跳轉(zhuǎn)到服務(wù)器控制臺放棒,信息變?yōu)?

****服務(wù)器即將啟動姻报,等待客戶端的連接****
我是服務(wù)器己英,客戶端發(fā)來的消息為:用戶名:codingxiaxw;密碼:123
客戶端的數(shù)量:1
當前客戶端的IP:127.0.0.1

然后更改Client.java中傳遞給服務(wù)器的數(shù)據(jù)代碼為:pw.write("用戶名:codingxiaxw;密碼:456");间螟,再運行Client.java,發(fā)現(xiàn)服務(wù)器的控制臺輸出信息變?yōu)?

****服務(wù)器即將啟動,等待客戶端的連接****
我是服務(wù)器厢破,客戶端發(fā)來的消息為:用戶名:codingxiaxw;密碼:13
客戶端的數(shù)量:1
當前客戶端的IP:127.0.0.1
我是服務(wù)器荣瑟,客戶端發(fā)來的消息為:用戶名:codingxiaxw;密碼:456
客戶端的數(shù)量:2
當前客戶端的IP:127.0.0.1

這樣我們便完成了多線程服務(wù)器的編碼工作,到此我們便成功使用Socket完成了基于TCP協(xié)議的編程摩泪。接下來趁熱打鐵笆焰,用Socket實現(xiàn)一個簡單的聊天室功能。

4.使用Socket實現(xiàn)一個簡單的聊天室

功能概述:客戶端用于發(fā)送信息(在控制臺中發(fā)送信息),將在控制臺輸出的信息轉(zhuǎn)換為輸出流輸出到服務(wù)器端见坑,服務(wù)器通過socket.getOutputStream()方法接收客戶端傳來的信息嚷掠,并將發(fā)送該信息的客戶端地址及信息發(fā)送在控制臺中,這樣我們便簡單的實現(xiàn)了我們的聊天室荞驴。

這里給大家講講qq通信的原理:qq使用c/s模式進行通信不皆,qq中的用戶A發(fā)送信息給用戶B,過程是這樣的:當A打開和B的聊天窗口時即和B還有服務(wù)器建立了一個聊天室(同時服務(wù)器和客戶端開啟連接)熊楼,A發(fā)送信息霹娄,其實是發(fā)送到了qq聊天室服務(wù)器的接收容器中,然后qq服務(wù)器將該客戶端地址(即qq頭像)和信息內(nèi)容顯示在聊天室中(即聊天窗口)鲫骗,你每和一個好友進行聊天打開一個窗口就等于和她(另一個客戶端)還有我們的qq服務(wù)器組成了一個聊天室(當然聊天室的服務(wù)器肯定是多線程的)犬耻。qq上還有多人聊天的功能,實現(xiàn)道理也是這樣执泰,只是該聊天室中有多個客戶端給服務(wù)器發(fā)送消息罷了枕磁。(這是qq剛興起時的聊天功能設(shè)計,也就是我們本篇文章需要實現(xiàn)的簡單的聊天室功能坦胶,下面的內(nèi)容與設(shè)計聊天室無關(guān)透典,但是我覺得還是有必要跟大家介紹清楚如今的qq時怎樣工作的,了解便可)

qq服務(wù)器掛在騰訊的某臺主機上顿苇,相當于起了一個中轉(zhuǎn)站的成分峭咒,這種聊天功能的實現(xiàn)對于客戶端數(shù)量比較少時服務(wù)器端還能接受,但是在客戶端數(shù)量很多時服務(wù)器肯定要癱瘓纪岁。

所以為了減少服務(wù)器端的壓力凑队,需要實現(xiàn)客戶端和客戶端之間的直接通信,這樣客戶端上的qq既要實現(xiàn)服務(wù)器端的功能(用于接收信息)又要實現(xiàn)客戶端的功能(用于發(fā)送信息)幔翰。此時qq服務(wù)器就不再作為一個中轉(zhuǎn)站的功能了漩氨,它主要用于:用于客戶端程序登陸,驗證用戶名密碼,獲取其他在線好友信息等等。

分析了功能后接下來進行我們服務(wù)器端和客戶端代碼的編寫遗增,首先創(chuàng)建一個ChatRoom.java叫惊,運行后用于開啟服務(wù)器和客戶端的連接,代碼如下:

待更新做修。

待更新霍狰。

2018.3.19更

歡迎加入我的Java交流1群:659957958抡草。群里目前已有1800人,每天都非痴崤鳎活躍康震,但為了篩選掉那些不懷好意的朋友進來搞破壞,所以目前入群方式已改成了付費方式宾濒,你只需要支付9塊錢腿短,即可獲取到群文件中的所有干貨以及群里面各位前輩們的疑惑解答;為了鼓勵良好風氣的發(fā)展绘梦,讓每個新人提出的問題都得到解決橘忱,所以我將得到的入群收費收入都以紅包的形式發(fā)放到那些主動給新手們解決疑惑的朋友手中。在這里卸奉,我們除了談技術(shù)鹦付,還談生活、談理想择卦;在這里敲长,我們?yōu)槟愕膶W(xué)習(xí)方向指明方向,為你以后的求職道路提供指路明燈秉继;在這里祈噪,我們把所有好用的干貨都與你分享。還在等什么尚辑,快加入我們吧辑鲤!

2018.4.21更:如果群1無法加入,請加Java學(xué)習(xí)交流2群:305335626 杠茬。群2作為群1的附屬群月褥,除了日常的技術(shù)交流、資料分享瓢喉、學(xué)習(xí)方向指明外宁赤,還會在每年互聯(lián)網(wǎng)的秋春招時節(jié)在群內(nèi)發(fā)布大量的互聯(lián)網(wǎng)內(nèi)推方式,話不多說栓票,快上車吧决左!

5.聯(lián)系

If you have some questions after you see this article,you can tell your doubts in the comments area or you can find some info by clicking these links.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市走贪,隨后出現(xiàn)的幾起案子佛猛,更是在濱河造成了極大的恐慌,老刑警劉巖坠狡,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件继找,死亡現(xiàn)場離奇詭異,居然都是意外死亡逃沿,警方通過查閱死者的電腦和手機婴渡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進店門漩勤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人缩搅,你說我怎么就攤上這事〈ビ祝” “怎么了硼瓣?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長置谦。 經(jīng)常有香客問我堂鲤,道長,這世上最難降的妖魔是什么媒峡? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任瘟栖,我火速辦了婚禮,結(jié)果婚禮上谅阿,老公的妹妹穿的比我還像新娘半哟。我一直安慰自己,他們只是感情好签餐,可當我...
    茶點故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布寓涨。 她就那樣靜靜地躺著,像睡著了一般氯檐。 火紅的嫁衣襯著肌膚如雪戒良。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天冠摄,我揣著相機與錄音糯崎,去河邊找鬼。 笑死河泳,一個胖子當著我的面吹牛沃呢,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播拆挥,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼樟插,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了竿刁?” 一聲冷哼從身側(cè)響起黄锤,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎食拜,沒想到半個月后鸵熟,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡负甸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年流强,在試婚紗的時候發(fā)現(xiàn)自己被綠了痹届。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡打月,死狀恐怖队腐,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情奏篙,我是刑警寧澤柴淘,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站秘通,受9級特大地震影響为严,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜肺稀,卻給世界環(huán)境...
    茶點故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一第股、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧话原,春花似錦夕吻、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至改备,卻和暖如春控漠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背悬钳。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工盐捷, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人默勾。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓碉渡,卻偏偏與公主長得像,于是被迫代替她去往敵國和親母剥。 傳聞我的和親對象是個殘疾皇子滞诺,可洞房花燭夜當晚...
    茶點故事閱讀 44,611評論 2 353

推薦閱讀更多精彩內(nèi)容

  • Socket編程 1基礎(chǔ)知識 協(xié)議 端口號(辨別不同應(yīng)用) TCP/IP協(xié)議 是目前世界上應(yīng)用最廣泛的協(xié)議是以TC...
    __豆約翰__閱讀 1,091評論 0 3
  • 《相聚正能量》 四十三年彈指間, 同窗友情心相連环疼。 帥哥高鐵飛似箭习霹, 亮妹空中飄如燕。 相聚路途雖遙遠炫隶, 教你開事...
    我健康平安閱讀 251評論 1 0
  • 致化學(xué):你的微笑是原子間最美麗的記號 是在一個略顯悶熱的夏天淋叶,第一次認識化學(xué),那時的我?guī)в幸环N小心翼翼的感覺伪阶,以為...
    我是獨秀他爸爸閱讀 406評論 4 4
  • 我們繼續(xù)看《和弦》第二到第四小節(jié): 我走到街上喧囂被擋在紅燈后面影子扇形般打開腳印歪歪斜斜安全島孤零零的海很遙遠 ...
    牛泳書海閱讀 944評論 2 4