分享
 
 
 

利用Java实现网络通信

王朝java/jsp·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

利用Java实现网络通信

吕昱

计算机通过TCP/UDP协议在网络上通信是建立在以下四层模型之上的:

当我们编写JAVA应用程序时,主要是在第四层即应用层工作。一般说来,不必关心TCP/UDP层,这是因为java.net包已提供了系统无关的底层实现。然而,理解TCP和UDP对于决定选择使用哪种java类,还是十分必要的。

TCP协议是以连接为基础的协议,通信前,首先要建立连接,然后才能通信。因此,能保证同步、准确地进行通信。如果应用程序需要可靠的点对点通信,一般采用TCP这种协议。比如:HTTP,ftp,telnet等应用程序,确保其可靠性对于程序运行是非常关键的。另一方面,有的应用程序对通信的要求并不一定要求如此严格。比如ping命令。我们还可以考虑这样一个例子:服务器提供一个时钟服务,它定时发送时间数据;客户端不断提取时间数据;如果对这样一个应用程序,我们也建立可靠的连接,那么,如果一个数据包丢失,客户请求重发该包,重发回的数据已失去意义了。因此,降低了该服务程序的效率和适用性。

最后,我们阐述以下另一个较重要的概念:端口。我们知道,数据通过网络到达一台主机(或准确的说是主机的网卡)是通过IP地址实现的。但当该主机运行多个程序时如何识别数据是属于哪个程序的呢?这就是端口。一个端口只能绑定一个应用程序。通过TCP/UDP通信的应用程序必须知道对方的IP地址和端口号才能通信。端口号可取从0-65535,其中,0-1023为保留端口,提供给众所周知的一些服务。下图说明了端口的使用。

下面,我们具体分析两个java程序,加深对上面概念的理解。

一、 socket和TCP

下面列出服务程序和客户程序的源代码。具体分析见代码中的注释。

该程序只能建立单一的一对连接,但根据其原理,可以编写出多线程的多对连接的复杂的网络程序。该应用的客户端程序是一个独立的应用程序,但其完全可以通过Applet来实现相同的功能。

笔者在IE4.0的环境下,通过关闭一些安全选项实现了浏览器内的Applet与服务器程序的轻松通讯。然而,在IE5.0的环境中,情况发生了变化。IE5.0的安全选项与IE4.0有一定区别。无论如何设置,都无法实现在IE4.0中的效果,显示出了”xxx.xxx.xxx.xxx:4444 can not access”这样的出错信息。通过分析,我认为,这可能是IE5.0对网络安全性的考虑加强的缘故。通过分析,我认为applet程序只应与其所在服务器上的应用程序通讯,且该服务器应该是WEB服务器。于是,我把本应用程序的服务器程序放在WEB SERVER上运行,结果获得了成功。我使用的环境是,WEB SERVER:IIS (Option pack 3);MS VJ++6.0。//下面KKState类实现一个有趣的对话过程的主要流程:用户启动服务程序,服务程序监听来自网络的连接请求,//如果有客户连接,则建立连接。然后,在客户终端示″knock!knock!″,客户端如″Who′s there?″,则//回答″Turnip″。客户输入″Turnip who?″,回答″Turnip the heat,it′s cold in here! Want another?(y/n)″。如果客户//输入″y″,则进行下一轮新的对话,否则结束。关于对话的内容事先存储在两个字符串数组中。

import java.net.*;

import java.io.*;

