Java網絡編程以及簡單的聊天程序

網絡編程技術是互聯網技術中的主流編程技術之一,懂的一些基本的操做是很是必要的。這章主要講解網絡編程,UDP和Socket編程,以及使用Socket作一個簡單的聊天軟件。html

所有代碼下載:連接java

1.網絡編程簡要概述:

    網絡編程實質實質就是兩個(或多個)設備(例如計算機)之間的數據傳輸。而要實現兩臺計算機經過互聯網鏈接進行數據傳輸,必輸要知足計算機網絡的5層協議(物理層,數據鏈路層,網絡層,運輸層,應用層);固然有劃分可能不一樣,但如今你們比較容易接受理解的是五層模型。而其中前三層物理層,數據鏈路層以及網絡層,做爲java程序員暫時是不能直接對他進行控制的。而本章講的網絡編程是處在運輸層和應用層中。對與計算機網絡,能夠參看個人博客互聯網協議入門程序員

  1. 運輸層主要有兩種協議TCP和UDP,TCP經過握手協議進行可靠的鏈接,UDP則是不可靠鏈接。
  2. 應用層主要就是應用層直接和應用程序接口並提供常見的網絡應用服務,主要協議有HTTP,FTP,DNS等。
  3. IP地址:用與標記一臺計算機的身份證,此去須要注意的是網絡地址轉換技術NAT的存在,咱們的我的計算機基本上都是專用網絡IP(192.168..),這個IP只能本地電腦訪問。只有當咱們有公網IP後,才能使本身的電腦被世界全部鏈接在互聯網中的電腦訪問到。
  4. 服務端口:計算機經過不一樣的服務端口來區分不一樣網絡服務,就像經過進程號區分不一樣的進程同樣;常見服務的端口,HTTP(80),FTP(21).
  5. URL:統一資源定位符。只能定位互聯網資源。
URL基本格式:
protocol://hostname:port/resourcename#anchor
protocol:使用的協議,能夠是http,ftp,news,telnet等
hostname:主機名
port:端口號,可選
resourcename:資源名,主機上能訪問到的目錄或文件
anchor:標記,可選,指定文件中的有特定標記的位置
如:
http://localhost:8080/HttpSer/index.html。

下面主要經過UDP和socket編程來說解網絡編程;編程

2. UDP編程介紹:

  1. 簡介:
    UDP(User Datagram Protocol),中文意思是用戶數據報協議,方式相似於發短信息,是一種物美價廉的通信方式,使用該種方式無需創建專用的虛擬鏈接,因爲無需創建專用的鏈接,因此對於服務器的壓力要比TCP小不少。是一種儘量可靠傳輸的協議。主要用於視頻電話等對信息準確傳輸性不高追求速度的應用程序。
  2. 主要類的講解:
    DatagramSocket:
            DatagramSocket類實現「網絡鏈接」,包括客戶端網絡鏈接和服務器端網絡鏈接。雖然UDP方式的網絡通信不須要創建專用的網絡鏈接,可是畢竟仍是須要發送和接收數據,DatagramSocket實現的就是發送數據時的發射器,以及接收數據時的監聽器的角色
    DatagramPacket:
            DatagramPacket類實現對於網絡中傳輸的數據封裝,也就是說,該類的對象表明網絡中交換的數據。在UDP方式的網絡編程中,不管是須要發送的數據仍是須要接收的數據,都必須被處理成DatagramPacket類型的對象,該對象中包含發送到的地址、發送到的端口號以及發送的內容等。
  3. 一個簡單的udp通訊:
    客戶端:
DatagramSocket ds = null;
            //定義一個UDP來發送數據
            ds = new DatagramSocket();
            //假設發送的數據是個字符串
            String hello = "hello world";
            //定義一個UDP的數據發送包來發送數據,inetSocketAddress表示要接收的地址
            DatagramPacket dp = new DatagramPacket(hello.getBytes()
                        ,hello.getBytes().length,new InetSocketAddress("127.0.0.1", 9999));
            for(int i=0;i<10;i++) {
    //數據發送
                ds.send(dp);
    //線程睡眠1s
                Thread.sleep(1000);
            }

服務端:服務器

DatagramSocket ds = null;
        //UDP接受端鏈接
        ds = new DatagramSocket(9999);
        //定義將UDP的數據包接收到什麼地方
        byte[] buf = new byte[1024];
        //定義UDP的數據接收包
        DatagramPacket dp = new DatagramPacket(buf, buf.length);
        while(true) {
            //接收數據包
            ds.receive(dp);
  //將數據轉換輸出:
            String str = new String(dp.getData(),0,dp.getLength());
            System.out.println(str);
        }

