在Java中实现TCP协议编程中怎么传
在Java中实现TCP协议编程
ServerSocket:编写TCP网络服务程序,首先要用到javanetServerSocket类用以创建服务器Socket
构造方法:
ServerSocket(int port):创建绑定到特定端口的服务器套接字
ServerSocket(int port, int backlog):利用指定的backlog(服务器忙时保持连接请求的等待客户数量),创建服务器套接字并将其绑定到指定的本地端口号。
ServerSocket(int port, int backlog, InetAddress bindAddr):使用指定的端口、侦听 backlog 和要绑定到的本地 IP 地址创建服务器。
Socket:客户端要与服务器建立连接,必须先创建一个Socket对象
常用构造方法
Socket(String host, int port):创建一个流套接字并将其连接到指定主机上的指定端口号。
Socket(InetAddress address, int port):创建一个流套接字并将其连接到指定 IP 地址的指定端口号。
服务器端程序调用ServerSocket类中的accept()方法等待客户端的连接请求,一旦accept()接收了客户端连接请求,该方法返回一个与该客户端建立了专线连接的Socket对象,不用程序去创建这个Socket对象。建立了连接的两个Socket是以IO流的方式进行数据交换的,Java提供了Socket类中的getInputStream()返回Socket的输入流对象,getOutputStream()返回Socket的输出流对象。
TCP服务器与TCP客户端间的数据的接受图示:
创建一个TCP服务器端程序的步骤
(1)创建一个ServerSocket
(2)从ServerSocket接受客户连接请求
(3)创建一个服务线程处理新的连接
(4)在服务线程中,从socket中获得I/O流
(5)对I/O流进行读写操作,完成与客户的交互
(6)关闭I/O流
(7)关闭Socket
ServerSocket server=new ServerSocket(port)
Socket s =serveraccept();
ObjectInputStream in=new ObjectInputStream(sgetInputStream());
ObjectOutputStream out=new ObjectOutputStream(sgetOutputStream());
outclose();
inclose();
sclose();
创建一个TCP客户端程序的步骤
(1)创建Socket
(2)获得I/O流
(3)对I/O流进行读写操作
(4)关闭I/O流
(5)关闭Socket
Socket connection =new Socket(127001,9009);
ObjectInputStream input=new ObjectInputStream(connectiongetInputStream());
ObjectOutputStream output=new ObjectOutputStream(connectiongetOutputStream());
outputclose();
inputclose();
connectionclose();
用TCP实现服务器与客户端的“聊天”:
实例代码:
客户端:
package comhbsinet;
import javanetSocket;
import javaio;
public class TcpClient{
public static void main(String[] args) throws Exception {
// 1建立tcp客户端socket,要确定要连接的服务器ip,port
Socket s = new Socket("1921684987", 9009);
// 获取键盘录入
BufferedReader br = new BufferedReader(new InputStreamReader(Systemin));
// 2通过建立的socket,获取输出流对象
// 数据输出给服务器端
OutputStream out = sgetOutputStream();
BufferedWriter bwout = new BufferedWriter(new OutputStreamWriter(out));
// 获取服务器端返回的数据
// 读取服务器端发过来的信息InputStreamReader()
BufferedReader brin = new BufferedReader(new InputStreamReader(
sgetInputStream()));
String line = null;
while ((line = brreadLine()) != null) {
if (lineequals("over"))
break;
bwoutwrite(line);
bwoutnewLine();
bwoutflush();
String str = brinreadLine();
Systemoutprintln("server:" + str);
}
brclose();
sclose();
}
}
服务器端:
package comhbsinet;
import javaioBufferedReader;
import javaioBufferedWriter;
import javaioInputStream;
import javaioInputStreamReader;
import javaioOutputStreamWriter;
import javanetServerSocket;
import javanetSocket;
public class TcpServer{
public static void main(String[] args) throws Exception {
// 1建立服务器socket
ServerSocket ss = new ServerSocket(9009);
// 2调用accept()
Socket s = ssaccept();
Systemoutprintln(sgetInetAddress()getHostAddress()
+ "connection");
// 读取客户的信息的输入流
InputStream in = sgetInputStream();
BufferedReader brin = new BufferedReader(new InputStreamReader(in));
// 向客户端发送信息输出流,服务端向客户端返回信息OutputStreamWriter()
BufferedWriter brout = new BufferedWriter(new OutputStreamWriter(
sgetOutputStream())); String line = null;
while ((line = brinreadLine()) != null) {
Systemoutprintln("client:" + line);
broutwrite(linetoUpperCase());//服务器端收到信息后,将信息转为大写返回给客户端toUpperCase()
broutnewLine();
broutflush();
}
sclose();
ssclose();
}
}
要完成这个工作,需要完成三个部分的工作,以下依次说明:
一、建立服务器类
Java中有一个专门用来建立Socket服务器的类,名叫ServerSocket,可以用服务器需要使用的端口号作为参数来创建服务器对象。
ServerSocket server = new ServerSocket(9998)
这条语句创建了一个服务器对象,这个服务器使用9998号端口即在端口9998上注册服务,这里稍微要注意的是端口的分配必须是唯一的。因为端口是为了唯一标识每台计算机唯一服务的,另外端口号是从0~65535之间的,前1024个端口已经被Tcp/Ip 作为保留端口,因此你所分配的端口只能是1024个之后的。当一个客户端程序建立一个Socket连接,所连接的端口号为9998时,服务器对象server便响应这个连接,并且serveraccept()方法会创建一个Socket对象。服务器端便可以利用这个Socket对象与客户进行通讯。
Socket incoming = serveraccept() ; // 监听窗口,等待连接
进而得到输入流和输出流,并进行封装
BufferedReader in = new BufferedReader(new
InputStreamReader(incominggetInputStream()));
/
当读取文件时,先把内容读到缓存中,当调用inreadLine()时,再从缓存中以字符的方式读取数据(以下简称“缓存字节读取方式”)。
/
PrintWriter ut = new PrintWriter(incominggetOutputStream(),true);
随后,就可以使用inreadLine()方法得到客户端的输入,也可以使用outprintln()方法向客户端发送数据。从而可以根据程序的需要对客户端的不同请求进行回应。
在所有通讯结束以后应该关闭这两个数据流,关闭的顺序是先关闭输出流,再关闭输入流,即使用
outclose();
inclose();
二、建立客户端代码
相比服务器端,客户端要简单一些,客户端只需用服务器所在机器的ip以及服务器的端口作为参数创建一个Socket对象。得到这个对象后,就可以用"建立服务器"部分介绍的方法实现数据的输入和输出。
Socket socket = new Socket("1681601242",9998);
或:
Socket socket = new Socket(InetAddressgetLocalHost(),5678); // 向主机名为InetAddressgetLocalHost()的服务器申请连接
客户机必须知道有关服务器的IP地址,对于着一点Java也提供了一个相关的类InetAddress 该对象的实例必须通过它的静态方法来提供,它的静态方法主要提供了得到本机IP 和通过名字或IP直接得到InetAddress的方法。
in = new BufferedReader(new InputStreamReader(socketgetInputStream()));
out = new PrintWriter(socketgetOutputStream(),true);
以上的程序代码建立了一个Socket对象,这个对象连接到ip地址为1681601242的主机上、端口为9998的服务器对象。并且建立了输入流和输出流,分别对应服务器的输出和客户端的写入。
三、实例分析
服务方:
import javaio;
import javanet;
public class MyServer {
public static void main(String[] args) throws IOException{
ServerSocket server=new ServerSocket(5678); //在端口5678上注册服务
Socket client=serveraccept(); // 监听窗口,等待连接
BufferedReader in=new BufferedReader(new InputStreamReader(clientgetInputStream()));
BufferedReader serverInput=new BufferedReader(new InputStreamReader(Systemin));
PrintWriter ut=new PrintWriter(clientgetOutputStream());
while(true){
String str=inreadLine(); //// 读取从client传来的数据信息
str = serverInputreadLine(); // 读取用户键盘输入的字符串
Systemoutprintln(str); //服务器控制台输出数据信息
outprintln("has receive"); //服务器向客户端发送信息:has receive
outflush();
if(strequals("end"))
break;
}
clientclose();
}
}
这个程序的主要目的在于服务器不断接收客户机所写入的信息只到,客户机发送"End"字符串就退出程序,并且服务器也会做出"Receive"为回应,告知客户机已接收到消息。
客户机代码:
import javanet;
import javaio;
public class Client{
static Socket server;
public static void main(String[] args)throws Exception{
server=new Socket(InetAddressgetLocalHost(),5678); // 向主机名为InetAddressgetLocalHost()的服务器申请连接
BufferedReader in=new BufferedReader(new InputStreamReader(servergetInputStream())); //客户端建立输入流并进行封装
PrintWriter ut=new PrintWriter(servergetOutputStream());
BufferedReader wt=new BufferedReader(new InputStreamReader(Systemin)); //客户端从键盘输入信息
while(true){
String str=wtreadLine(); //客户端读取(获得)键盘的字符串
String str1=inreadLine(); // 从服务器获得字符串
outprintln(str); //客户端向服务器发送信息
outflush();
if(strequals("end")){
break;
}
Systemoutprintln(inreadLine());
}
serverclose();
}
}
客户机代码则是接受客户键盘输入,并把该信息输出,然后输出"End"用来做退出标识。
这个程序只是简单的两台计算机之间的通讯,如果是多个客户同时访问一个服务器呢?你可以试着再运行一个客户端,结果是会抛出异常的。那么多个客户端如何实现呢
其实,简单的分析一下,就可以看出客户和服务通讯的主要通道就是Socket本身,而服务器通过accept方法就是同意和客户建立通讯这样当客户建立Socket的同时。服务器也会使用这一根连线来先后通讯,那么既然如此只要我们存在多条连线就可以了。那么我们的程序可以变为如下:
服务器:
import javaio;
import javanet;
public class MyServer {
public static void main(String[] args) throws IOException{
ServerSocket server=new ServerSocket(5678);
while(true){
Socket client=serveraccept();
BufferedReader in=new BufferedReader(new InputStreamReader(clientgetInputStream()));
PrintWriter ut=new PrintWriter(clientgetOutputStream());
while(true){
String str=inreadLine();
Systemoutprintln(str);
outprintln("has receive");
outflush();
if(strequals("end"))
break;
}
clientclose();
}
}
}
这里仅仅只是加了一个外层的While循环,这个循环的目的就是当一个客户进来就为它分配一个Socket直到这个客户完成一次和服务器的交互,这里也就是接受到客户的"End"消息那么现在就实现了多客户之间的交互了。但是问题又来了,这样做虽然解决了多客户,可是是排队执行的。也就是说当一个客户和服务器完成一次通讯之后下一个客户才可以进来和服务器交互,无法做到同时服务,那么要如何才能同时达到既能相互之间交流又能同时交流呢?很显然这是一个并行执行的问题了。所以线程是最好的解决方案。
那么下面的问题是如何使用线程首先要做的事情是创建线程并使得其可以和网络连线取得联系。然后由线程来执行刚才的操作,要创建线程要么直接继承Thread要么实现Runnable接口,要建立和Socket的联系只要传递引用就可以了而要执行线程就必须重写run方法,而run方法所做的事情就是刚才单线程版本main所做的事情,因此我们的程序变成了这样:
import javanet;
import javaio;
public class MultiUser extends Thread{
private Socket client;
public MultiUser(Socket c){
thisclient=c;
}
public void run(){
try{
BufferedReader in=new BufferedReader(new InputStreamReader(clientgetInputStream()));
PrintWriter ut=new PrintWriter(clientgetOutputStream());
//Mutil User but can't parallel
while(true){
String str=inreadLine();
Systemoutprintln(str);
outprintln("has receive");
outflush();
if(strequals("end"))
break;
}
clientclose();
}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(serveraccept());
mustart();
}
}
}
我的类直接从Thread类继承了下来并且通过构造函数传递引用和客户Socket建立了联系,这样每个线程就有了。一个通讯管道同样我们可以填写run方法,把之前的操作交给线程来完成,这样多客户并行的Socket就建立起来了。
创建两个类 分别添加main方法 一个作为server类一个作为client类
客户端先发送再接收 服务器端先接收再发送
下面给你一个server和client的例子 你在基础上改一下就行了
client发送的时候发送三边值 server接收后 写一个计算三角形面积的函数 把接收到的三边值传入函数计算出结果作为数据返回到client端
/UDPServer
/
import javaio;
import javanet;
class UDPServer{
public static void main(String[] args)throws IOException{
DatagramSocket server = new DatagramSocket(5050);
byte[] recvBuf = new byte[100];
DatagramPacket recvPacket
= new DatagramPacket(recvBuf , recvBuflength);
serverreceive(recvPacket);
String recvStr = new String(recvPacketgetData() , 0 , recvPacketgetLength());
Systemoutprintln("Hello World!" + recvStr);
int port = recvPacketgetPort();
InetAddress addr = recvPacketgetAddress();
String sendStr = "Hello ! I'm Server";
byte[] sendBuf;
sendBuf = sendStrgetBytes();
DatagramPacket sendPacket
= new DatagramPacket(sendBuf , sendBuflength , addr , port );
serversend(sendPacket);
serverclose();
}
}/
UDPClient
/
import javaio;
import javanet;
class UDPClient{
public static void main(String[] args)throws IOException{
DatagramSocket client = new DatagramSocket();
String sendStr = "Hello! I'm Client";
byte[] sendBuf;
sendBuf = sendStrgetBytes();
InetAddress addr = InetAddressgetByName("127001");
int port = 5050;
DatagramPacket sendPacket
= new DatagramPacket(sendBuf ,sendBuflength , addr , port);
clientsend(sendPacket);
byte[] recvBuf = new byte[100];
DatagramPacket recvPacket
= new DatagramPacket(recvBuf , recvBuflength);
clientreceive(recvPacket);
String recvStr = new String(recvPacketgetData() , 0 ,recvPacketgetLength());
Systemoutprintln("收到:" + recvStr);
clientclose();
}
}
Socket TCP
反射API介绍
4Java Socket编程 TCP 协议编程
1) TCP工作模型: 先找
堂(主机/IP), 到食堂以后找窗口
(Socket/套接字 端口号), 服务员等待连接,客户向服务员发起连接
连接以后, 一个窗口可以为每个客户安排一个服务员(线程)提供服务,
每个服务过程可以双向交流通讯(流), 通讯完成后要关闭连接
5 TCP 服务端编程(食堂)(javaio,javanet,javalang)
1) 创建ServerSocket实例绑定一个服务端口(Socket/套接字 端口号)
2) 开始ServerSocket实例 的监听, 等待客户端的连接
3) 如果有客户连接进来, 就获得了客户的套接字(Socket)实例
客户的套接字(Socket)实例中包括与客户端建立的连接流
4) 为这个客户(Socket) 创建一个服务线程, 提供服务(run方法)
5) 继续等待下一个连接, 返回到2)
6) 服务线程 完成通讯服务过程
7) 端口号: 0~65535, 1K以下留给系统使用
6 TCP 客户端编程
1) 创建Socket 实例, 连接到服务器端, 成功创建s就表示连接到了
服务器
Socket s = new Socket("host", port)
2) 客户端 Socket 与服务器端 Socket 对应, 都包含输入, 输出流
客户端的sgetInputStream() 连接于服务器sgetOutputStream()
客户端的sgetOutputStream()连接于服务器sgetInputStream()
3) 使用线程处理 网络流
7 Java 反射
1) 反射是Java自我管理(类, 对象)的机制
2) 可以通过反射机制发现对象的类型 发现类型的方法/属性/构造器
3) Java 反射 可以创建对象 并 访问任意对象方法和属性等
4) Class 加载
类加载到内存: java 将磁盘类文件加载到内存中,为一个对象(实例)
这个对象是Class的实例, 也就是 这些对象都是Class实例
5)Class 实例代表Java中类型, 基本类型的类型: intclass, longclass
类类型 Class 实例获得如下:
Class cls = Stringclass;
Class cls = ClassforName("javalangString");
Class cls = "abc"getClass();
以上方法获得cls 是同一个对象, 就是String 类内存加载的结果
package javase2day06ftp;
import javaioBufferedOutputStream;
import javaioFile;
import javaioFileOutputStream;
import javaioIOException;
import javaioInputStream;
import javaioOutputStream;
import javanetSocket;
import javautilScanner;
/
ftp 客户端 可以使用命令 ls pwd get
/
public class FtpClient {
public static void main(String[] args)
throws IOException{
FtpClient client = new FtpClient();
clientopen();
}
public void open() throws IOException{
Socket s = new Socket("localhost", 9000);
InputStream in = sgetInputStream();
OutputStream out = sgetOutputStream();
//处理客户端对服务器的请求
new RequestProcess(out)start();
//处理服务器的反馈信息
new ResponseProcess(in)start();
}
//处理客户端对服务器的请求
class RequestProcess extends Thread{
OutputStream out;
public RequestProcess(OutputStream out) {
thisout = out;
}
public void run() {
try{
Scanner sc = new Scanner(Systemin);
while(true){
String s = scnextLine();
IOUtilsprintln(out, s);
if(sequals("bye")){
Systemexit(0);
}
}
}catch(IOException e){
eprintStackTrace();
}
}
}
class ResponseProcess extends Thread{
InputStream in;
public ResponseProcess(InputStream in) {
thisin = in;
}
public void run() {
try{
while(true){
String header = IOUtilsreadLine(in);
if(headerstartsWith("text,")){
show(header,in);
}else if(headerstartsWith("file,")){
save(header, in);
}
}
}catch(IOException e){
eprintStackTrace();
}
}
}
public void show(String header, InputStream in)
throws IOException {
int n = IntegerparseInt(headersplit(",")[1]);
for(int i=0; i<n; i++){
String s = IOUtilsreadLine(in);
Systemoutprintln(s);
}
}
public void save(String header, InputStream in)
throws IOException{
File dir = new File("ftp");
if(!direxists()){
dirmkdir();
}
//header: file,10,filename
String[] data = headersplit(",");
long length = LongparseLong(data[1]);
String filename = data[2];
File file = new File(dir, filename);
BufferedOutputStream out =
new BufferedOutputStream(
new FileOutputStream(file));
for(long i=0; i<length; i++){
int b = inread();
outwrite(b);
}
outclose();
}
}
package javase2day06ftp;
import javaioFile;
import javaioFileInputStream;
import javaioIOException;
import javaioInputStream;
import javaioOutputStream;
import javanetServerSocket;
import javanetSocket;
/
模拟FTP服务器, 支持命令pwd,ls,get file
协议: type,length,value TLV格式
文本: text,5\nline1\nline2\nline3\nline4\nline5\n
文件: file,4,filename\n 41 42 43 44
text,5\nline1\nline2\nline3\nline4\nline5\nfile,4,filename\n 41 42 43 44
/
public class FtpServer {
public static void main(String[] args)
throws IOException{
FtpServer server = new FtpServer();
serverstart();
}
public void start() throws IOException{
ServerSocket ss = new ServerSocket(9000);
while(true){
Socket s = ssaccept();
new Agent(s)start();
}
}
class Agent extends Thread{
Socket s;
public Agent(Socket s) {
thiss = s;
}
public void run() {
try{
InputStream in = sgetInputStream();
OutputStream out = sgetOutputStream();
//向客户端发送, 先发协议头,再发送文本行
outwrite("text,1\n"getBytes());//协议头
//发送消息内容, 一行文本消息
outwrite("欢迎使用FTP演示服务器!\n"getBytes());
outflush();
while(true){
//读取客户端发送到命令
String cmd = IOUtilsreadLine(in)trim();
if("pwd"equals(cmd)){//显示当前目录
pwd(out);
}else if("ls"equals(cmd)){
ls(out);
}else if(cmdstartsWith("get ")){
get(cmd, out);
}else if("bye"equalsIgnoreCase(cmd)){
IOUtilsprintln(out, "text,1");
IOUtilsprintln(out, "Bye, Bye!");
sclose();
}else{
outwrite("text,1\n"getBytes());//协议头
outwrite("只支持pwd,ls,get,bye!\n"getBytes());
outflush();
}
}
}catch(IOException e){
eprintStackTrace();
}
}
}
public void pwd(OutputStream out)
throws IOException{
File dir = new File("");
IOUtilsprintln(out, "text,1");
IOUtilsprintln(out, dirgetCanonicalPath());
}
public void ls(OutputStream out)
throws IOException{
File dir = new File("");
File[] files = dirlistFiles();
IOUtilsprintln(out, "text,"+fileslength);
for (File f : files) {
if(fisDirectory()){
IOUtilsprintln(out, "["+fgetName()+"]");
}else{
IOUtilsprintln(out, fgetName());
}
}
}
public void get(String cmd, OutputStream out)
throws IOException{
//cmd="get filename"
String name = cmdsplit("\\s+")[1];
File file = new File(name);
if(! fileexists()){
IOUtilsprintln(out, "text,1");
IOUtilsprintln(out, "没有文件呀!"+name);
return;
}
//文件协议头:
IOUtilsprintln(out,
"file,"+filelength()+","+name);
FileInputStream in =
new FileInputStream(file);
IOUtilscp(in, out);
outflush();
inclose();
IOUtilsprintln(out, "text,1");
IOUtilsprintln(out, "发送成功:"+name);
}
}
Java 远程处理
Java远程方法调用(RMI)提供了Java程序语言的远程通讯功能,这种特性使客户机上运行的程序可以调用远程服务器上的对象,使Java编程人员能够在网络环境中分布操作。
创建一个简单的Java分布式远程方法调用程序可以按以下几个步骤操作,
一、定义远程接口:
在 Java 中,远程对象是实现远程接口的类的实例, 远程接口声明每个要远程调用的方法。在需要创建一个远程对象的时候,我们通过传递一个接口来隐藏基层的实施细节,客户通过接口句柄发送消息即可。
远程接口具有如下特点:
1) 远程接口必须为public属性。如果不这样,除非客户端与远程接口在同一个包内,否则 当试图装入实现该远程接口的远程对象时,调用会得到错误结果。
2) 远程接口必须扩展接口javarmiRemote。
3) 除与应用程序本身特定的例外之外,远程接口中的每个方法都必须在自己的throws从句中 声明javarmiRemoteException。(或 RemoteException 的父类)。
4) 作为参数或返回值传递的一个远程对象(不管是直接,还是本地对象中嵌入)必须声明为远 程接口,而不应声明为实施类。
下面是远程接口的定义
[java] view plaincopy
package test;
import javarmiRemote;
import javarmiRemoteException;
import javamathBigInteger;
public interface Fib extends Remote {
public int getFib(int n) throws RemoteException;
// public BigInteger getFib(BigInteger n) throws RemoteException;
}
二、实现远程接口:
远程对象实现类必须扩展远程对象javarmiUnicastRemoteObject类,并实现所定义的远程接口。远程对象的实现类中包含实现每个远程接口所指定的远程方法的代码。这个类也可以含有附加的方法,但客户只能使用远程接口中的方法。因为客户是指向接口的一个句柄,而不是它的哪个类。必须为远程对象定义构造函数,即使只准备定义一个默认构造函数,用它调用基础类构造函数。因为基础类构造函数可能会抛出 javarmiRemoteException,所以即使别无它用必须抛出javarmiRemoteException例外。
以下是远程对象实现类的声明:
[java] view plaincopy
package test;
import javamathBigInteger;
import javarmi;
import javarmiserverUnicastRemoteObject;
public class FibImp extends UnicastRemoteObject implements Fib {
public FibImp() throws RemoteException {
super();
}
public int getFib(int n) throws RemoteException {
return n+2;
}
}
三、编写服务器类:
包含 main 方法的类可以是实现类自身,也可以完全是另一个类。下面通过RmiSampleServer 来创建一个远程对象的实例,并通过javarmiregistryLocateRegistry类的createRegistry 方法从指定端口号启动注册服务程序,也可以通过执行 rmiregistry 命令启动注册服务程序,注册服务程序的缺省运行端口为 1099。必须将远程对象名字绑定到对远程对象的引用上: Namingrebind("//localhost:8808/SAMPLE-SERVER" , Server);
以下是服务器类的声明:
[java] view plaincopy
package test;
import javanetMalformedURLException;
import javarmiNaming;
import javarmiRemoteException;
import javarmiregistryLocateRegistry;
public class FibonacciServer {
/
@param args
/
public static void main(String[] args) {
try {
LocateRegistrycreateRegistry(8804);
FibImp f = new FibImp();
// 注册到 registry 中
Namingrebind("//localhost:8804/SAMPLE-SERVER", f);
Systemoutprintln("fib server ready");
} catch (RemoteException re) {
Systemoutprintln("Exception in FibonacciImplmain: " + re);
} catch (MalformedURLException e) {
Systemoutprintln("MalformedURLException " + e);
}
}
}
四、编写使用远程服务的客户机类:
客户机类的主要功能有两个,一是通过Naminglookup方法来构造注册服务程序 stub 程序实例,二是调用服务器远程对象上的远程方法。
以下是客户端类的声明:
[java] view plaincopy
package testClient;
import testFib;
import javamathBigInteger;
import javanetMalformedURLException;
import javarmiNaming;
import javarmiNotBoundException;
import javarmiRemoteException;
public class FibClient {
/
@param args
/
public static void main(String[] args) {
String url = "//localhost:8804/SAMPLE-SERVER";
try {
Fib calc = (Fib) Naminglookup(url);
for (int i = 0; i < 10; ++i) {
int f = calcgetFib(i);
Systemoutprintln(f);
}
} catch (MalformedURLException e) {
eprintStackTrace();
} catch (RemoteException e) {
eprintStackTrace();
} catch (NotBoundException e) {
eprintStackTrace();
}
}
}
用异步输入输出流编写Socket进程通信程序
在Merlin中加入了用于实现异步输入输出机制的应用程序接口包:javanio(新的输入输出包,定义了很多基本类型缓冲(Buffer)),javaniochannels(通道及选择器等,用于异步输入输出),javaniocharset(字符的编码解码)。通道(Channel)首先在选择器(Selector)中注册自己感兴趣的事件,当相应的事件发生时,选择器便通过选择键(SelectionKey)通知已注册的通道。然后通道将需要处理的信息,通过缓冲(Buffer)打包,编码/解码,完成输入输出控制。
通道介绍:
这里主要介绍ServerSocketChannel和 SocketChannel它们都是可选择的(selectable)通道,分别可以工作在同步和异步两种方式下(注意,这里的可选择不是指可以选择两种工作方式,而是指可以有选择的注册自己感兴趣的事件)。可以用channelconfigureBlocking(Boolean )来设置其工作方式。与以前版本的API相比较,ServerSocketChannel就相当于ServerSocket(ServerSocketChannel封装了ServerSocket),而SocketChannel就相当于Socket(SocketChannel封装了Socket)。当通道工作在同步方式时,编程方法与以前的基本相似,这里主要介绍异步工作方式。
所谓异步输入输出机制,是指在进行输入输出处理时,不必等到输入输出处理完毕才返回。所以异步的同义语是非阻塞(None Blocking)。在服务器端,ServerSocketChannel通过静态函数open()返回一个实例serverChl。然后该通道调用serverChlsocket()bind()绑定到服务器某端口,并调用register(Selector sel, SelectionKeyOP_ACCEPT)注册OP_ACCEPT事件到一个选择器中(ServerSocketChannel只可以注册OP_ACCEPT事件)。当有客户请求连接时,选择器就会通知该通道有客户连接请求,就可以进行相应的输入输出控制了;在客户端,clientChl实例注册自己感兴趣的事件后(可以是OP_CONNECT,OP_READ,OP_WRITE的组合),调用clientChlconnect(InetSocketAddress )连接服务器然后进行相应处理。注意,这里的连接是异步的,即会立即返回而继续执行后面的代码。
选择器和选择键介绍:
选择器(Selector)的作用是:将通道感兴趣的事件放入队列中,而不是马上提交给应用程序,等已注册的通道自己来请求处理这些事件。换句话说,就是选择器将会随时报告已经准备好了的通道,而且是按照先进先出的顺序。那么,选择器是通过什么来报告的呢?选择键(SelectionKey)。选择键的作用就是表明哪个通道已经做好了准备,准备干什么。你也许马上会想到,那一定是已注册的通道感兴趣的事件。不错,例如对于服务器端serverChl来说,可以调用keyisAcceptable()来通知serverChl有客户端连接请求。相应的函数还有:SelectionKeyisReadable(),SelectionKeyisWritable()。一般的,在一个循环中轮询感兴趣的事件(具体可参照下面的代码)。如果选择器中尚无通道已注册事件发生,调用Selectorselect()将阻塞,直到有事件发生为止。另外,可以调用selectNow()或者select(long timeout)。前者立即返回,没有事件时返回0值;后者等待timeout时间后返回。一个选择器最多可以同时被63个通道一起注册使用。
应用实例:
下面是用异步输入输出机制实现的客户/服务器实例程序――程序清单1(限于篇幅,只给出了服务器端实现,读者可以参照着实现客户端代码):
程序类图
public class NBlockingServer {
int port = 8000;
int BUFFERSIZE = 1024;
Selector selector = null;
ServerSocketChannel serverChannel = null;
HashMap clientChannelMap = null;//用来存放每一个客户连接对应的套接字和通道
public NBlockingServer( int port ) {
thisclientChannelMap = new HashMap();
thisport = port;
}
public void initialize() throws IOException {
//初始化,分别实例化一个选择器,一个服务器端可选择通道
thisselector = Selectoropen();
thisserverChannel = ServerSocketChannelopen();
thisserverChannelconfigureBlocking(false);
InetAddress localhost = InetAddressgetLocalHost();
InetSocketAddress isa = new InetSocketAddress(localhost, thisport );
thisserverChannelsocket()bind(isa);//将该套接字绑定到服务器某一可用端口
}
//结束时释放资源
public void finalize() throws IOException {
thisserverChannelclose();
thisselectorclose();
}
//将读入字节缓冲的信息解码
public String decode( ByteBuffer byteBuffer ) throws
CharacterCodingException {
Charset charset = CharsetforName( "ISO-8859-1" );
CharsetDecoder decoder = charsetnewDecoder();
CharBuffer charBuffer = decoderdecode( byteBuffer );
String result = charBuffertoString();
return result;
}
//监听端口,当通道准备好时进行相应操作
public void portListening() throws IOException, InterruptedException {
//服务器端通道注册OP_ACCEPT事件
SelectionKey acceptKey =thisserverChannelregister( thisselector,
SelectionKeyOP_ACCEPT );
//当有已注册的事件发生时,select()返回值将大于0
while (acceptKeyselector()select() > 0 ) {
Systemoutprintln("event happened");
//取得所有已经准备好的所有选择键
Set readyKeys = thisselectorselectedKeys();
//使用迭代器对选择键进行轮询
Iterator i = readyKeysiterator();
while (i
else if ( keyisReadable() ) {//如果是通道读准备好事件
Systemoutprintln("Readable");
//取得选择键对应的通道和套接字
SelectableChannel nextReady =
(SelectableChannel) keychannel();
Socket socket = (Socket) keyattachment();
//处理该事件,处理方法已封装在类ClientChInstance中
thisreadFromChannel( socketgetChannel(),
(ClientChInstance)
thisclientChannelMapget( socket ) );
}
else if ( keyisWritable() ) {//如果是通道写准备好事件
Systemoutprintln("writeable");
//取得套接字后处理,方法同上
Socket socket = (Socket) keyattachment();
SocketChannel channel = (SocketChannel)
socketgetChannel();
thiswriteToChannel( channel,"This is from server!");
}
}
}
}
//对通道的写操作
public void writeToChannel( SocketChannel channel, String message )
throws IOException {
ByteBuffer buf = ByteBufferwrap( messagegetBytes() );
int nbytes = channelwrite( buf );
}
//对通道的读操作
public void readFromChannel( SocketChannel channel, ClientChInstance clientInstance )
throws IOException, InterruptedException {
ByteBuffer byteBuffer = ByteBufferallocate( BUFFERSIZE );
int nbytes = channelread( byteBuffer );
byteBufferflip();
String result = thisdecode( byteBuffer );
//当客户端发出”@exit”退出命令时,关闭其通道
if ( resultindexOf( "@exit" ) >= 0 ) {
channelclose();
}
else {
clientInstanceappend( resulttoString() );
//读入一行完毕,执行相应操作
if ( resultindexOf( "\n" ) >= 0 ){
Systemoutprintln("client input"+result);
clientInstanceexecute();
}
}
}
//该类封装了怎样对客户端的通道进行操作,具体实现可以通过重载execute()方法
public class ClientChInstance {
SocketChannel channel;
StringBuffer buffer=new StringBuffer();
public ClientChInstance( SocketChannel channel ) {
thischannel = channel;
}
public void execute() throws IOException {
String message = "This is response after reading from channel!";
writeToChannel( thischannel, message );
buffer = new StringBuffer();
}
//当一行没有结束时,将当前字窜置于缓冲尾
public void append( String values ) {
bufferappend( values );
}
}
//主程序
public static void main( String[] args ) {
NBlockingServer nbServer = new NBlockingServer(8000);
try {
nbServerinitialize();
} catch ( Exception e ) {
eprintStackTrace();
Systemexit( -1 );
}
try {
nbServerportListening();
}
catch ( Exception e ) {
eprintStackTrace();
}
}
}
程序清单1
小结:
从以上程序段可以看出,服务器端没有引入多余线程就完成了多客户的客户/服务器模式。该程序中使用了回调模式(CALLBACK)。需要注意的是,请不要将原来的输入输出包与新加入的输入输出包混用,因为出于一些原因的考虑,这两个包并不兼容。即使用通道时请使用缓冲完成输入输出控制。该程序在Windows2000,J2SE14下,用telnet测试成功。
本篇文章主要介绍了JPype实现在python中调用JAVA的实例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
一、JPype简述
1JPype是什么?
JPype是一个能够让 python 代码方便地调用 Java 代码的工具,从而克服了 python 在某些领域(如服务器端编程)中的不足。
2JPype与Jython(JPython后继者)的区别?
1)运行环境不同:jython运行在jvm上,而JPype的实际运行环境仍然是python runtime,只是在运行期间启动了一个嵌入的jvm;
2)使用者不同:jython是给java程序玩的,JPype是给python程序员玩的。
二、JPype安装
1先安装Python27和JAVA16
2安装JPype-0542win32-py27exe(http://sourceforgenet/projects/jpype/files/JPype/054/)
3Ubuntu1204安装命令:sudo apt-get install python-jpype
三、JPype使用说明
1启动JVM
JPype 提供的 startJVM() 函数的作用是启动 JAVA 虚拟机,所以在后续的任何 JAVA 代码被调用前,必须先调用此方法启动 JAVA 虚拟机。
jpypestartJVM() 的定义
startJVM(jvm, args)jpypestartJVM() 的参数
参数 1: jvm, 描述你系统中 jvmdll 文件所在的路径,如“ C:Program FilesIBMJava50jreinj9vmjvmdll ”。可以通过调用 jpypegetDefaultJVMPath() 得到默认的 JVM 路径。
参数 2: args, 为可选参数,会被 JPype 直接传递给 JVM 作为 Java 虚拟机的启动参数。此处适合所有合法的 JVM 启动参数,例如:
-agentlib:libname[=options]
-classpath classpath
-verbose
-Xint 2关闭JVM
当使用完 JVM 后,可以通过 jpypeshutdownJVM() 来关闭 JVM,该函数没有输入参数。当 python 程序退出时,JVM 会自动关闭。
3引用第三方Java扩展包
很多时候,在 python 项目中需要调用第三方的 Java 扩展包,这也是 JPype 的一个重要用途。
通过在 JVM 启动参数增加:-Djavaclasspath=ext_classpath,实现在 python 代码中调用已有的 Java 扩展包。
4访问JAVA的系统属性
有时,某些 Java 应用需要设置或者获取 JVM 中的系统属性。
在 JVM 启动时设置系统变量示例:
在 JVM 的启动参数中加入如下参数:
-Dproperty=value四、举例
1直接调用JAVA API
from jpype import
import ospath
startJVM("C:/Java/jdk160_10/jre/bin/client/jvmdll", "-ea")
javalangSystemoutprintln("hello World")
shutdownJVM()2调用JAVA第三方扩展包
1)JAVA自定义第三方jar包:将JpypeDemo类打包为jpypedemojar文件并存储到F:/sample_Py目录下
package jpype;
public class JpypeDemo {
public String sayHello(String user){
return "hello" + user;
}
public int calc(int a, int b){
return a + b;
}
} 2)Python调用第三方JAVA jar包程序
from jpype import
import ospath
jarpath = ospathjoin(ospathabspath(''), 'F:/sample_Py/')
startJVM("C:/Java/jdk160_10/jre/bin/client/jvmdll","-ea", "-Djavaclasspath=%s" % (jarpath + 'jpypedemojar'))
#ubuntu 中startJVM("/home/geek/Android/jdk160_43/jre/lib/i386/server/libjvmso","-ea", "-Djavaclasspath=%s" % (jarpath + 'XXXjar'))
JDClass = JClass("jpypeJpypeDemo")
jd = JDClass()
#jd = JPackage("jpype")JpypeDemo() #两种创建jd的方法
jprint = javalangSystemoutprintln
jprint(jdsayHello("waw"))
jprint(jdcalc(2,4))
shutdownJVM()3访问JAVA的系统属性
假设你要设置的属性名为 yourProperty,属性值为 yourValue 。
1)JVM启动时设置系统变量示例
import jpype
jvmPath = jpypegetDefaultJVMPath()
jvmArg = “ -DyourProperty=yourValue ”
if not jpypeisJVMStarted():
jpypestartJVM(jvmPath,jvmArg)2)在程序中设置系统变量示例
import jpype
prop = “ yourProperty ”
value = “ yourValue ”
system = jpypeJClass('javalangSystem')
systemsetProperty(str(prop),str(value))3)在程序中获取系统变量示例
import jpype
prop = “ yourProperty ”
system = jpypeJClass('javalangSystem')
value = systemgetProperty(str(prop))
0条评论