Java網絡編程基于TCP/UDP協(xié)議的基礎之上,TCP/IP協(xié)議是一個協(xié)議簇。里面包括很多協(xié)議的,UDP只是其中的一個, 之所以命名為TCP/IP協(xié)議,因為TCP、IP協(xié)議是兩個很重要的協(xié)議,就用他兩命名了。那么首先我們先介紹一下TCP和UDP的特點:
1.TCP(Transmission Control Protocol,傳輸控制協(xié)議)是面向連接的,也就是說在以TCP協(xié)議發(fā)送數(shù)據(jù)的之前需要兩臺主機建立連接,一個TCP連接必須要經過三次“對話”才能建立起來,也俗稱“三次握手”,其中的過程非常復雜,下面將以簡單的形式介紹一下三次握手的過程:
有兩臺主機,主機A與主機B。
1)主機A向主機B發(fā)出連接請求數(shù)據(jù)包:“我想給你發(fā)數(shù)據(jù),可以嗎?”,這是第一次對話;
2)主機B向主機A發(fā)送同意連接和要求同步 (同步就是兩臺主機一個在發(fā)送,一個在接收,協(xié)調工作)的數(shù)據(jù)包 :“可以,你什么時候發(fā)?”,這是第二次對話;
3)主機A再發(fā)出一個數(shù)據(jù)包確認主機B的要求同步:“我現(xiàn)在就發(fā),你接著吧!”, 這是第三次對話。
三次“對話”的目的是使數(shù)據(jù)包的發(fā)送和接收同步, 經過三次“對話”之后,主機A才向主機B正式發(fā)送數(shù)據(jù)。
TCP建立連接需要三次握手,而斷開連接需要四次,即四次揮手:
1)主機A向主機B發(fā)送請求斷開連接,這是第一次對話;
2)主機B收到主機A的斷開連接請求,返回確認,這是第二次對話;
3)主機B在接收完所有數(shù)據(jù)之后,向主機A發(fā)送確認斷開連接,這是第三次對話;
4)主機A回復確認斷開,這是第四次對話;
這四次對話完成之后,主機A和主機B才正式斷開連接。
2.UDP(User Data Protocol,用戶數(shù)據(jù)報協(xié)議),與TCP相反,UDP是不面向連接的協(xié)議,所以也是不可靠傳輸,只能盡力傳輸數(shù)據(jù)而保證數(shù)據(jù)傳輸?shù)目煽啃裕S糜谝曨l會議或者消息發(fā)送等服務。
了解了基本的概念特點之后,我們將使用Java編程的方式來實現(xiàn)基于這兩種協(xié)議的數(shù)據(jù)傳輸過程:
UDP
傳輸數(shù)據(jù)與我們生活中寄快遞很相似,在寄快遞的時候,我們首先要用一個盒子將物品打包起來,再通過物流公司寄送出去,那么傳輸數(shù)據(jù)也是類似的:
首先client,DatagramSocket對象就類似與快遞公司,負責最后的物品寄送,DatagramPacket對象就類似于打包過程,將所需要的數(shù)據(jù)進行打包。
1 public class SendMessageDemo { 2 public static void main(String[] args) throws IOException { 3 //采用UDP協(xié)議發(fā)送數(shù)據(jù) 4 //1.創(chuàng)建DatagramSocket對象(快遞公司,負責發(fā)送數(shù)據(jù)) 5 //空參 隨機綁定一個端口號進行使用 有參則指定端口號進行綁定 6 DatagramSocket ds = new DatagramSocket(); 7 InetAddress address = InetAddress.getByName("127.0.0.1"); 8 int port = 10087; 9 //2.打包數(shù)據(jù),用字節(jié)數(shù)組的形式發(fā)送 10 String str = "Hello world!"; 11 byte[] bytes = str.getBytes(); 12 DatagramPacket dp = new DatagramPacket(bytes,bytes.length,address,port); 13 //3.發(fā)送數(shù)據(jù) 14 ds.send(dp); 15 //4.釋放資源 16 ds.close(); 17 18 } 19 }
對于Server端,接收到client發(fā)送的數(shù)據(jù)之后,需要對這些字節(jié)進行解析,才能夠知道發(fā)送的是什么內容:
1 public class ReceiveMessageDemo { 2 public static void main(String[] args) throws IOException { 3 //先運行接收進程,再運行發(fā)送進程 4 //創(chuàng)建快遞公司用來接收數(shù)據(jù)包,必須綁定跟發(fā)送端發(fā)送的端口一致 5 DatagramSocket ds = new DatagramSocket(10087); 6 7 //2.接收數(shù)據(jù)包 8 byte[] bytes = new byte[1024]; 9 //使用空的數(shù)據(jù)包存儲數(shù)據(jù) 10 DatagramPacket dp = new DatagramPacket(bytes,bytes.length); 11 //該方法是阻塞的,會一直等待發(fā)送方發(fā)送消息 12 ds.receive(dp); 13 14 //3.解析數(shù)據(jù)包 15 byte[] data = dp.getData();//獲取接收到的數(shù)據(jù) 16 int length = dp.getLength();//獲取接收到的數(shù)據(jù)的長度 17 String str = new String(data,0,length);//解析成字符串的形式 18 InetAddress address = dp.getAddress();//獲取發(fā)送方的IP信息 19 int port = dp.getPort(); 20 System.out.println("接收到的信息為:"+str+",來自主機:"+address+"端口為:"+port); 21 System.out.println("長度為:"+length); 22 23 24 } 25 }
如果想發(fā)送多條消息呢?我們可以使用while循環(huán)來接收用戶鍵盤輸入的消息:
1 public class SendMessageAgent { 2 public static void main(String[] args) throws IOException { 3 4 //發(fā)送方端口可以不指定隨機使用 5 DatagramSocket ds = new DatagramSocket(); 6 7 //創(chuàng)建要發(fā)送的數(shù)據(jù)包 8 Scanner scanner = new Scanner(System.in); 9 while(true) { 10 System.out.println("請輸入你要發(fā)送的內容:"); 11 String s = scanner.nextLine(); 12 if (s.equals("886")) { 13 break; 14 } 15 //將輸入的字符串轉為字節(jié)數(shù)組發(fā)送 16 byte[] message = s.getBytes(); 17 18 //選擇發(fā)送的主機和端口 19 InetAddress address = InetAddress.getByName("127.0.0.1"); 20 int port = 10088; 21 DatagramPacket dp = new DatagramPacket(message, message.length, address, port); 22 23 //發(fā)送 24 ds.send(dp); 25 } 26 //釋放資源 27 ds.close(); 28 } 29 }
TCP
如果要使用TCP協(xié)議發(fā)送數(shù)據(jù),那么和UDP所需要的對象是不一樣的:
1 public class Client { 2 public static void main(String[] args) throws IOException { 3 //TCP協(xié)議 4 //1.創(chuàng)建socket對象 5 //該對象綁定需要連接的主機與端口,如果連接失敗則代碼報錯 6 Socket socket = new Socket("127.0.0.1",10000); 7 8 //TCP中以IO輸出流的形式發(fā)送,以輸入流的形式接收 9 //2.獲取輸出流,并寫入數(shù)據(jù) 10 OutputStream os = socket.getOutputStream(); 11 os.write("hello world!你們好".getBytes()); 12 13 //釋放資源 這里進行四次揮手 14 os.close(); 15 socket.close(); 16 17 } 18 }
public class Server { public static void main(String[] args) throws IOException { //1.創(chuàng)建serverSocket,并綁定接收端口(必須與發(fā)送的端口相同) ServerSocket socket = new ServerSocket(10000); //2.監(jiān)聽連接,如果連接成功則返回一個socekt對象,沒有連接成功則阻塞等待 Socket accept = socket.accept();//這里進行三次握手 //3.從accept中讀取輸入流,并獲取信息 InputStream is = accept.getInputStream(); //字節(jié)流,一個字節(jié)一個字節(jié)的讀入,但是中文會出錯,一個中文三個字節(jié),讀入的時候會導致中文亂碼 InputStreamReader isr = new InputStreamReader(is);//字符流,一個字符一個字符的讀入,解決中文問題 BufferedReader br = new BufferedReader(isr);//提升讀取效率 int b; while((b=br.read()) != -1){//需要定義結束標記,不然會一直卡死在這里等待讀取 System.out.print((char) b); } //收到信息后寫入反饋 InetAddress address = accept.getInetAddress(); OutputStream outputStream = accept.getOutputStream(); String returnMessage = "已經收到:"+address.getHostAddress()+address.getHostName()+"發(fā)來的信息!"; outputStream.write(returnMessage.getBytes()); //4.釋放資源 accept.close(); socket.close(); } }
需要先運行server端代碼,再運行client端代碼,因為client端如果沒有找到對應主機則會報錯。
?文件傳輸
client端:
1 public class client { 2 public static void main(String[] args) throws IOException { 3 Socket socket = new Socket("127.0.0.1",10000); 4 5 BufferedInputStream bis = new BufferedInputStream(new FileInputStream("mysocketnet/clientdir/neural.png")); 6 BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream()); 7 8 byte[] bytes = new byte[1024]; 9 int len; 10 while((len=bis.read(bytes)) != -1){ 11 bos.write(bytes,0,len); 12 } 13 14 socket.shutdownOutput(); 15 //獲取服務器的回寫數(shù)據(jù) 16 BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())); 17 String line = br.readLine(); 18 System.out.println(line); 19 20 socket.close(); 21 } 22 }
通過多線程來解決多用戶同時上傳文件的問題,每有一個用戶建立連接就新創(chuàng)建一個線程來處理上傳文件的請求,可以采用線程池進行優(yōu)化,采用runnable的方式創(chuàng)建線程,這樣我們可以定義自己的runnable方法來處理文件上傳請求:文章來源:http://www.zghlxwxcb.cn/news/detail-711747.html
1 public class server { 2 public static void main(String[] args) throws IOException { 3 4 ServerSocket serverSocket = new ServerSocket(10000); 5 Socket accept = serverSocket.accept(); 6 //每建立一個連接,就創(chuàng)建一個線程來處理上傳請求! 7 //創(chuàng)建線程池對象 8 ThreadPoolExecutor pool = new ThreadPoolExecutor( 9 3,//核心線程數(shù)量 10 16,//線程池總大小 11 60,//空閑時間 12 TimeUnit.SECONDS, 13 new ArrayBlockingQueue<Runnable>(2), 14 Executors.defaultThreadFactory(), 15 new ThreadPoolExecutor.AbortPolicy() 16 ); 17 //new Thread(new SocketRunnable(accept) ).start(); 18 pool.submit(new SocketRunnable(accept)); 19 20 } 21 }
public class SocketRunnable implements Runnable{ Socket accept; public SocketRunnable(Socket accept){ this.accept = accept; } @Override public void run() { try { BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("mysocketnet/serverdir/neural.png")); BufferedInputStream bis = new BufferedInputStream(accept.getInputStream()); byte[] bytes = new byte[1024]; int len; while ((len = bis.read(bytes)) != -1) { bos.write(bytes, 0, len); } //回寫數(shù)據(jù) BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(accept.getOutputStream())); bw.write("上傳成功!"); bw.newLine(); bw.flush(); }catch (Exception e){ System.out.println("上傳失敗"); }finally { try { if(accept != null){ accept.close();} } catch (IOException e) { throw new RuntimeException(e); } } } }
?文章來源地址http://www.zghlxwxcb.cn/news/detail-711747.html
到了這里,關于Java網絡編程基礎的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!