程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Java中的TCP/UDP網絡通信編程

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

編輯:關於JAVA

 127.0.0.1是回路地址,用於測試,相當於localhost本機地址,沒有網卡,不設DNS都可以訪問.

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

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

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

  1,Java編寫UDP網絡程序

  1.1,DatagramSocket

  DatagramSocket有如下構造方法:

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

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

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

  如果在創建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 Java.Net.DatagramPacket;

  import Java.Net.DatagramSocket;

  import Java.Net.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 Java.Net.DatagramPacket;

  import Java.Net.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網絡服務程序,首先要用到Java.Net.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 Java.Net.ServerSocket;

  import Java.Net.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 Java.Net.ServerSocket;

  import Java.Net.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 Java.Net.ServerSocket;

  import Java.Net.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 Java.Net.InetAddress;

  import Java.Net.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工具來查看已經被使用的端口

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved