Socket通信入門之基于TCP

Socket本質(zhì)上就是Java封裝了傳輸層上的TCP協(xié)議(注:UDP用的是DatagramSocket類)炸渡。要實現(xiàn)Socket的傳輸坎拐,需要構(gòu)建客戶端和服務(wù)器端。另外岗喉,傳輸?shù)臄?shù)據(jù)可以是字符串和字節(jié)。

基于TCP

首先看一下通信模型:


通信模型

簡單概括就是以下幾個步驟:

  • 創(chuàng)建ServerSocket和Socket
  • 打開連接到Socket的輸入/輸出流
  • 按照協(xié)議對Socket進行讀寫操作
  • 關(guān)閉輸入輸出流,關(guān)閉Socket

下面會通過實現(xiàn)一個登錄的功能來介紹以上幾個步驟
項目運行的時候服務(wù)器的代碼放在ServerApp里,客戶端的代碼放在ClientApp里,先運行ServerApp,又運行ClientApp.兩個app都在一個手機里面
服務(wù)器
完成以上需求可以遵循如下幾個步驟:

  • 創(chuàng)建ServerSocket對象,綁定監(jiān)聽端口
  • 通過accept()方法監(jiān)聽客戶端請求
  • 建立連接后,通過輸入流讀取客戶端發(fā)送的請求信息
  • 通過輸出流向客戶端發(fā)送響應(yīng)信息
  • 關(guān)閉相關(guān)資源
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        new Thread() {
            @Override
            public void run() {
                ServerSocket serverSocket = null;
                Socket socket = null;
                try {
                    //步驟①創(chuàng)建ServerSocket對象,綁定監(jiān)聽端口
                    //參數(shù)port 我們要指定在1024-65535
                    //如果端口重復(fù),也就是有其他的應(yīng)用使用,會報異常
                    serverSocket = new ServerSocket(43211);

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

                int count = 0;
                System.out.println("服務(wù)器端準備好了鏈接...");
                //無線循環(huán)接受客戶端的連接,也就是允許多個客戶端連接,當然可以通過ServerSocket的構(gòu)造方法對連接數(shù)做一個限制
                //比如最大連接20,當超過20個連接后會報異常
                while (true) {
                    try {
                        //步驟②通過accept()方法監(jiān)聽客戶端請求
                        //這個socket是客戶端與服務(wù)器通信的socket
                        //執(zhí)行accept方法之后,服務(wù)器處于監(jiān)聽狀態(tài),隨時與客戶端連接
                        socket = serverSocket.accept();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    ServerThread serverThread = new ServerThread(socket);
                  //設(shè)置優(yōu)先級為4(默認為5),為了保證程序運行的速度,適當降低該線程的優(yōu)先級
                    serverThread.setPriority(4);
                    serverThread.start();

                    //記錄有多少個連接
                    count++;
                    System.out.println("當前連接數(shù)" + count);
                }
            }
        }.start();

    }
}
public class ServerThread extends Thread {
    Socket socket = null;