class KKState{

private static final int WAITING=0;

private static final int SENTKNOCKKNOCK=1;

private static final int SENTCLUE=2;

private static final int ANOTHER=3;

private static final int NUMJOKES=5;

private int state=WAITING;

private int currentJoke=0;

private Stringclues={″Tumip″,″Little Old Lady″,″Atch″,″Who″;″Who″};

private Stringanswers={″Turnip the heat,it′s cold in here!″,

″I didn′t know you could yodel!″,

″Bless you!″,

″Is there an owl in here?″,

″Is there an echo in here?″};

String processInput(String theInput){

String theOutput=null;

if(state==WAITING){

theOutput=″Knock!Knock!″;

state=SENTKNOCKKNOCK;

} else if(state==SENTKNOCKKNOCK){

if(theInput.equalsIgnoreCase(″Who′s there?″)){

theOutput=clues[currentJoke];

state=SENTCLUE;

} else{

theOutput=″You′re supposed to say\″Who′s there?\″!Try again.Knock!Knock!″;

}

}else if(state==SENTCLUE){

if(theInput.equalsIgnoreCase(clues[currentJoke]+″who?″)){

theOutput=answers[currentJoke]+″Want another?(y/n)″;

state=ANOTHER;

} else{

theOutput=″You′re supposed to say\″″+clues[currentJoke]+″who?\″″+″!Try again.Knock!Knock!″;

state=SENTKNOCKKNOCK;

}

} else if(state==ANOTHER){

if(theInput.equalsIgnoreCase(″y″)){

theOutput=″Knock!Knock!″;

if(currentJoke==(NUMJOKES-1))

currentJoke=0

else

currentJoke++;

state=SENTKNOCKKNOCK;

} else{

theOutput=″Bye.″;

state=WAITING;

}

}

return theOutput;

}

}

//服务器程序

