Java中的TCP/UDP網絡通訊編程

127.0.0.1是迴路地址,用於測試,至關於localhost本機地址,沒有網卡,不設DNS均可以訪問.java

  端口地址在0~65535之間,其中0~1023之間的端口是用於一些知名的網絡服務和應用,用戶的普通網絡應用程序應該使用1024以上的端口.windows

  網絡應用中基本上都是TCP(Transmission Control Protocol傳輸控制協議)和UDP(User Datagram Protocol用戶數據報協議),TCP是面向鏈接的通訊協議,UDP是無鏈接的通訊協議.緩存

  Socket鏈接套接字,Java分別爲TCP和UDP提供了相應的類,TCP是.ServerSocket(用於服務器端)和.Socket(用於客戶端);UDP是.DatagramSocket.服務器

  1,Java編寫UDP網絡程序網絡

  1.1,DatagramSocket閉包

  DatagramSocket有以下構造方法:工具

  1,DatagramSocket() :構造數據報套接字並將其綁定到本地主機上任何可用的端口。測試

  2,DatagramSocket(int port):建立數據報套接字並將其綁定到本地主機上的指定端口。ui

  3,DatagramSocket(int port, InetAddress laddr):建立數據報套接字,將其綁定到指定的本地地址。即指定網卡發送和接收數據.this

  若是在建立DatagramSocket對象時,沒有指定網卡的IP 地址,在發送數據時,底層驅動程序會自動選擇一塊網卡去發送,在接收數據時,會接收全部的網卡收到的與端口一致的數據.

  發送信息時,能夠不指定端口號,接收信息時,要指定端口號,由於要接收指定的數據.

  發送數據使用DatagramSocket.send(DatagramPacket p)方法,接收數據使用DatagramSocket.receive(DatagramPacket p)方法.

  1.2,DatagramPacket

  DatagramPacket類有以下構造方法:

  1,DatagramPacket(byte[] buf, int length):構造 DatagramPacket,用來接收長度爲length的數據包。

  2,DatagramPacket(byte[] buf, int length, InetAddress address, int port):構造數據報包,用來將長度爲length的包發送到指定主機上的指定端口號。

  接收數據時使用第一次構造方法,發送數據時使用第二種構造方法.

  1.3,InetAddress

  Java中對IP地址進行包裝的類,

  DatagramPacket.getAddress()能夠獲取發送或接收方的IP地址.DatagramPacket.getPort()能夠獲取發送或接收方的端口.

  1.4,UDP程序例子

  發送程序:

  import .DatagramPacket;

  import .DatagramSocket;

  import .InetAddress;

  public class UdpSend {

  public static void main(String[] args) throws Exception {

  DatagramSocket ds = new DatagramSocket();

  String str = "hello , world!";

  DatagramPacket dp = new DatagramPacket(str.getBytes(),str.length(),InetAddress.getByName("192.168.0.105"),3000);

  ds.send(dp);

  ds.close(); //關閉鏈接

  }

  }

  接收程序:

  import .DatagramPacket;

  import .DatagramSocket;

  public class UdpRecv {

  public static void main(String[] args) throws Exception {

  DatagramSocket ds = new DatagramSocket(3000);

  byte[] buf = new byte[1024];

  DatagramPacket dp = new DatagramPacket(buf,buf.length);

  ds.receive(dp);

  String str = new String(dp.getData(),0,dp.getLength());

  System.out.println(str);

  System.out.println("IP:" + dp.getAddress().getHostAddress() + ",PORT:" + dp.getPort());

  ds.close();

  }

  }

  測試要先運行接收程序,再運行發送程序.若是接收程序沒有接收到數據,則會一直阻塞,接收到數據後纔會關閉程序.若是網絡上沒有數據發送過來,接收程序也沒有阻塞,一般都是使用了一個已經被佔用的端口.

  2,Java編寫TCP網絡程序

  2.1,ServerSocket

  編寫TCP網絡服務程序,首先要用到.ServerSocket類用以建立服務器Socket.它的經常使用構造方法有:

  1,ServerSocket(int port):建立綁定到特定端口的服務器套接字。

  2,ServerSocket(int port, int backlog):利用指定的backlog(服務器忙時保持鏈接請求的等待客戶數量),建立服務器套接字並將其綁定到指定的本地端口號。

  3,ServerSocket(int port, int backlog, InetAddress bindAddr):使用指定的端口、偵聽 backlog 和要綁定到的本地 IP 地址建立服務器。

  2.2,Socket

  客戶端要與服務器創建鏈接,必須先建立一個Socket對象,它的經常使用構造方法有:

  1,Socket(String host, int port):建立一個流套接字並將其鏈接到指定主機上的指定端口號。

  2,Socket(InetAddress address, int port):建立一個流套接字並將其鏈接到指定 IP 地址的指定端口號。

  3,Socket(InetAddress address, int port, InetAddress localAddr, int localPort):建立一個套接字並將其鏈接到指定遠程端口上的指定遠程地址。

  4,Socket(String host, int port, InetAddress localAddr, int localPort):建立一個套接字並將其鏈接到指定遠程主機上的指定遠程端口。

  對於一般狀況的應用,使用第1個構造方法來建立客戶端的Socket對象,並與服務器創建鏈接,是很是簡單和方便的.

  服務器端程序調用ServerSocket.accept方法等待客戶端的鏈接請求,一旦accept接收了客戶端鏈接請求,該方法返回一個與 該客戶端創建了專線鏈接的Socket對象,不用程序去建立這個Socket對象.創建了鏈接的兩個Socket是以IO流的方式進行數據交換 的,Java提供了Socket.getInputStream返回Socket的輸入流對象,Socket.getOutputStream返回 Socket的輸出流對象.

  2.3,TCP程序例子的服務器程序:

  import java.io.InputStream;

  import java.io.OutputStream;

  import .ServerSocket;

  import .Socket;

  public class TcpServer {

  public static void main(String[] args) throws Exception {

  ServerSocket ss = new ServerSocket(8000);

  Socket s = ss.accept();

  InputStream ips = s.getInputStream();

  OutputStream ops = s.getOutputStream();

  ops.write("hello,World!".getBytes());

  byte[] buf = new byte[1024];

  int len = ips.read(buf);

  System.out.println(new String(buf,0,len));

  ips.close();

  ops.close();

  s.close();

  ss.close();

  }

  }

  在這個程序裏,建立了一個在8000端口上等待鏈接的ServerSocket對象,當接收到一個客戶的鏈接請求後,程序從與這個客戶創建了連 接的Socket對象中得到輸入輸出流對象,經過輸出流首先向客戶端發送一串字符,而後經過輸入流讀取客戶端發送過來的信息,並將這些信息打印,而後關閉 全部資源.

  要先運行服務器程序,而後才能運行客戶端程序,當TCP服務器程序運行到Socket.accpet()方法等待客戶鏈接時,accept方法 將阻塞,一直到有客戶鏈接請求到來,該方法纔會返回,若是又沒有請求到來,又沒有發生阻塞,一般都是使用了一個已經被佔用的端口.

  咱們能夠使用windows提供的telnet工具在命令行窗口中測試一下服務器程序:命令以下:telnet localhost 8000

  能夠看到,telnet只要有輸入就發送,所以咱們若是想要在服務器端一次讀多個字符的話,還須要進一步處理,看以下代碼:

  import java.io.BufferedReader;

  import java.io.InputStream;

  import java.io.InputStreamReader;

  import java.io.OutputStream;

  import .ServerSocket;

  import .Socket;

  public class TcpServer {

  public static void main(String[] args) throws Exception {

  ServerSocket ss = new ServerSocket(8000);

  Socket s = ss.accept();

  InputStream ips = s.getInputStream();

  BufferedReader br = new BufferedReader(new InputStreamReader(ips)); //對InputStream進行包裝,增長了緩存

  OutputStream ops = s.getOutputStream();

  ops.write("hello,World!".getBytes());

  System.out.println(br.readLine());

  br.close(); //關閉包裝類,會自動關閉裏面的基類

  ops.close();

  s.close();

  ss.close();

  }

  }

  再次使用telnet工具能夠看到,此次能夠發送不止一個字符了,按回車鍵後發送數據到服務器端.

  2.4,TCP程序例子改進後的服務器程序:

  大多數狀況下,服務器端都要服務多個客戶端,但一次accept方法調用只接收一個鏈接,所以,要把accept方法放在一個循環語句中,這樣就能夠接收多個鏈接.每一個鏈接的數據交換代碼也放在一個循環中,這樣才能保證二者能夠不停地交換數據.

  每一個鏈接的數據交換代碼必須放在獨立的線程中運行,不然,這在段代碼運行期間,就無法執行其餘的程序代碼,accept方法也得不到調用,新的鏈接沒法進入.

  下面是一個例子,客戶端向服務器發送一個字符串,服務器將這個字符串中的全部字符反向排列後回送給客戶端.客戶端輸入"quit",退出程序.

  import java.io.BufferedReader;

  import java.io.DataOutputStream;

  import java.io.InputStream;

  import java.io.InputStreamReader;

  import java.io.OutputStream;

  import .ServerSocket;

  import .Socket;

  public class TcpServer {

  public static void main(String[] args) throws Exception {

  ServerSocket ss = new ServerSocket(8000);

  while(true){

  Socket s = ss.accept();

  new Thread(new Servicer(s)).start();

  }

  }

  }

  class Servicer implements Runnable{

  Socket s;

  public Servicer(Socket s){

  this.s = s;

  }

  public void run(){

  try{

  InputStream ips = s.getInputStream();

  OutputStream ops = s.getOutputStream();

  BufferedReader br = new BufferedReader(new InputStreamReader(ips));

  DataOutputStream dos = new DataOutputStream(ops);

  while(true){

  String strWord = br.readLine();

  if(strWord.equalsIgnoreCase("quit")){

  break;

  }

  String strEcho = (new StringBuffer(strWord).reverse().toString());

  dos.writeBytes(strWord + "------->" + strEcho + System.getProperty("line.separator"));

  }

  br.close();

  dos.close();

  s.close();

  }catch(Exception e){

  e.printStackTrace();

  }

  }

  }

  2.5,TCP程序例子客戶端程序:

  import java.io.BufferedReader;

  import java.io.DataOutputStream;

  import java.io.InputStream;

  import java.io.InputStreamReader;

  import java.io.OutputStream;

  import .InetAddress;

  import .Socket;

  public class TcpClient {

  public static void main(String[] args) throws Exception{

  if(args.length < 2){

  System.out.println("Usage:java TcpClient ServerIP ServerPort");

  return ;

  }

  Socket s = new Socket(InetAddress.getByName(args[0]),Integer.parseInt(args[1]));

  InputStream ips = s.getInputStream();

  OutputStream ops = s.getOutputStream();

  BufferedReader brKey = new BufferedReader(new InputStreamReader(System.in));

  DataOutputStream dos = new DataOutputStream(ops);

  BufferedReader brNet = new BufferedReader(new InputStreamReader(ips));

  while(true){

  String strWord = brKey.readLine();

  dos.writeBytes(strWord + System.getProperty("line.separator"));

  if("quit".equalsIgnoreCase(strWord)){

  break;

  }else{

  System.out.println(brNet.readLine());

  }

  }

  dos.close();

  brNet.close();

  brKey.close();

  s.close();

  }

  }

  先運行服務器程序,再在命令行使用java TcpClient 192.168.0.3 8000,這樣就啓動了客戶端程序.咱們能夠啓動多個客戶端程序.

  咱們能夠利用netstat工具來查看已經被使用的端口

相關文章
相關標籤/搜索