3.socket編程介紹:

  1. socket簡介:
    Socket是TCP/IP協議的一個十分流行的編程界面,一個Socket由一個IP地址和一個端口號惟一肯定。
    而TCP是Tranfer Control Protocol的 簡稱,是一種面向鏈接的保證可靠傳輸的協議。經過TCP協議傳輸,獲得的是一個順序的無差錯的數據流。發送方和接收方的成對的兩個socket之間必須創建鏈接,以便在TCP協議的基礎上進行通訊,當一個socket(一般都是server socket)等待創建鏈接時,另外一個socket能夠要求進行鏈接,一旦這兩個socket鏈接起來,它們就能夠進行雙向數據傳輸,雙方均可以進行發送 或接收操做。
  2. 主要的類介紹:
    1.創建服務器類:ServerSocket
    能夠用服務器須要使用的端口號做爲參數來建立服務器對象:
    ServerSocket serverSocket = new ServerSocket(8888)
    這條語句建立了一個服務器對象,這個服務器使用8888號端口。當一個客戶端程序創建一個Socket鏈接,所鏈接的端口號爲8888時,服務器對象server便響應這個鏈接,而且server.accept()方法會建立一個Socket對象。服務器端即可以利用這個Socket對象與客戶進行通信。
//進行監聽,當客戶端沒數據發送過來時,停在這裏;
    Socket s=serverSocket.accept();

2.通訊類:Socket
服務器端和客戶端能夠經過Socket類進行通訊:
客戶端創建socket類過程:網絡

//創建socket客戶端:第一個參數:ip地址;第二個參數:發送的端口,假如沒有服務器,會停在這裏,而後扔出異常;
    Socket socket=new Socket("192.168.1.105", 8888);

經過socket類能夠得到輸入輸出流進行通訊:socket

//得到socket的輸出流
PrintWriter out=new PrintWriter(socket.getOutputStream());
            //得到socket的輸入流
BufferedReader in=new BufferedReader(new InputStreamReader(socket.getInputStream()));
  1. 一個簡單的Socket通訊:
    客戶端:
public static void main(String[] args) {
        Socket socket=null;
        PrintWriter out=null;
        BufferedReader in=null;
        try {
            //創建socket客戶端:第一個參數:ip地址;第二個參數:發送的端口,假如沒有服務器,會停在這裏,而後扔出異常;
            socket=new Socket("192.168.1.105", 8888);
            //得到本機分配的當地socket端口
            System.out.println("當地端口:"+socket.getLocalPort());
            //得到本機分配的當地socket端口
            System.out.println("遠程端口:"+socket.getPort());
            //得到socketAddress
            System.out.println("遠程adress:"+socket.getRemoteSocketAddress());
            System.out.println("本地adress:"+socket.getLocalSocketAddress());
            //得到inetAddress
            System.out.println("遠程inetAddress:"+socket.getInetAddress());
            System.out.println("本地inetAddress:"+socket.getLocalAddress());
            //得到socket的輸出流
            out=new PrintWriter(socket.getOutputStream());
            //得到socket的輸入流
            in=new BufferedReader(new InputStreamReader(socket.getInputStream()));
            //發送數據
            out.println("peace");
            //刷新纔會當即發送
            out.flush();
              //接收數據
               String str=null;
               //此去會一直等待服務端發送一個數據;
                str=in.readLine();
                System.out.println("收到:"+str);
            
            
        } catch (UnknownHostException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }finally {
            //關閉鏈接
            if(socket!=null){
                try {
                    socket.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }

服務端:ide

public static void main(String[] args) {
        ServerSocket serverSocket=null;
        PrintWriter out=null;
        BufferedReader in=null;
        Socket s=null;
        int i=0;
        try {
            //服務器的建立:建立相應的serversocket
            serverSocket=new ServerSocket(8888);
            while(true){
                try {
                    //進行監聽,當客戶端沒數據發送過來時,停在這裏;
                    s=serverSocket.accept();
                    //得到本機分配的當地socket端口
                    System.out.println("當地端口:"+s.getLocalPort());
                    //得到本機分配的當地socket端口
                    System.out.println("遠程端口:"+s.getPort());
                    //得到socketAddress
                    System.out.println("遠程adress:"+s.getRemoteSocketAddress());
                    System.out.println("本地adress:"+s.getLocalSocketAddress());
                    //得到inetAddress
                    System.out.println("遠程inetAddress:"+s.getInetAddress());
                    System.out.println("本地inetAddress:"+s.getLocalAddress());
                    //得到socket的輸出流,關閉socket時,會自動關閉socket的輸出輸入留
                    out=new PrintWriter(new OutputStreamWriter(s.getOutputStream()));
                    //得到socket的輸出流,關閉socket時,會自動關閉socket的輸出輸入留
                    in=new BufferedReader(new InputStreamReader(s.getInputStream()));
                    //將客戶端發過來的數據輸出:
                    String str=null;
                     str=in.readLine();
                        System.out.println("收到"+str);
                    
                    out.println("hello"+i++);
                    
                    out.flush();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }finally {
                    if(s!=null){
                        s.close();
                    }
                }
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        finally {
            if(serverSocket!=null){
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }

4.利用socket創建簡單聊天程序:

該聊天進程能夠實現指定好友聊天,和羣聊。只是簡單的演示。函數

  1. 客戶端
    線程數:2個,一個主線程用於發送消息,一個線程用於接收服務器發送過來的消息
    單聊實現:經過single標誌進行判斷。
    退出實現:控制擡輸入:quit就會提示退出
    單聊退出實現:當進入單聊後,控制檯輸入q就退出單聊
  2. 服務端:
    線程數:由客戶端決定,當客戶端鍵入名字以後就啓動一個線程用於處理客戶端的各類請求,以及發送數據。將全部線程放入map集合,鍵爲用戶名,值爲線程對象;
    單聊實現:當收到客戶端的singl標誌後使sin爲true實現單聊,同時得到指定用戶的線程進行發送數據。
    退出實現:當接收到客戶端發送的quit時,向客戶端發送disconnect斷開鏈接,關閉線程
    單聊退出:將單聊標誌置爲false就行;
  3. 客戶端代碼以下:
package com.rlovep.clinet;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
/**
 * 
* @ClassName: TalkClinet
* @Description: 聊天客戶端:
* @author peace w_peace@163.com 
* @date 15 Oct 2015 7:21:22 pm
*
 */
public class TalkClinet {
    // 得到控制檯輸入
    private BufferedReader sysin = null;
    // socket
    private Socket s = null;
    private BufferedReader in = null;
    private PrintWriter out = null;
    // 線程中止flag
    private boolean flag = true;
    //單聊標誌
    private final static String single="single";
    
    public static void main(String[] args) {
        // 啓動客戶端:
        new TalkClinet().start();
    }
    /**
     * 
    * @Title: start 
    * @Description: 主要做用:啓動發送和接收任務  
    * @return:void   
    * @throws 
    * @author peace w_peace@163.com
     */
  public void  start(){
     try {
            // 得到系統輸入:
            sysin = new BufferedReader(new InputStreamReader(System.in, "utf-8"));
            // 設置名字:
            System.out.println("請輸入用戶名:");
            String name = sysin.readLine();
            // 創建客戶端socket
            s = new Socket("127.0.0.1", 8888);
            // 得到socket輸出out
            out = new PrintWriter(s.getOutputStream(), true);
            out.println(name);
            // 得到socket輸入 in
            in = new BufferedReader(new InputStreamReader(s.getInputStream()));
            // 創建接收線程;緣由獲取系統輸入會阻塞主線程
            new Thread(new ClinetThread()).start();
            // 發送消息:
            String str = null;
            while (flag && ((str = sysin.readLine()) != null)) {
                //判斷是否爲單聊標誌。若是不是單聊,就羣發消息
                if(single.equalsIgnoreCase(str)){
                    System.out.println("請輸入想跟誰聊 :");
                    //獲取系統輸入:
                    if(flag && ((str = sysin.readLine()) != null))
                        {
                              //發送單聊標誌
                             out.println(single) ;
                        }
                }
                //向服務端發送內容:
                out.println(str);
            }
        
     } catch (UnsupportedEncodingException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    
     finally{
         //關閉資源
         if(sysin!=null){
             try {
                sysin.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
         }
         if(s!=null){
             try {
                s.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
         }
     }
  }
 private class ClinetThread implements Runnable{
    /**
     * 
    * @Title: recive 
    * @Description: 接收消息,當消息爲disconnect時退出,此去須要在按下一次回車用來終止系統輸入; 
    * @return:void   
    * @throws 
    * @author peace w_peace@163.com
     */
     private void recive(){
        try {
            //接收服務端消息
            String str=in.readLine();
            if(str!=null){
            //若是是結束聊天,就退出線程
            if("disconnect".equals(str)){
                stop();
                System.out.println("回車退出:");
            }
            else 
            {
                //不然顯示消息
              System.out.println(str);    
            }
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
     }
     //線程中止方法
     public void  stop()
     {
         flag=false;
     }
     //線程主要任務
    @Override
    public void run() {
        while(flag){
            //接收消息函數調用
            recive();
        }
    } 
 }
}

4.服務端代碼以下:ui

package com.rlovep.server;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.HashMap;
import java.util.Map;


public class TalkServer {
    //保存聊天線程:
    private Map<String, ServerThread> map = new HashMap<String, ServerThread>();
    
    
    public static void main(String[] args) {
        //啓動服務器
        new TalkServer().start();
    }
    /**
     * 
    * @Title: start 
    * @Description: 爲每個客戶端建立一個獨立線程 
    * @return:void   
    * @throws 
    * @author peace w_peace@163.com
     */
    public void start(){
        //服務器servesocket
        ServerSocket serverSocket=null;
        //服務器socket
        Socket socket=null;
        try {       
            //服務器建立
            serverSocket=new ServerSocket(8888);
            //死循環,用於接收更多的客戶端
            while(true){
                //聊天線程建立
            socket=serverSocket.accept();
            //爲每個客戶端創建一個服務線程。
            ServerThread st=new ServerThread(socket);
            new Thread(st).start();
            }
            
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        finally{
            //釋放資源
            try {
                if(serverSocket!=null){
                    serverSocket.close();
                }
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
    /**
     * 
    * @ClassName: ServerThread
    * @Description: 聊天線程
    * @author peace w_peace@163.com 
    * @date 15 Oct 2015 8:31:44 pm
    *
     */
    private class ServerThread implements Runnable{
        //相應的socket
        private Socket socket=null;
        private BufferedReader in=null;
        private PrintWriter out=null;
        //每一個聊天的名字
        private  String name=null;
        //聊天頭
        private String meshead=null;
        //中止線程標誌位
        private boolean flag=true;
        //單聊標誌位:
        private boolean sin=false;
        /**
         * 
        * <p>Title:線程構造器 </p>
        * <p>Description:用來建立線程,以及初始化,和將線程加入map </p>
        * @param socket
        * @throws IOException
         */
        public ServerThread(Socket socket) throws IOException {
            //得到socket
            this.socket=socket;
            //得到輸出和輸入
            out=new PrintWriter(socket.getOutputStream(),true);
            //得到用戶名字
            in=new BufferedReader(new InputStreamReader(socket.getInputStream()));
            name=in.readLine();
            System.out.println(name+"上線了");
            //製做聊天頭
            meshead=name+"["+socket.getInetAddress().getHostAddress()+":"+socket.getPort()+"]";
            //將線程加入map:key爲名字;value爲線程
            map.put(name, this);
            //提醒全部用戶
            send(meshead+"上線了");
        }
        /**
         * 
        * @Title: send 
        * @Description: 發送消息(羣發)
        * @param msg 
        * @return:void   
        * @throws 
        * @author peace w_peace@163.com
         */
        public void send(String msg){
            //迭代羣發
            for(ServerThread thread:map.values()){
                thread.out.println(msg);
            }
        }
        /**
         * 
        * @Title: Receiver 
        * @Description: 接收消息,並轉發
        * @throws IOException 
        * @return:void   
        * @author peace w_peace@163.com
         */
        public void Receiver() throws IOException{
            String str=null;
            ServerThread qq=null;
            //接收消息
            while((str=in.readLine())!=null){
                //若是消息爲quit則退出
                if("quit".equalsIgnoreCase(str)){
                    stop();
                    //給客戶端發送關閉連接命令
                    out.println("disconnect");
                    break;
             }
             //判斷是否爲單聊
                if(sin==false)
                {
                   if(str.equals("single")){
                       //若是爲單聊就得到想要跟誰聊
                    if((str=in.readLine())!=null){
                         //得到另一個客戶端線程
                         qq=map.get(str);
                        if(qq!=null)
                            sin=true;
                    }
                    }
                   else
                   {
                     //轉發消息  羣發
                        send(meshead+str);  
                   }
                
                }
                //若是是單聊就給對應的客戶端發送消息;
                else{
                    //若是爲q則退出單聊
                    if(str.equals("q")){
                        sin=false;
                        qq.out.println(name+"對話結束");
                    }
                    //不然發送消息
                    else
                       qq.out.println(name+"對你說:"+str);
                    }
            
            }
        }
        /**
         * 
        * @Title: stop 
        * @Description: 中止函數
        * @return:void   
        * @throws 
        * @author peace w_peace@163.com
         */
        public void stop(){
            flag=false;
            //下線移去碼map;
            map.remove(name);
            send(meshead+"已經下線了");
        }
        /**
         * run方法
         */
        @Override
        public void run() {
        
            try {
                while(flag){
                    //不停接收消息
                    Receiver();
                }
            }
            catch(SocketException e){
                stop();//客戶端直接關閉引起的錯誤;
            }
            catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            finally{
                //釋放資源
                try {
                    if(socket!=null){
                        socket.close();
                    }
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
        
    }
}

來自一條小鯊魚wpeace(rlovep.com)

相關文章
相關標籤/搜索