class KnockKnockServer{

public static void main(String[]args){

ServerSocket serverSocket=null;

try {

serverSocket=new ServerSocket(4444);// 创建服务器端socket

}catch(IOException e){

System.out.println(″Could not listen on port:″+4444+″,″+e;

System.exit(1);

}

Socket clientSocket=null;

try{

clientSocket=serverSocket.accept();//监听申请连接到该socket的要求并建立连接

}catch(IOException e){

System.out.println(″Accept failed:″+4444+″,″+e);

System.exit(1);

}

try{

//通过客户端socket,获取输入数据流对象,通过它可以读入客户端输入的信息

//通过客户端socket,获取输出数据流对象,通过它可以向客户端输入的信息

DataInputStream is=new DataInputStream(new BufferedInputStream(clientSocket.getInputStream()));

PrintStream os=new PrintStream(new BufferedOutputStream(clientSocket.getOutputStream(),1024),false);

//KKState类用于处理输入的会话信息,然后返回设定好的回答

KKState kks=new KKState():

String inputLine,outputLine;

outputLine=kks.processInput(null);

os.println(outputLine);//第一次运行,输入参数为null,kks返回:″Knock!Knock!″;然后向客户端写入。

0s.flush();

//如果客户端没有输入,readLine将阻塞;如果客户端关闭,readLine返回为null。

//在下面循环语句中,以读一行,再写一行的方式与客户交互。

while((inputLine=is.readLine())!=null){

outputLine=kks.processInput(inputLine);

os.println(outputLine);

os.flush();

if(outputLine.equals(″Bye.″))break;

}

//以下完成清除工作。

os.close();

is.close();

clientSocket.close();

serverSocket.close();

} catch(IOException e){

e.printStackTrace();

}

}

}

//客户端程序

import java.io.*;

import java.net.*;

public class EchoTest{

public static void main(Stringargs){

Socket echoSocket=null;

DataOutputStream os-null;

DataInputsStream is=null;

DataInputStream stdIn=new DataInputStream(System.in);

try{ echoSocket=new Socket(″myHost″,4444);

os=new DataOutputStream(echoSocket.getOutputStream());

is=newDataInputStream(echoSocket.getInputStream());

} catch(UnknownHostException e){

System.err.println(″Don`t know about host:myHost″);

} catch(IOException e){

System.err.println(″Couldn`t get I/O for the connection to:myHost″);

}

if(echoSocket!=null && os!=null&& is!=null){

try{

String userInput;

while((userInput=stdin.readLine())!

=null){

os.writeBytes(userInput);

os.writeByte(″\n′);

System.out.println(″echo:″+is.readLine()); }

os.close();

is.close();

echoSocket.close();

} catch (IOException e){

System.err.println(″I/O failed on the connection to:myHost″);

}

}

}

}

二、 UDP

采用UDP方式,和TCP有很大的不同。TCP必须由服务器端创建socket,然后由客户端建立socket向服务器端socket发出连接请求,接着服务器端创建一个与客户端socket对应的socket并通过该socket,获得输入输出流,这样,网络通讯变类似文件I/O的方式,而所有网络通讯的复杂性被socket机制所掩盖了(参见图2);使用UDP的情况则简单一些,在通讯中,客户和服务端都只涉及了两个主要的类:DatagramSocket和DatagramPacket类。DatagramPacket类可以看为是信息的载体,必须为它建立数据缓冲区,从而容纳发送或接受的具体信息;DatagramSocket可以看成是DatagramPacket的发送或接受装置,发送一个Packet必须知道发送的主机地址和端口,接受则只需有一个Packet对象作为容器即可(参见图3)。

//本程序说明:首先服务器端生成DatagramSocket,监听某个端口,准备截获发往该端口的数据报。一旦客户端发送一针对服务器端机器和该端口的数据报,则服务器接收该数据报,从中取得数据报的发送地址和发送端口,并向该发送地址和端口发送一应答报文。报文内容为服务器端的一个文件one-liners.txt中的一行文本,如果,该文件不存在,则发送服务器端的当前时间。

//服务器端程序:

import java.io.*;

import java.net.*;

import java.util.*;

//QuoteServer只是提供服务程序的一个入口,主要的程序流程包含在QuoteServer Thread中。

class QuoteServer{

public static void main(Stringargs){

new QuoteServerThread().start();

}

}

class QuoteServerThread extends Thread{

private DatagramSocket socket=null;//用于发送和接收数据报

private DataInputStream qfs=null; //用于读取one-liners.txt文件

QuoteServerThread(){

super(″QuoteServer″);

try{

socket=new DatagramSocket();

System.out.println(″QuoteServer listening on port:″+socket.getLocalPort());//显示服务器端DatagramSocket的端口号(供客户端使用)

} catch(java.net.SocketExceptione){

System.err.println(″Could not create datagram socket.″);

}

this.openInputFile();

}

public void run(){

if(socket==null)returnm;

while(true){

try{

byte[]buf=new byte[256]; //建立内存缓冲区供DatagramPacket使用

DatagramPacket packet;

InetAddress address;

int port;

String dString=null;

packet=new DatagramPacket(buf,256);

socket.receive(packet); //接收发往该地址和端口的数据报

address=packet.getAddress(); //获取数据报的源发送地址

port=packet.getPort();获取数据报的源发送端口

if(qfs==null)

dString=new Date().toString();

else

dString=getNextQuote();

dString.getBytes(0,dString.length(),buf,0);

packet=new DatagramPacket(buf,buf.length,address,port);

socket.send(packet); //发回服务器端响应数据报

} catch(IOExceptione){

System.err.println(″IOException:″+e);

e.printStackTrace();

}

}

}

protected void finalize(){

if(socket!=null){

socket.close();

socket=null;

System.out.println(″Closing datagram socket.″);

}

}

private void openInputFile(){

try{

qfs=new DataInputStream(new FileInputStream(″one-liners.txt″));

}catch(java.io.FileNotFoundException e){

System.err.println(″Could not open quote file.Serving time instead.″);

}

}

private String getNextQuote(){

String returnValue-null;

try{

if((returnValue=qfs.readiine())==null){

qfs.close();

this.openInputFile();

returnValue=qfs.readLine(); //该文件应该至少有一行!

}

}catch(IOException e){

returnValue=″IOException occurred in server.″;

}

return returnValue;

}

}//客户端必须输入java QuoteClient主机名 端口号的形式来运行客户程序

import java.io*;

impor tjava.net.*;

impor tjava.util.*;

class QuoteClient{

public static void main(Stringargs){

int port;

InetAddress address;

DatagramSocket socket=null;

DatagramPacket packet;

bytesendBuf=new byte[256];

if(args.length!=2){

System.out.println(″Usage:java QuoteClient<hostname> <port#>″);

return;

}

try{

socket=new DatagramSocket(); //建立DatagramSocket用于发送数据报

} catch(java.net.SocketException e){

System.err.println(″Could not create datagram socket.″);

}

if(socket!=null){

try{

//发送数据

por=Integer.parseInt(args[1];

address=InetAddress.getByName(args[0]);

packet=new DatagramPacket(sendBuf,256,address,port);

socket.send(packet);

//获得应答

packet=new DatagramPacket(sendBuf,256);

socket.receive(packet);

String received=new String(packet.getData(),0);

System.out.println(″Quote of the Moment:″+received);

socket.close();

}catch(IOException e){

System.err.println(″IOException;″+e);

e.printStackTrace();

}

}

}

}

作者单位:吕昱重庆大学机械工程学院(重庆400044)

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有