Socket通信简介
网络上的两个程序通过一个双向的通信连接实现数据的交换,这个双向链路的一端称为一个套接字(Socket)。
套接字通常用来实现客户和服务方的连接。
套接字是(TCP/IP)协议的一个十分流行的编程界面,一个套接字由一个IP地址和一个端口号唯一确定。
创建Socket和ServerSocket
Java在包Java.net中提供了两个类Socket和ServerSocket,分别用来表示双向连接的客户端和服务端。
◆ 客户端的Socket:
客户端的程序使用Socket类建立服务器的套接字连接。Socket常用的构造方法如下:
- Socket(InetAddress address,int port);
- Socket(InetAddress address,int port,boolean stream);
- Socket(string host,int port);
- Socket(string host,int port,boolean stream);
- Socket(SocketImpl impl);
其中address、host和port分别是双向连接中另一方的IP地址、主机号和端口号,stream指明Socket是流Socket还是数据报Socket,localPort表示本地主机的端口号,localAddr和bindAddr是本地机器的地址(ServerSocket的主机地址),impl是Socket的父类,既可以用来创建ServerSocket,又可以用来创建Socket。
当建立Socket时可能发生IOException异常,因此应像下面那样建立到服务器的套接字连接。例如:
try{
Socket client=new Socket("127.0.0.1",80);
ServerSocket server=new ServerSocket(80);
}catch(IOException e){
……
}
注意,在选择端口时,必须小心。每一个端口提供一种特定的服务,只有给出正确的端口,才能得到相应的服务。0~1023的端口为系统所保留,例如:
- http服务的端口号为80
- telnet服务的端口号为21
- ftp服务端口号为23
所以我们选择端口号时,最好选择一个大于1023的数以防止冲突。
◆ 服务器端的ServerSocket:
我们已经知道负责建立客户到服务器的套接字的连接,因此服务器必须建立一个等待接受客户的套接字ServerSocket对象。ServerSocket的构造方法是:
- ServerSocket(int port);
下面是一个典型的创建Server端的ServerSocket的过程。
ServerSocket server=null;
try{
//创建一个ServerSocket在端口1890监听客户请求
server=new ServerSocket(1890);
}catch(IOException e){
System.out.println("出错提示:"+e);
}
Socket socket=null;
try{
/* accept()(一个套接口接受一个连接)是一个阻塞的方法,一旦有客户请求,它就会返回一个Socket对象用于同客户进行交互*/
socket=server.accept();
} catch(IOException e1){
System.out.println("出错提示:"+e1);
}
以上的程序是Server的典型工作模式,只不过在这里Server只能接收一个请求,接受完成后Server就退出了。实际的应用中总是它不停地循环接收,一旦有客户请求,Server总是会创建一个服务线程来服务新来的客户,而自己继续临听。程序中accept()是一个阻塞方法,所谓阻塞方法就是说该方法被调用后,将等待客户的请求,直到有一个客户启动并请求连接到相同的端口,然后accept()返回一个对应于客户的Socket。这时,客户方和服务方都建立了用于通信的Socket,接下来就是由各个Socket分别打开各自的输入/输出流。
◆打开输入/输出流:
类Socket提供了方法getInputStream()和getOutStream()来得到对应的输入/输出流以进行读/写操作,这两个方法分别返回InputStream和OutputStream类对象。为了便于读/写数据,我们可以在返回的输入/输出流对象上建立过滤流,如DataInputStream、DataOutputStream(数据输入流允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型。应用程序可以使用数据输出流写入稍后由数据输入流读取的数据)或PrintStream类对象,对于文本方式流对象,可以采用InputStreamReader和OutputStreamWriter等处理。
下列模拟客户端-服务器端之间的对话:
客户端程序:客户向服务器发出信息:“你好,我是客户”,然后每隔一秒,客户向服务器发送一个随机数。服务器将回答:“你好,我是服务器”,并将客户发来的数据返回给客户。
注意:应首先将服务器的Server.java编译通过,并运行起来,等待客户的呼叫,然后运行客户端程序。
客户端程序代码:
//Client.java
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
public class Client {
public static void main(String args[]) {
String s = null;
Socket mysocket;
DataInputStream in = null;
DataOutputStream out = null;
try {
mysocket = new Socket("localhost", 2345);
in = new DataInputStream(mysocket.getInputStream());
out = new DataOutputStream(mysocket.getOutputStream());
out.writeUTF("你好,我是客户端");// 通过 out向"线路"写入信息。UTF,是Unicode Transformation Format的缩写,意为Unicode转换格式
while (true) {
s = in.readUTF();// 通过使用in读取服务器放入"线路"里的信息。堵塞状态,除非读取到信息。
out.writeUTF(":" + Math.random());
System.out.println("客户收到:" + s);
Thread.sleep(3000);
}
} catch (IOException e) {
System.out.println("无法连接");
} catch (InterruptedException e) { }
}
}
服务器端程序代码:
//Server.java
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String args[]) {
ServerSocket server = null;
Socket you = null;
String s = null;
DataOutputStream out = null;
DataInputStream in = null;
try {
server = new ServerSocket(2345);
} catch (IOException e1) {
System.out.println("出错提示:" + e1);
}
try {
you = server.accept();
in = new DataInputStream(you.getInputStream());
out = new DataOutputStream(you.getOutputStream());
while (true) {
s = in.readUTF();// 通过使用in读取客户放入"线路"里的信息。堵塞状态,除非读取到信息。
out.writeUTF("你好:我是服务器");// 通过 out向"线路"写入信息.
out.writeUTF("你说的数是:" + s);
System.out.println("服务器收到:" + s);
Thread.sleep(3000);
}
} catch (IOException e) {
System.out.println("" + e);
} catch (InterruptedException e) {
}
}
}