Java網(wǎng)絡(luò)編程之簡單的服務(wù)端客戶端應(yīng)用實(shí)例
本文實(shí)例講述了Java網(wǎng)絡(luò)編程之簡單的服務(wù)端客戶端應(yīng)用。分享給大家供大家參考。具體如下:
在Java中,我們使用java.net.Socket及其相關(guān)類來完成有關(guān)網(wǎng)絡(luò)的相關(guān)功能。Socket類非常簡單易用,因?yàn)镴ava技術(shù)隱藏了建立網(wǎng)絡(luò)連接和通過連接發(fā)送數(shù)據(jù)的復(fù)雜過程。下面所說的內(nèi)容只適用于TCP協(xié)議。
一、連接到服務(wù)器
我們可以使用Socket類的構(gòu)造函數(shù)來打開一個(gè)套接字,如
Socket sk = new Socket("210.0.235.14",13);
其中,210.0.235.14是一個(gè)點(diǎn)分十進(jìn)制的String對象,表示目的主機(jī)的IP地址(或主機(jī)名), 13表示指定連接目標(biāo)主機(jī)的13端口。這里的210.0.235.14是位于香港的一個(gè)授時(shí)服務(wù)器,授時(shí)服務(wù)器默認(rèn)的端口一般都為13.
注意,在成功連接到服務(wù)器之前,程序會(huì)阻塞。
接下來可以使用Socket類的getInputStream()方法來得到一個(gè)InputStream對象,通過這個(gè)對象就可以獲取到目標(biāo)主機(jī)給我們發(fā)過來的信息:
InputStream inStream = sk.getInputStream();
同理,要向目標(biāo)主機(jī)發(fā)送數(shù)據(jù),則可以調(diào)用getOutputStream()方法來獲取一個(gè)輸出流對象。
下面的例子功能是連接授時(shí)服務(wù)器,并將返回的信息打印到標(biāo)準(zhǔn)輸出中:
try
{
Socket sk = new Socket("210.0.235.14",13);
sk.setSoTimeout(3000);
InputStream inStream = sk.getInputStream();
//得到輸入流對象
Scanner sc = new Scanner(inStream);
//將數(shù)據(jù)打印到控制臺(tái)
while(sc.hasNextLine())
{
String str = sc.nextLine();
System.out.println("Output : " + str);
}
sk.close();
}
catch(SocketTimeoutException e) //超時(shí)異常
{
System.out.println("Time Out!");
}
catch(Exception e)
{
e.printStackTrace();
}
代碼中setSoTimeout()方法可以設(shè)置超時(shí)時(shí)間,即如果超過了設(shè)定時(shí)間還沒有完成讀寫操作,則會(huì)拋出SocketTimeoutException,可以通過捕獲這個(gè)異常來關(guān)閉連接。
另外還有一個(gè)超時(shí)問題是必須要解決的,就是這個(gè)Socket類的構(gòu)造函數(shù)
new Socket(host,port);
會(huì)一直無限地阻塞下去,直到成功建立了到目標(biāo)主機(jī)的連接為止。這當(dāng)然不是我們所希望的。我們可以通過如下調(diào)用方式解決此問題:
Socket sk = new Socker(); sk.connect(new InetSocketAddress(host,port),2000); //設(shè)置超時(shí)時(shí)間為2秒
二、獲取主機(jī)地址
InetAddress類的靜態(tài)方法getByName(hostname)可以返回代表了某個(gè)主機(jī)地址的InetAddress對象,這個(gè)對象封閉了一個(gè)4字節(jié)的序列,即主機(jī)的IP地址。然后再調(diào)用getHostAddress()方法返回一個(gè)表示IP地址的String對象.
一些訪問量大的主機(jī)名通常會(huì)對應(yīng)著多個(gè)IP地址以實(shí)現(xiàn)負(fù)載均衡。我們可以調(diào)用getAllByName()方法來獲得所有主機(jī)地址,該方法返回一個(gè)InetAddress對象的數(shù)組。
下面是一個(gè)簡單的小程序,實(shí)現(xiàn)的功能是,如果不在命令行中設(shè)置參數(shù),就打印出本地的IP地址,如果指定了主機(jī)名,則打印出該主機(jī)所有的IP地址:
package cls;
import java.net.*;
public class ShowIP
{
public static void main(String[] args)
{
try
{
if(args.length > 0)
{
String hostName = args[0]; //主機(jī)名
InetAddress[] addr = InetAddress.getAllByName(hostName);
//得到該主機(jī)的所有地址
//打印輸出至控制臺(tái)
for(InetAddress address : addr)
{
System.out.println(address.getHostAddress());
}
}
else
{
System.out.println(InetAddress.getLocalHost().getHostAddress());
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
三、服務(wù)器端程序
服務(wù)器端應(yīng)用程序使用ServerSocket類來創(chuàng)建套接字,并鈄其綁定至本地端口中,如
ServerSocket sock = new ServerSocker(8000);
sock.accept()方法讓程序不停地等待連接,該方法只有當(dāng)有客戶端連接時(shí)才會(huì)返回一個(gè)代表了新連接的Socket對象,即該方法會(huì)發(fā)生阻塞。
這里一般要為每個(gè)連接新開啟一個(gè)線程為其服務(wù)。
下面是一個(gè)完整的例子,服務(wù)端在8400端口處等待連接,每當(dāng)連接到來時(shí),新開一個(gè)線程為其服務(wù),并將連接信息寫入的日志文件中:
package cls;
import java.io.*;
import java.net.*;
import java.util.*;
public class ServerDemo
{
/**
* @param args
*/
public static void main(String[] args)
{
try
{
//ServerSocket servSocket = new ServerSocket(8000);
ServerSocket servSocket = new ServerSocket(8400);
int amount = 0;
while(true)
{
Socket client = servSocket.accept();
++amount;
Date time = new Date();
String prompt = time.toString() + ": 第" + amount + "個(gè)用戶 " + client.getInetAddress().getHostAddress() + " 已連接\n";
System.out.print(prompt); //在控制臺(tái)輸出信息
ServerDemo.writeLog(prompt); //寫入到文件中
//start a new Thread
Thread th = new Thread(new ServThread(client,amount));
th.start();
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
//寫入日志文件
public static void writeLog(String str)
{
File logFile = new File("server-log.txt");
try
{
FileWriter out = new FileWriter(logFile,true);
out.append(str);
out.close();
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
/*
* 服務(wù)線程類
*/
class ServThread implements Runnable
{
private Socket client;
private int ix;
public ServThread(Socket soc,int ix)
{
client = soc;
this.ix = ix;
}
public void run()
{
try
{
InputStream inStream = client.getInputStream();
OutputStream outStream = client.getOutputStream();
Scanner recv = new Scanner(inStream);
PrintWriter send = new PrintWriter(outStream,true);
send.println("歡迎~隨便聊幾句吧![輸入'bye'關(guān)閉聯(lián)接]");
while(recv.hasNextLine())
{
String str = recv.nextLine();
if(str.equals("bye"))
{
send.println("See you later ~ ^-^");
break;
}
send.println("這是個(gè)測試程序,現(xiàn)在還沒有什么功能哦");
}
Date time = new Date();
String prompt = time.toString() + ": 第" + ix + "個(gè)用戶 " + client.getInetAddress().getHostAddress() + " 已斷開連接\n";
System.out.print(prompt);
ServerDemo.writeLog(prompt); //寫入到文件中
client.close();
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
這個(gè)程序已經(jīng)被放到了服務(wù)器上,大家可以使用telnet youthol.tk 8400 命令來體驗(yàn)一下這個(gè)程序的運(yùn)行結(jié)果
希望本文所述對大家的java程序設(shè)計(jì)有所幫助。
相關(guān)文章
淺談Java回收對象的標(biāo)記和對象的二次標(biāo)記過程
這篇文章主要介紹了淺談Java回收對象的標(biāo)記和對象的二次標(biāo)記過程的相關(guān)內(nèi)容,小編覺得還是挺不錯(cuò)的,這里給大家分享一下,需要的朋友可以參考。2017-10-10
Java實(shí)現(xiàn)LeetCode(報(bào)數(shù))
這篇文章主要介紹了Java實(shí)現(xiàn)LeetCode(報(bào)數(shù)),本文通過使用java實(shí)現(xiàn)leetcode的報(bào)數(shù)題目和實(shí)現(xiàn)思路分析,需要的朋友可以參考下2021-06-06
Java利用序列化實(shí)現(xiàn)對象深度clone的方法
這篇文章主要介紹了Java利用序列化實(shí)現(xiàn)對象深度clone的方法,實(shí)例分析了java序列化及對象克隆的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-07-07
Spring Boot集成Java DSL的實(shí)現(xiàn)代碼
這篇文章主要介紹了Spring Boot集成Java DSL的實(shí)現(xiàn)代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-01-01
springboot集成nacos讀取nacos配置數(shù)據(jù)的原理
這篇文章主要介紹了springboot集成nacos讀取nacos配置數(shù)據(jù)的原理,文中有詳細(xì)的代碼流程,對大家學(xué)習(xí)springboot集成nacos有一定的幫助,需要的朋友可以參考下2023-05-05
Java實(shí)現(xiàn)文件點(diǎn)擊沒反應(yīng)的方法
jsp頁面鏈接,點(diǎn)擊訪問action用IO流去下載服務(wù)器上的文件,問題是任憑怎么點(diǎn)擊都沒反應(yīng),日志也不報(bào)錯(cuò)。這篇文章給大家介紹Java實(shí)現(xiàn)文件點(diǎn)擊沒反應(yīng)的方法,需要的朋友參考下吧2018-07-07

