日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

Java编程之路——网络编程篇

發(fā)布時(shí)間:2024/3/24 java 61 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java编程之路——网络编程篇 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

網(wǎng)絡(luò)編程

  • 關(guān)注的是底層數(shù)據(jù)的傳輸;
  • 與網(wǎng)頁編程不同,網(wǎng)頁編程是關(guān)注與用戶的數(shù)據(jù)交互。

概念:

  • 網(wǎng)絡(luò):

    將不同區(qū)域的計(jì)算機(jī)連接到一起;區(qū)域(局域網(wǎng),城域網(wǎng),互聯(lián)網(wǎng))

  • 地址:

    IP地址:
    確定網(wǎng)絡(luò)上一個(gè)絕對(duì)地址,位置;——》房子的地址

  • 端口號(hào):
    區(qū)分計(jì)算機(jī)的軟件——》房門 2個(gè)字節(jié) 0-65535 共65536個(gè)
    1、在同一個(gè)協(xié)議下 端口號(hào)不能重復(fù);不同協(xié)議下可以重復(fù)
    2、1024以下的不要使用,給知名廠商預(yù)留的,如80——》 http;21——》ftp 盡量往大了指定

  • 資源定位:
    URL統(tǒng)一資源定位符;URI:統(tǒng)一資源。

  • 數(shù)據(jù)的傳輸

    1.協(xié)議:
    TCP和UDP兩種協(xié)議
    1)TCP:面向連接,安全可靠;電話 類似于三次握手;效率低
    2)UDP:非面向連接,效率高;短信

    2.傳輸數(shù)據(jù):
    1)先封裝
    2)后拆封

  • 在Java中所涉及的類主要包括:=========》類

  • IP(地址,端口):InetAddress、netSocketAddress
  • URL
  • TCP: ServerSocket 、Socket
  • UDP: DatagramSocket 、 DatagramPacket
  • Java中相關(guān)類的學(xué)習(xí)

    網(wǎng)絡(luò)編程相關(guān)的類與接口位于java.net包中

    一、地址與端口
    InetAddress類,封裝計(jì)算機(jī)的IP地址和域名解析DNS,但是沒有端口

    需掌握的有:
    該類沒有公開的構(gòu)造器,所以構(gòu)造對(duì)象應(yīng)使用本身提供的靜態(tài)方法。
    1)靜態(tài)方法獲取對(duì)象:
    InetAddress.getLocalHost();產(chǎn)生一個(gè)本機(jī)的InetAddress對(duì)象
    InetAddress.getByName(“IP地址或者域名”);通過IP或域名產(chǎn)生一個(gè)InetAddress對(duì)象
    2)調(diào)用對(duì)象的方法:
    getHostAddress();返回對(duì)象的IP地址
    getHostName();返回對(duì)象的域名,若無或者不能解析,則返回IP地址

    InetSocketAddress類,封裝端口,在InetAddress類基礎(chǔ)上 + 端口
    1)含有對(duì)外構(gòu)造器,故用構(gòu)造方法產(chǎn)生對(duì)象:
    InetSocketAddress(String hostname, int port);主機(jī)名與端口號(hào)構(gòu)造對(duì)象
    InetSocketAddress(InetAddress addr, int port);IP地址與端口號(hào)構(gòu)造對(duì)象
    2)調(diào)用對(duì)象的方法:
    getAddress();返回IP地址IntAddress;
    getHostName();返回域名hostname
    getPort();返回端口號(hào)

    構(gòu)造端口的例子:

    1.InetSocketAddress address = new InetSocketAddress(“127.0.0.1”,9999);
    等價(jià)于:
    2. Address = new InetSocketAddress(InetAddress.getByName(“127.0.0.1”),9999);

    因?yàn)榈谝环N方法的源碼中就存在著將字符轉(zhuǎn)化成為IP地址InetAddress的代碼;故這兩種方法是等價(jià)的。

    二、 資源定位

    URL包含四部分:協(xié)議+域名+端口號(hào)+資源文件名(/之后開始算,是相對(duì)路徑)

    URL類:
    表示指向互聯(lián)網(wǎng)資源的指針
    1)創(chuàng)建對(duì)象:
    URL(String spec);絕對(duì)路徑構(gòu)建
    URL(URL context, String spec);相對(duì)路徑構(gòu)建
    2)方法:
    getProtocol();返回協(xié)議
    getHost();返回域名
    getPort();返回端口號(hào)
    getFile();返回資源
    getPath();獲取相對(duì)路徑
    getRef();返回錨點(diǎn)
    getQuery();返回參數(shù);在有錨點(diǎn)情況下返回null;無錨點(diǎn)則返回正確
    3)資源流
    openStream();返回一個(gè)輸入流InputStream

    構(gòu)建URL與使用方法的一個(gè)例子:
    1.絕對(duì)URL

    url = new URL(“http://www.baidu.com:80/index.html#aa?uname=bjsxt”);

    2.相對(duì)URL

    url = new URL(“http://www.baidu.com:80/a/”); url = URL(url, “b.txt”); System.out.println(url.toString()); //http://www.baidu.com:80/a/b.txt 對(duì)第一種構(gòu)建對(duì)象使用方法: System.out.println(“協(xié)議:”+ url.getProtocol()); //協(xié)議:http System.out.println(“域名:”+ url.getHost()); //域名:www.baidu.com System.out.println(“端口:”+ url.getPort()); //端口:80 System.out.println(“資源:”+url.getFile());//資源:index.html#aa?uname=bjsxt System.out.println(“相對(duì)路徑:”+url.getPath()); //相對(duì)路徑:index.html System.out.println(“錨點(diǎn):”+url.getRef()); // 錨點(diǎn):aa?uname=bjsxt System.out.println(“參數(shù):”+url.getQuery()); //參數(shù):有錨點(diǎn)返回null,若無錨點(diǎn)則返回uname=bjsxt

    爬蟲原理的一個(gè)例子:(爬蟲第一步,先獲取網(wǎng)頁數(shù)據(jù))

    public static void main(String[] args){URL url = new URL(“http://www.baidu.com”); //主頁,默認(rèn)資源//獲取資源,網(wǎng)絡(luò)流/* //直接這樣讀取的主頁數(shù)據(jù)會(huì)存在亂碼,主要是由于編碼與解碼的字符集不統(tǒng)一造成的,所以可以用轉(zhuǎn)換流InputStream is = url.openStream();byte[] flush = new byte[1024];int len = 0;while((len=is.read(flush))!= -1){System.out.println(new String(flush,0,len));}is.close();*///使用轉(zhuǎn)換流BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream(),”utf-8”));//也可以再定義寫入流將讀取到的內(nèi)容直接寫到文件中BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(baidu.html),”utf-8”));String msg = null;while((msg=br.readLine())!=null){System.out.println(msg); //直接打印出來內(nèi)容bw.append(msg);bw.newLine();}bw.flush();bw.close();br.close(); }

    三、UDP傳輸數(shù)據(jù)

    UDP是以數(shù)據(jù)為中心的,非面向連接的,不安全,但效率高
    根據(jù)數(shù)據(jù)傳輸?shù)膶?duì)象可分為客戶端與服務(wù)器端;

    在java種主要涉及到***DatagramSocket類***以及***DatagramPacket類***,其傳輸步驟分別為:
    1.客戶端
    1)創(chuàng)建客戶端對(duì)象,DatagramSocket類 + 指定端口
    2)準(zhǔn)備數(shù)據(jù) 字節(jié)數(shù)組
    3)打包 DatagramPacket類 + 服務(wù)器地址及端口
    4)發(fā)送數(shù)據(jù)
    5)釋放資源

    2.服務(wù)器端
    1)創(chuàng)建服務(wù)器對(duì)象,DatagramSocket類 + 指定端口
    2)準(zhǔn)備接收容器 字節(jié)數(shù)組 封裝 DatagramPacket類
    3)包 接收數(shù)據(jù)
    4)分析數(shù)據(jù)
    5)釋放資源

    服務(wù)器端與客戶端使用的類是相同的,不過使用的方法有些不同:

    DatagramSocket類 發(fā)送和接收數(shù)據(jù)報(bào)數(shù)據(jù)包的套接字。
    1)創(chuàng)建對(duì)象 均使用DatagramSocket(int port)
    2)方法
    send(DatagramPacket p) 通過數(shù)據(jù)包p發(fā)送數(shù)據(jù)
    receive(DatagramPacket p)從數(shù)據(jù)包p接收數(shù)據(jù)
    close()關(guān)閉套接字,釋放資源

    DatagramPacket類 該類表示數(shù)據(jù)報(bào)包
    數(shù)據(jù)封裝與拆封操作均是DatagramPacket類,
    客戶端打包數(shù)據(jù):

    DatagramPacket(byte[] buf, int offset, int length, SocketAddress address)//此處的SocketAddress是包含了接收端主機(jī)IP和端口的InetSocketAddress

    服務(wù)端準(zhǔn)備包接收數(shù)據(jù):

    DatagramPacket(byte[] buf, int length)//主需要準(zhǔn)備一個(gè)byte接收數(shù)組即可,不需要知道發(fā)送方的信息。

    一般先寫服務(wù)端,再寫客戶端:
    服務(wù)端保存為:MyServer.java

    public static void main(String[] args){//1、創(chuàng)建服務(wù)器端 + 端口DatagramSocket server = new DatagramSocket(8888);//2、準(zhǔn)備接收容器DatagramPacket(byte[] buf, int length)byte[] container = new byte[1024];//3、封裝成包 DatagramPacket(byte[] buf, int length)DatagramPacket packet = new DatagramPacket(container, container.length);//4、接收數(shù)據(jù)server.receive(packet);//5、分析數(shù)據(jù)byte[] data = packet.getData();int len = packet.getLength();System.out.println(new String(data,0,len));//6、釋放資源server.close(); }

    客戶端保存為:MyClient.java

    public static void main(String[] args){//1、創(chuàng)建客戶端 + 端口DatagramSocket client = new DatagramSocket(6666);//2、準(zhǔn)備數(shù)據(jù)String msg = “UDP編程”;byte[] data = msg.getBytes();//3、封裝成包(指定發(fā)送地點(diǎn)及端口)DatagramPacket(byte[] buf, int length, SocketAddress address)DatagramPacket packet = new DatagramPacket(data,data.length,new InetSocketAddress(“l(fā)ocalHost”,8888));//4、發(fā)送數(shù)據(jù)client.send(packet);//5、釋放資源client.close(); }

    上述實(shí)現(xiàn)的功能是將字符數(shù)組從客戶端發(fā)送到服務(wù)端;而如何將其他類型的數(shù)據(jù)發(fā)送呢?
    由于數(shù)據(jù)的傳輸只能一字節(jié)數(shù)組的形式,所以數(shù)據(jù)在打包和拆封時(shí)均需要進(jìn)行相對(duì)應(yīng)的轉(zhuǎn)換;上述其實(shí)已經(jīng)存在String——》byte[]的轉(zhuǎn)換:byte[] data = msg.getBytes();以及相對(duì)應(yīng)的byte[]——》String轉(zhuǎn)換:new String(data,0,len)。

    所以對(duì)于其他類型的數(shù)據(jù),我們發(fā)送前也先要將其轉(zhuǎn)換為byte[]形式,接收后也需要將其轉(zhuǎn)化為原來的形式。

    客戶端的轉(zhuǎn)換
    轉(zhuǎn)換方式double——》byte[] 字節(jié)數(shù)組 數(shù)據(jù)源 + Data輸出流

    public static byte[] convert(double num) throws IOException{byte[] data = null;ByteArrayOutputStream bos = new ByteArrayOutputStream();DataOutputStream dos = new DataOutputStream(bos);dos.writeDouble(num);dos.flush();//獲取數(shù)據(jù)data = bos.toByteArray();dos.close();return data; }

    類型轉(zhuǎn)換結(jié)束后再將客戶端的步驟2修改為:

    double num = 89.12;byte[] data = convert(num);

    服務(wù)端的轉(zhuǎn)換
    轉(zhuǎn)換方式 字節(jié)數(shù)組——》double 字節(jié)數(shù)組+ Data輸入流

    public static double convert(byte[] data) throws IOException {DataInputStream dis = new DataInputStream(new ByteArrayInputStream(data));//獲取數(shù)據(jù)double num = dis.readDouble();dis.close();return num; }

    完成轉(zhuǎn)換后再將服務(wù)端的分析數(shù)據(jù)步驟5修改為:

    double data = convert(packet.getData());

    四、TCP數(shù)據(jù)傳輸

    TCP是面向連接的,安全,可靠,效率低;這個(gè)連接對(duì)服務(wù)端與客戶端是同一個(gè)Socket。
    1.面向連接Request—Response。
    2.Socket編程

    服務(wù)端ServerSocket類
    1)構(gòu)建對(duì)象:ServerSocket(int port)需指明端口
    2)接收客戶端連接:accept()阻塞式接收
    3)發(fā)送數(shù)據(jù);一個(gè)連接Socket相當(dāng)于雙向流,所以可以由Socket中g(shù)etOutputStream()方法獲得輸出流OutputStream;

    客戶端Socket類
    1)創(chuàng)建對(duì)象:Socket(String host, int port)指明服務(wù)器IP及端口;客戶端本身不需要指明端口,由客戶端自動(dòng)分配。
    客戶端建立成功即表示建立了連接,一個(gè)接收accept()只能連接一次
    2)接收數(shù)據(jù):先使用Socket中g(shù)etInputStream()方法獲得輸入流InputStream由于服務(wù)端與客戶端的連接是同一個(gè),所以兩端對(duì)流進(jìn)行包裝時(shí)需要用同樣的種類,即若服務(wù)端用BufferedWriter,則客戶端要用BufferedReader;同樣若服務(wù)端用DataOutputStream,則客戶端也要用DataInputStream

    如何在一個(gè)服務(wù)端創(chuàng)建多個(gè)客戶端的連接?

    若直接在服務(wù)端的接受連接accept()處建立循環(huán),則實(shí)現(xiàn)的功能實(shí)際上是服務(wù)器先與一個(gè)客戶端 建立連接,然后與之通信完畢后再與其他客戶端建立連接。這樣每個(gè)客戶端之間并不是獨(dú)立的,若一個(gè)客戶端的連接出現(xiàn)阻塞式服務(wù),則所有客戶端均需等待。

    實(shí)際上多個(gè)客戶端連接需要的是每個(gè)客戶端都能與服務(wù)器獨(dú)立的發(fā)送,接收數(shù)據(jù);并且客戶端之間也是獨(dú)立的;所以需要服務(wù)器為每一個(gè)客戶端的連接創(chuàng)建一個(gè)線程,且需要為客戶端的發(fā)送與接收分別創(chuàng)建一個(gè)線程。

    簡易聊天室原理:

    先寫服務(wù)器:Server01.java

    public static void main(String[] args){//創(chuàng)建服務(wù)器ServerSocket server = new ServerSocket(9999);//接收服務(wù)端連接Socket client = server.accept();//寫出數(shù)據(jù)//輸入流DataInputStream dis = new DataInputStream(client.getInputStream());String msg = dis.readUTF();//輸出流DataOutputStream dos = new DataOutputStream(client.getOutputStream());dos.writeUTF(“服務(wù)器——>” + msg);dos.flush(); }

    再寫客戶端:Client01.java

    public static void main(String[] args){//創(chuàng)建客戶端Socket client = new Socket(“l(fā)ocalhost”,9999);//創(chuàng)建控制臺(tái)輸入流BufferedReader console = new BufferedReader(new InputStreamReader(System.in));String info = console.readLine();//輸出流DataOutputStream dos = new DataOutputStream(client.getOutputStream());dos.writeUTF(info);//輸入流DataInputStream dis = new DataInputStream(client.getInputStream());String msg = dis.readUTF();System.out.println(msg);}

    在Client01.java中,客戶端只能單次讀入,為了能多次讀入信息并發(fā)送,需要進(jìn)行循環(huán),在循環(huán)中不需要重復(fù)建立流,所以可以將流單獨(dú)出來,將其處理進(jìn)行循環(huán)即可。

    針對(duì)上述聊天室,客戶端只能先讀取控制臺(tái)數(shù)據(jù),然后發(fā)送數(shù)據(jù),最后接收數(shù)據(jù);服務(wù)端只能先接收數(shù)據(jù),然后轉(zhuǎn)發(fā)數(shù)據(jù);即數(shù)據(jù)的收發(fā)并不是獨(dú)立的,所以需要建立線程單獨(dú)控制,如下:

    先建立發(fā)送線程Send.java

    public class Send implements Runnable{//控制臺(tái)輸入流private BufferedReader console;//輸出流private DataOutputStream dos;//線程標(biāo)識(shí)private boolean isRunning = true;//構(gòu)造器public Send(){console = new BufferedReader(new InputStreamReader(System.in));}public Send(Socket client){this.Send();try{dos = new DataOutputStream(client.getOutputStream());}catch(IOException e){isRunning = false;CloseUtil.closeAll(dos, console);}}//1.從控制臺(tái)接收數(shù)據(jù)public String getMsgFromConsole(){try{return console.readLine();}catch(IOException e){isRunning = false;CloseUtil.closeAll(dos, console);}return “”;}//將接收的數(shù)據(jù)發(fā)送出去public void send(){String msg = getMsgFromConsole();try{if(msg != null && !msg.equals(“”)){dos.writeUTF(msg);dos.flush();}}catch(IOException e){isRunning = false;CloseUtil.closeAll(dos, console);}}public void run(){//線程主體while(isRunning){send();}}}

    然后在客戶端處連接后直接建立新的線程即可:

    new thread(new Send(client)).start(); //一條路徑

    類似的建立接收線程Receive.java

    public class Receive implements Runnable{//輸入流private DataInputStream dis;//線程標(biāo)識(shí)private boolean isRunning = true;//構(gòu)造器public Receive(){}public Receive(Socket client){try{dis = new DataInputStream(client.getInputStream());}catch(IOException e){isRunning = false;CloseUtil.closeAll(dis);}}//接收數(shù)據(jù)public String receive(){String msg = “”;try{msg = dis.readUTF();}catch(IOException e ){isRunning = false;CloseUtil.closeAll(dis);}return msg;}public void run(){//線程主體while(isRunning){System.out.println(receive());}}}

    將關(guān)閉流的方法進(jìn)行封裝:CloseUtil.java

    public class CloseUtil{public static void closeAll(Closeable… io){for(Closeable temp:io){try{if(temp !=null){temp.close();}}catch(Exception e){}}} }

    線程封裝完畢后再將客戶端進(jìn)行修改Client02.java

    public static void main(String[] args){//創(chuàng)建客戶端Socket client = new Socket(“l(fā)ocalhost”,9999);//創(chuàng)建輸入線程new Thread(new Send(client)).start();//創(chuàng)建輸入線程new Thread(new Receive(client)).start(); }

    這樣客戶端在連接之后便可以任意發(fā)送與接收數(shù)據(jù)了,但是服務(wù)端目前還是只接受與轉(zhuǎn)發(fā)數(shù)據(jù)一次,所以需要將服務(wù)端加一個(gè)循環(huán)。Server02.java

    public static void main(String[] args){//創(chuàng)建服務(wù)器ServerSocket server = new ServerSocket(9999);//接收服務(wù)端連接Socket client = server.accept();//寫出數(shù)據(jù)//輸入流DataInputStream dis = new DataInputStream(client.getInputStream());//輸出流DataOutputStream dos = new DataOutputStream(client.getOutputStream());while(true){String msg = dis.readUTF();dos.writeUTF(“服務(wù)器——>” + msg);dos.flush();} }

    這樣便可以實(shí)現(xiàn)一個(gè)客戶端與服務(wù)端任意的收發(fā)數(shù)據(jù)了,但是還不能實(shí)現(xiàn)多個(gè)客戶端的連接。主要是因?yàn)榉?wù)端并沒有為每個(gè)客戶端的連接創(chuàng)建單獨(dú)的線程,由于這個(gè)服務(wù)線程只有服務(wù)端使用,所以可以考慮在服務(wù)端建立內(nèi)部類,創(chuàng)建如下:

    服務(wù)端Server03.java

    public class Server03{//為每個(gè)客戶端創(chuàng)建一個(gè)容器private List< MyChannel > all = new ArrayList< MyChannel >();public static void main(String[] args){new Server03. channel();}//接收客戶端線程服務(wù)public void channel(){//創(chuàng)建服務(wù)器ServerSocket server = new ServerSocket(9999);//接收服務(wù)端連接while(true){Socket client = server.accept();MyChannel channel = new MyChannel(client);all.add(channel); //將一條道路加入容器new Thread(channel).start();}}//內(nèi)部類,處理客戶端的線程private class MyChannel implements Runnable(){private DataInputStream dis;private DataOutputStream dos;private boolean isRunning = true;//構(gòu)造器public MyChannel(){}public MyChannel(Socket client){try{dis = new DataInputStream(client.getInputStream());dos = new DataOutputStream(client.getOutputStream());}catch(IOException e){isRunning = false;CloseUtil.closeAll(dis,dos);all.remove(this): //移除自身}}//接收數(shù)據(jù)pivate String receive(){String msg = “”;try{msg = dis.readUTF();}catch(IOException e){isRunning = false;CloseUtil.closeAll(dis);all.remove(this): //移除自身}return msg;}//發(fā)送數(shù)據(jù)private void send(String msg){try{if(msg!=null && !msg.equals(“”)){dos.writeUTF(msg);dos.flush();}}catch(IOException e){isRunning = false;CloseUtil.closeAll(dos);all.remove(this): //移除自身}}//發(fā)送給其他客戶端private void sendOthers(){String msg = receive();for(MyChannel other : all){if(other == this){continue;}other.send(msg)}}public void run(){//線程主體while(isRunning){sendOthers();}}}}

    這樣便實(shí)現(xiàn)了客戶端與服務(wù)器端的獨(dú)立通信。

    總結(jié)

    以上是生活随笔為你收集整理的Java编程之路——网络编程篇的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。