基於JAVA socket的服務器客戶…

要完成這個工作,需要完成三個部分的工作,以下依次說明:

一、建立服務器類

Java中有一個專門用來建立Socket服務器的類,名叫ServerSocket,可以用服務器需要使用的端口號作爲參數來創建服務器對象。

ServerSocket server = new ServerSocket(9998)

這條語句創建了一個服務器對象,這個服務器使用9998號端口即在端口9998上註冊服務,這裏稍微要注意的是端口的分配必須是唯一的。因爲端口是爲了唯一標識每臺計算機唯一服務的,另外端口號是從0~65535之間的,前1024個端口已經被Tcp/Ip 作爲保留端口,因此你所分配的端口只能是1024個之後的。當一個客戶端程序建立一個Socket連接,所連接的端口號爲9998時,服務器對象server便響應這個連接,並且server.accept()()方法會創建一個Socket對象。服務器端便可以利用這個Socket對象與客戶進行通訊。

Socket incoming = server.accept()() ; // 監聽窗口,等待連接

進而得到輸入流和輸出流,並進行封裝

BufferedReader in = new BufferedReader(new

InputStreamReader(incoming.getInputStream()));



PrintWriter ut = new PrintWriter(incoming.getOutputStream(),true);

隨後,就可以使用in.readLine()方法得到客戶端的輸入,也可以使用out.println()方法向客戶端發送數據。從而可以根據程序的需要對客戶端的不同請求進行迴應。

在所有通訊結束以後應該關閉這兩個數據流,關閉的順序是先關閉輸出流,再關閉輸入流,即使用

out.close();

in.close();

二、建立客戶端代碼

相比服務器端,客戶端要簡單一些,客戶端只需用服務器所在機器的ip以及服務器的端口作爲參數創建一個Socket對象。得到這個對象後,就可以用"建立服務器"部分介紹的方法實現數據的輸入和輸出。

Socket socket = new Socket("168.160.12.42",9998);

或:

Socket socket = new Socket(InetAddress.getLocalHost(),5678); // 向主機名爲InetAddress.getLocalHost()的服務器申請連接

客戶機必須知道有關服務器的IP地址,對於着一點Java也提供了一個相關的類InetAddress 該對象的實例必須通過它的靜態方法來提供,它的靜態方法主要提供了得到本機IP 和通過名字或IP直接得到InetAddress的方法。

in = new BufferedReader(new InputStreamReader(socket.getInputStream()));

out = new PrintWriter(socket.getOutputStream(),true);

以上的程序代碼建立了一個Socket對象,這個對象連接到ip地址爲168.160.12.42的主機上、端口爲9998的服務器對象。並且建立了輸入流和輸出流,分別對應服務器的輸出和客戶端的寫入。

三、實例分析

服務方:

import java.io.*;
import java.net.*;
public class MyServer {
public static void main(String[] args) throws IOException{
ServerSocket server=new ServerSocket(5678); //在端口5678上註冊服務
Socket client=server.accept()(); // 監聽窗口,等待連接
BufferedReader in=new BufferedReader(new InputStreamReader(client.getInputStream()));

BufferedReader serverInput=new BufferedReader(new InputStreamReader(System.in));
PrintWriter ut=new PrintWriter(client.getOutputStream());
while(true){
String str=in.readLine(); //// 讀取從client傳來的數據信息

str = serverInput.readLine(); // 讀取用戶鍵盤輸入的字符串
System.out.println(str); //服務器控制檯輸出數據信息
out.println("has receive...."); //服務器向客戶端發送信息:has receive....
out.flush();
if(str.equals("end"))
break;
}
client.close();
}
}

這個程序的主要目的在於服務器不斷接收客戶機所寫入的信息只到,客戶機發送"End"字符串就退出程序,並且服務器也會做出"Receive"爲迴應,告知客戶機已接收到消息。

客戶機代碼:

import java.net.*;
import java.io.*;