    public ServerThread(Socket socket) {
        this.socket = socket;
    }
    @Override
    public void run() {
        InputStream is = null;
        InputStreamReader isr = null;
        BufferedReader br = null;
        OutputStream os = null;
        PrintWriter pw = null;
        try {
            String info = null;
            StringBuilder stringBuilder = new StringBuilder();
            //步驟③建立連接后,通過輸入流讀取客戶端發(fā)送的請求信息
            //獲取輸入流
            is = socket.getInputStream();
            isr = new InputStreamReader(is);
            br = new BufferedReader(isr);
            while ((info = br.readLine()) != null) {
                stringBuilder.append(info);
            }
            socket.shutdownInput();
           /* try {
                Thread.sleep(6*1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }*/
            String[] split = stringBuilder.toString().split(":");
            String name = split[0];
            String password = split[1];
            //步驟④通過輸出流向客戶端發(fā)送響應(yīng)信息
            os = socket.getOutputStream();
            pw = new PrintWriter(os);
            //模擬驗證賬號密碼是否正確
            if ("admin".equals(name) && "123456".equals(password)) {
                pw.write("login success!");
            } else {
                pw.write("login failed!!!");
            }
            pw.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //步驟⑤關(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 (is != null)
                    is.close();
                if (socket != null)
                    socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

客戶端
客戶端布局:

需要以下幾個步驟:

  • 創(chuàng)建Socket對象,指明需要連接的服務(wù)器的地址和端口號
  • 連接建立后,通過輸出流向服務(wù)器發(fā)送請求信息
  • 通過輸入流獲取服務(wù)器的響應(yīng)信息
  • 關(guān)閉相關(guān)資源
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private EditText et_name;
    private EditText et_password;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.bt_con).setOnClickListener(this);
        et_name = findViewById(R.id.et_name);
        et_password = findViewById(R.id.et_password);

    }
    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.bt_con:
                final String name = et_name.getText().toString();
                final String password = et_password.getText().toString();
                if (name==null || TextUtils.isEmpty(name)){
                    Toast.makeText(this,"用戶名不能為空",Toast.LENGTH_SHORT).show();
                    return;
                }
                if (password==null || TextUtils.isEmpty(password)){
                    Toast.makeText(this,"密碼不能為空",Toast.LENGTH_SHORT).show();
                    return;
                }
                new Thread(){
                    @Override
                    public void run() {
                        try {
                            //步驟①創(chuàng)建Socket對象,指明需要連接的服務(wù)器的地址和端口號
                            //host指的是服務(wù)器的地址,因為服務(wù)器和客戶端裝在一個手機上,
                            Socket socket=new Socket("localhost", 43211);
                            //步驟②連接建立后,通過輸出流向服務(wù)器發(fā)送請求信息
                            OutputStream os=socket.getOutputStream();
                            PrintWriter pw=new PrintWriter(os);
                            //寫入用戶名和密碼
                            pw.write(name);
                            //用:來分割用戶名和密碼
                            pw.write(":");
                            pw.write(password);
                            pw.flush();
                            socket.shutdownOutput();
                             /*//注意!!!!
                            //上面為了簡單寫用的是字符串,實際開發(fā)中用的對象較多,也就是如下寫法
                            ObjectOutputStream oos = new ObjectOutputStream(os);
                            User user = new User(name,password);
                            oos.writeObject(user);*/


                            //設(shè)置超時時間,如果在5s沒沒有收到服務(wù)器的返回,則異常
                            socket.setSoTimeout(1000*5);
                            //步驟③通過輸入流獲取服務(wù)器的響應(yīng)信息
                            InputStream is=socket.getInputStream();
                            BufferedReader br=new BufferedReader(new InputStreamReader(is));
                            String info=null;
                            while((info=br.readLine())!=null){
                                System.out.println(info);
                            }
                            //步驟④關(guān)閉相關(guān)資源
                            br.close();
                            is.close();
                            pw.close();
                            os.close();
                            socket.close();
                        } catch (UnknownHostException e) {
                            e.printStackTrace();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }.start();
                break;
        }
    }
}

下一篇,基于UDP:http://www.reibang.com/p/20b2d7688cb7

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末炸庞,一起剝皮案震驚了整個濱河市钱床,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌埠居,老刑警劉巖查牌,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異滥壕,居然都是意外死亡纸颜,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門绎橘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來胁孙,“玉大人,你說我怎么就攤上這事′探希” “怎么了稠鼻?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長狂票。 經(jīng)常有香客問我枷餐,道長,這世上最難降的妖魔是什么苫亦? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任毛肋,我火速辦了婚禮,結(jié)果婚禮上屋剑,老公的妹妹穿的比我還像新娘润匙。我一直安慰自己,他們只是感情好唉匾,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布孕讳。 她就那樣靜靜地躺著,像睡著了一般巍膘。 火紅的嫁衣襯著肌膚如雪厂财。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天峡懈,我揣著相機與錄音璃饱,去河邊找鬼。 笑死肪康,一個胖子當著我的面吹牛荚恶,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播磷支,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼谒撼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了雾狈?” 一聲冷哼從身側(cè)響起廓潜,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎善榛,沒想到半個月后辩蛋,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡锭弊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年堪澎,在試婚紗的時候發(fā)現(xiàn)自己被綠了擂错。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片味滞。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出剑鞍,到底是詐尸還是另有隱情昨凡,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布蚁署,位于F島的核電站便脊,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏光戈。R本人自食惡果不足惜哪痰,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望久妆。 院中可真熱鬧晌杰,春花似錦、人聲如沸筷弦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽烂琴。三九已至爹殊,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間奸绷,已是汗流浹背梗夸。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留号醉,地道東北人绒瘦。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像扣癣,于是被迫代替她去往敵國和親惰帽。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344