JAVA网络
JAVA网络
1.客户端与服务器
网络和java是紧密结合的,java API提供用于创建套接字的类便于程序的网络通讯。套接字(socket)是两台主机之间逻辑连接的端点,可以用来发送和接受数据,类似于输入输出操作。Socket可以赋值为null。网络程序设计通常涉及一个服务器和一个或多个客户端,客户端向服务器发送请求,而服务器响应请求,一旦建立连接,客户端和服务器就可以通过套接字进行通信。套接字能执行7种基本操作:
- 连接到远程机器。
- 绑定到端口。
- 接受从远程机器来的连接请求。
- 监听到达的数据。
- 接收数据。
- 关闭连接。
1.1 服务器套接字
要创建一个服务器,需要创建一个服务器套接字(server socket),并把它附加到一个端口上,服务器从这个端口监听连接。端口标识套接字上的TCP服务。端口号的范围从0到65536,但是0到1024是为特权服务保留的端口号。可以选择任意一个当前没有被其他进程使用的端口创建服务器套接字,ServerSocket类具有4个构造器:
public ServerSocket(int port){}
public ServerSocket(int port, int queuelength){}
public ServerSocket(int port, int queuelength, InetAddress bindaddress){}
public ServerSocket(){}
其中port为ServerSocket对象的监听的端口,queueulength用来设置客户端连接请求队列的纯度,bindaddress指定ServerSocket对象绑定的本地网络地址。
如果在已经使用的端口上创建服务器套接字,就会抛出java.net.BindException异常。利用ServerSocket创建一个服务器的典型工作流程如下:
- 在指定的监听窗口创建一个ServerSocket对象。
- ServerSocket对象调用accept()方法在指定的端口监听到来的连接。
- 调用getInputStream()和getOutputStream()方法获得Socket对象的输入流和输出流。
- 服务器与客户端根据一定的协议交互数据,直到一段请求关闭连接。
- 服务器和客户端关闭连接。
- 服务器回到第二步,继续监听下一次连接。
1.2 客户端套接字
创建服务器套接字以后,服务器可以使用下面的accept()方法监听客户端的连接请求,具体如下:
Socket socket=serverSocket.accept();
这个语句将会一直等待,直到一个客户端连接到服务器套接字。客户端执行下面的语句来访问服务器并进行连接:
Socket socket=new Socket(severName,port);
其中serverName为服务器的主机名或IP地址,port为服务器端口号。这条语句打开一个套接字,使得客户端能够与服务器进行通信。程序可以使用主机名localhost来引用客户端正在运行的计算机。如果找不到主机的话,Socket构造方法就会抛出java.net.UnkonwnHostException异常。Socket对象具有下列常用方法:
- public InetAddress getInetAddress():返回Socket连接的目标主机的地址。
- public int getPort():返回Socket连接的目标主机的端口。
- public int getLocalPort():返回Socket绑定的本地端口。
- public InetAddress getLocalAddress():返回Socket绑定的本地地址,若未绑定则返回null。
- public InputStream getInputStream getInputStream():返回该Socket输入流。
- public OutputStream getOutputStream():返回Socket输出流。
- public synchronized void close():关闭当前Socket。
1.3 数据传输
服务器响应客户端的连接后,服务器和客户端之间的通信类似于输入输出流一样操作。为了获得输入流和输出流,对套接字对象使用getInputStream()方法和getOutputStream()方法。例如:
InputStream input=socket.getInputStream();
OutputStream output=socket.getOutputStream();
通过输入流我们可以从Socket中读入数据,通过输出流我们可以向Socket连接写入数据,这样Socket连接的两端客户端和服务器就可以传输数据,这与我们对磁盘文件进行的读写操作没有任何区别。
InputStream流和OutputStream流用来读取或写入字节。可以使用DataInputStream,DataOutputStream,BufferedReader,PrintWriter来包装输入输出流,以便读取int,double以及String之类的数据。例如在下面的语句中,创建一个DataInputStream流input,以及DataOutputStream流Output,用它们读取和写入基本数据类型:
DataInputStream input=new DataInputStream(socket.getInputStream());
DataOutputStream output=new DataOutputStream(socket.getOutputStream());
1.4 InetAddress类
java.net包中的InetAddress类是与IP地址相关的类,利用该类可以获取IP地址、主机地址等信息。该类中包含的方法如下表所示。需要注意的是,InetAddress类并没有提供构造函数,因此我们是通过类中的方法来返回InetAddress实例。具体使用方法如下代码所示。
方法 | 功能 |
---|---|
InetAddress getByName(String host) | 获取与Host相对应的InetAddress对象 |
String getHostAddress() | 虎丘InetAdress对象所含的IP地址 |
String getHostName() | 获取IP地址的主机名 |
InetAdress getLocalHost() | 返回本地主机的InetAdress对象 |
import java.net.*;
public class Adress{
public static void main(String[] args){
InetAddress ip;//创建InetAddress对象
try{
ip=InetAdress.getLocalHost();//实例化对象
String localname=ip.getHostName();//获取本机名
String localip=ip.getHostAdress();//获取本机IP地址
System.out.println(localname);
System.out.println(localip);
}catch(UnknownHostException e){
e.printStackTrace();
}
}
}
1.5 TCP网络程序
TCP协议是TCI/IP协议中的两个高级协议之一,即传输控制协议(Transmission Control Protocol)。TCP协议是一种以固接连线为基础的协议,它提供两天计算机之间可靠的数据传送。TCP可以保证从一段数据至连接的另一端时,数据能够确实送达,而且抵达的数据的排列顺序和送出的顺序相同,因此TCP协议适合可靠性要求比较高的场合。
在网络编程中,如果只要求客户机向服务器发送消息,并不要求服务器反馈消息,这种模式称为单向通信。客户机套接字和服务器套接字连接成功后,客户机通过输出流发送数据,服务器则通过输入流接受数据,下面的例子将展示TCP服务器程序实例。
import java.io.*;
import java.net.*;
public class netText extends Thread{//服务线程
private Socket connection;
private BufferedReader localread;
public netText(Socket connection){//通过构造函数获得服务器端所接收的客户机套接字
this.connection=connection;
}
public void run(){
getMessage();
}
private void getMessage(){//服务功能方法
try{
while(true){
localread=new BufferedReader(new InputStreamReader(connection.getInputStream()));
System.out.println("客户机"+localread.readLine());
}
}catch(Exception e){
e.printStackTrace();
}
}
public static void main(String[] args) {
Runnable threadServer=new ThreadServer();
(new Thread(threadServer)).start();
}
}
class ThreadServer implements Runnable{//创建客户端
private ServerSocket localserver;
private Socket socket;
void getServer(){
try{
localserver=new ServerSocket(8000);
System.out.println("服务器端口号:"+localserver.getLocalPort());
while(true){
System.out.println("等待客户端连接");
socket=localserver.accept();//获取客户端套接字
netText task=new netText(socket);//将客户端套接字通过构造函数赋予服务线程
(new Thread(task)).start();//创建新的线程为客户端进行服务
}
}catch(Exception e){
e.printStackTrace();
}
}
public void run(){
getServer();
}
}
上面这个例子中,结合了多线程并发的模式创建了一个服务器,服务器的功能是输出来自客户端输入的信息。一个服务器通常要为数目不确定的客户端提供服务,因此单一的点对点的服务机制无法满足这样的需求,所以我们要结合多线程并发运行的模式,每当服务器收到一个客户端的连接请求时,就创建并启动一个服务线程netText,并用localserver.accept()所获取的客户端的套接字Socket传入服务线程netText。服务器工作时,可能存在多个服务线程netText。