public class Client{
static Socket server;

public static void main(String[] args)throws Exception{
server=new Socket(InetAddress.getLocalHost(),5678); // 向主機名爲InetAddress.getLocalHost()的服務器申請連接
BufferedReader in=new BufferedReader(new InputStreamReader(server.getInputStream())); //客戶端建立輸入流並進行封裝
PrintWriter ut=new PrintWriter(server.getOutputStream());
BufferedReader wt=new BufferedReader(new InputStreamReader(System.in)); //客戶端從鍵盤輸入信息

while(true){
String str=wt.readLine(); //客戶端讀取(獲得)鍵盤的字符串

String str1=in.readLine(); // 從服務器獲得字符串
out.println(str); //客戶端向服務器發送信息
out.flush();
if(str.equals("end")){
break;
}
System.out.println(in.readLine());
}
server.close();
}
}

客戶機代碼則是接受客戶鍵盤輸入,並把該信息輸出,然後輸出"End"用來做退出標識。

這個程序只是簡單的兩臺計算機之間的通訊,如果是多個客戶同時訪問一個服務器呢?你可以試着再運行一個客戶端,結果是會拋出異常的。那麼多個客戶端如何實現呢?

其實,簡單的分析一下,就可以看出客戶和服務通訊的主要通道就是Socket本身,而服務器通過accept方法就是同意和客戶建立通訊.這樣當客戶建立Socket的同時。服務器也會使用這一根連線來先後通訊,那麼既然如此只要我們存在多條連線就可以了。那麼我們的程序可以變爲如下:

服務器:

import java.io.*;
import java.net.*;

public class MyServer {
public static void main(String[] args) throws IOException{
ServerSocket server=new ServerSocket(5678);
while(true){
Socket client=server.accept();
BufferedReader in=new BufferedReader(new InputStreamReader(client.getInputStream()));
PrintWriter ut=new PrintWriter(client.getOutputStream());
while(true){
String str=in.readLine();
System.out.println(str);
ut.println("has receive....");
ut.flush();
if(str.equals("end"))
break;
}
client.close();
}
}
}

這裏僅僅只是加了一個外層的While循環,這個循環的目的就是當一個客戶進來就爲它分配一個Socket直到這個客戶完成一次和服務器的交互,這裏也就是接受到客戶的"End"消息.那麼現在就實現了多客戶之間的交互了。但是.問題又來了,這樣做雖然解決了多客戶,可是是排隊執行的。也就是說當一個客戶和服務器完成一次通訊之後下一個客戶纔可以進來和服務器交互,無法做到同時服務,那麼要如何才能同時達到既能相互之間交流又能同時交流呢?很顯然這是一個並行執行的問題了。所以線程是最好的解決方案。

那麼下面的問題是如何使用線程.首先要做的事情是創建線程並使得其可以和網絡連線取得聯繫。然後由線程來執行剛纔的操作,要創建線程要麼直接繼承Thread要麼實現Runnable接口,要建立和Socket的聯繫只要傳遞引用就可以了.而要執行線程就必須重寫run方法,而run方法所做的事情就是剛纔單線程版本main所做的事情,因此我們的程序變成了這樣:

import java.net.*;
import java.io.*;

public class MultiUser extends Thread{
private Socket client;

public MultiUser(Socket c){
this.client=c;
}

public void run(){
try{
BufferedReader in=new BufferedReader(new InputStreamReader(client.getInputStream()));
PrintWriter ut=new PrintWriter(client.getOutputStream());
//Mutil User but can't parallel
while(true){
String str=in.readLine();
System.out.println(str);
ut.println("has receive....");
ut.flush();
if(str.equals("end"))
break;
}
client.close();
}catch(IOException ex){
}finally{
}
}

public static void main(String[] args)throws IOException{
ServerSocket server=new ServerSocket(5678);
while(true){
//transfer location change Single User or Multi User
MultiUser mu=new MultiUser(server.accept());
mu.start();
}
}
}

我的類直接從Thread類繼承了下來.並且通過構造函數傳遞引用和客戶Socket建立了聯繫,這樣每個線程就有了。一個通訊管道.同樣我們可以填寫run方法,把之前的操作交給線程來完成,這樣多客戶並行的Socket就建立起來了。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章