天天看點

Java Socket學習筆記

最近學了學Java Socket方面的東西,網上找了很多朋友的代碼參考了,做了兩個小例子,為資料使用。

一,網絡程式設計中兩個主要的問題:

一個是如何準确的定位網絡上一台或多台主機,另一個就是找到主機後如何可靠高效的進行資料傳輸。

在TCP/IP協定中IP層主要負責網絡主機的定位,資料傳輸的路由,由IP位址可以唯一地确定Internet上的一台主機。

而TCP層則提供面向應用的可靠(tcp)的或非可靠(UDP)的資料傳輸機制,這是網絡程式設計的主要對象,一般不需要關心IP層是如何處理資料的。

目前較為流行的網絡程式設計模型是客戶機/伺服器(C/S)結構。即通信雙方一方作為伺服器等待客戶提出請求并予以響應。客戶則在需要服務時向伺服器提 出申請。伺服器一般作為守護程序始終運作,監聽網絡端口,一旦有客戶請求,就會啟動一個服務程序來響應該客戶,同時自己繼續監聽服務端口,使後來的客戶也 能及時得到服務。

二,兩類傳輸協定:TCP;UDP:

TCP是Tranfer Control Protocol的 簡稱,是一種面向連接配接的保證可靠傳輸的協定。通過TCP協定傳輸,得到的是一個順序的無差錯的資料流。發送方和接收方的成對的兩個socket之間必須建 立連接配接,以便在TCP協定的基礎上進行通信,當一個socket(通常都是server socket)等待建立連接配接時,另一個socket可以要求進行連接配接,一旦這兩個socket連接配接起來,它們就可以進行雙向資料傳輸,雙方都可以進行發送 或接收操作。

UDP是User Datagram Protocol的簡稱,是一種無連接配接的協定,每個資料報都是一個獨立的資訊,包括完整的源位址或目的位址,它在網絡上以任何可能的路徑傳往目的地,是以能否到達目的地,到達目的地的時間以及内容的正确性都是不能被保證的。

UDP:

1,每個資料報中都給出了完整的位址資訊,是以無需要建立發送方和接收方的連接配接。

2,UDP傳輸資料時是有大小限制的,每個被傳輸的資料報必須限定在64KB之内。

3,UDP是一個不可靠的協定,發送方所發送的資料報并不一定以相同的次序到達接收方

TCP:

1,面向連接配接的協定,在socket之間進行資料傳輸之前必然要建立連接配接,是以在TCP中需要連接配接時間。

2,TCP傳輸資料大小限制,一旦連接配接建立起來,雙方的socket就可以按統一的格式傳輸大的資料。

3,TCP是一個可靠的協定,它確定接收方完全正确地擷取發送方所發送的全部資料。

了解Java Socket:

所謂socket通常也稱作"套接字",用于描述IP位址和端口,是一個通信鍊的句柄。應用程式通常通過"套接字"向網絡送出請求或者應答網絡請求。

網絡上的兩個程式通過一個雙向的通訊連接配接實作資料的交換,這個雙向鍊路的一端稱為一個Socket。Socket通常用來實作客戶方和服務方的連接配接。Socket是TCP/IP協定的一個十分流行的程式設計界面,一個Socket由一個IP位址和一個端口号唯一确定。

但是,Socket所支援的協定種類也不光TCP/IP一種,是以兩者之間是沒有必然聯系的。在Java環境下,Socket程式設計主要是指基于TCP/IP協定的網絡程式設計。

Socket通訊的過程:

Server端Listen(監聽)某個端口是否有連接配接請求,Client端向Server 端發出Connect(連接配接)請求,Server端向Client端發回Accept(接受)消息。一個連接配接就建立起來了。Server端和Client 端都可以通過Send,Write等方法與對方通信。

通常Socket通信包括四個步驟:

1、建立Socket。

2、打開連接配接到Socket的輸入/出流。

3、按照一定的協定對Socket進行讀/寫操作。

4、關閉Socket.(在實際應用中,并未使用到顯示的close,雖然很多文章都推薦如此,不過在我的程式中,可能因為程式本身比較簡單,要求不高,是以并未造成什麼影響。

下面做了兩個例子:一個是單線程的Socket通信,二個是多線程的Socket通信

單線程:

package com.zyujie.socket;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class ServerApp {

	public static void main(String[] args) throws IOException {
		//建立服務端,監聽端口6688,端口号是從0~65535之間的,socket的端口最好是1024以上。1024之前的端口已經被Tcp/Ip 作為保留端口
		ServerSocket server = new ServerSocket(6688);
		//接受到一個連接配接,并且傳回一個用戶端的Socket對象執行個體
		Socket client = server.accept();
		//原始的位元組流來源于Socket的兩個方法.getInputStream()和getOutputStream()
		//這裡我們建立緩沖,把原始的位元組流轉變為Unicode
		BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
		PrintWriter out = new PrintWriter(client.getOutputStream());
		while (true) {
			//一行一行的讀取用戶端消息
			String str = in.readLine();
			System.out.println(str);
			//傳回消息給用戶端
			out.println("服務端已收到消息...");
			out.flush();
			//伺服器端讀到end就結束Socket通信,監聽退出
			if (str.equals("end")){
				break;
			}
		}
		//socket執行個體,連接配接關閉
		client.close();
	}
}
           
package com.zyujie.socket;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;

public class ClientApp {

	private static Socket server;

	public static void main(String[] args) throws Exception {
		//InetAddress是java.net包中的類,其靜态方法是得到本機IP和通過名字或IP直接得到InetAddress的方法
		server = new Socket(InetAddress.getLocalHost(), 6688);
		BufferedReader in = new BufferedReader(new InputStreamReader(server.getInputStream()));
		PrintWriter out = new PrintWriter(server.getOutputStream());
		//控制台輸入字元串
		BufferedReader wt = new BufferedReader(new InputStreamReader(System.in));
		while (true) {
			//一行一行的讀取控制台的輸入
			String str = wt.readLine();
			//發送給伺服器端
			out.println(str);
			out.flush();
			if (str.equals("end")) {
				break;
			}
			//讀取伺服器端傳回的消息
			System.out.println(in.readLine());
		}
		//socket執行個體,連接配接關閉
		server.close();
	}
}
           

多線程的Socket通信,隻提供了服務端代碼,用戶端和單線程的一樣:

package com.zyujie.socket;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class SocketServer extends Thread {

	private Socket client;

	/*
	 * 構造函數,接收每一個socket執行個體
	 */
	public SocketServer(Socket socket) {
		this.client = socket;
	}

	/*
	 * 線程執行方法
	 */
	public void run() {
		try {
			//這裡我們建立輸入和輸出的緩沖,把原始的位元組流轉變為Unicode
			BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
			PrintWriter out = new PrintWriter(client.getOutputStream());
			while (true) {
				//讀取用戶端消息
				String str = in.readLine();
				System.out.println(str);
				//傳回給用戶端消息
				out.println("服務端已收到消息...");
				out.flush();
				//遇到end就結束,這裡隻是結束單個用戶端的連接配接
				if (str.equals("end")){
					break;
				}
			}
			//socket執行個體,連接配接關閉
			client.close();
		} catch (IOException ex) {
		} finally {

		}
	}

	/*
	 * 啟用線程方法實作多個使用者連接配接socket伺服器端.
	 */
	public static void main(String[] args) throws IOException {
		//建立服務端,監聽端口6688
		ServerSocket server = new ServerSocket(6688);
		//使用伺服器端保持永久監聽狀态
		while (true) {
			//接收每一個用戶端的連接配接,并傳回socket執行個體
			SocketServer ss = new SocketServer(server.accept());
			//為每一個用戶端啟一個線程,去執行操作
			ss.start();
		}
	}
}
           

繼續閱讀