作者 孙卫琴
Java 网络程序都采用客户/服务器通信模式, 致力于实现应用层。 传输层向应用层提供了套接字 Socket 接口, Socket 封装了下层的数据传输细节, 应用层的程序通过 Socket 来建立与远程主机的连接, 以及进行数据传输。
站在应用层的角度,两个进程之间的一次通信过程从建立连接开始,接着交换数据,到断开连接结束。 套接字可看做是通信线路两端的接收器,进程通过套接字来收发数据。
如 进程A1-------Socket <------tcp连接----->Socket-------进程B1
在Java中, 有3种套接字类: java.net.Socket、java.net.ServerSocket 和 DatagramSocket。 其中 Socket 和 ServerSocket 类建立在 TCP协议基础上; DatagramSocket 类建立在UDP协议基础上。
- 创建 EchoClient
package com.sockettest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
public class EchoClient {
private String host = "localhost";//EchoServer进程所在的主机的名字,这里为"localhost"表示EchoClient和EchoServe
//进程运行在同一个主机上.
private int port = 8000; //EchoServer进程监听的端口
private Socket socket;
public EchoClient() throws IOException{
if(socket == null){
socket = new Socket(host,port);//如果Socket对象成功创建,就表示建立了EchoClient和EchoServer之间的连接.
System.out.println("连接服务器!");
}
}
private PrintWriter getWriter(Socket socket) throws IOException{
OutputStream socketOut = socket.getOutputStream();
return new PrintWriter(socketOut,true);
}
private BufferedReader getReader(Socket socket) throws IOException{
InputStream socketIn = socket.getInputStream();
return new BufferedReader(new InputStreamReader(socketIn));
}
/**
* EchoClient 类中最主要的方法. 该方法不断读取用户从控制台输入的字符串, 然后把它发送给 EchoServer, 再把EchoServer
* 返回的字符串打印到控制台. 如果用户输入的字符串为 "bye", 就会结束与 EchoServer的通信, 调用socket.close()方法断开
* 连接.
*/
public void talk() {
try{
//EchoClient和EchoServer建立连接后, EchoClient从Socket对象中得到输出流和输入流, 就能与EchoServer交换数据.
BufferedReader br = getReader(socket);
PrintWriter pw = getWriter(socket);
BufferedReader localReader = new BufferedReader(new InputStreamReader(System.in));
String msg = null;
while((msg = localReader.readLine())!= null){
pw.println(msg); //输出信息到服务器
System.out.println(br.readLine()); //打印从服务器接受的信息
if(msg.equals("bye")) //如果客户发送的信息为"bye",就结束通信
break;
}
}catch(IOException e){
e.printStackTrace();
}finally{
try{
if(socket != null) socket.close(); //断开连接
}catch(IOException e){e.printStackTrace();}
}
}
public static void main(String args[])throws IOException{
new EchoClient().talk();
}
}
• 创建 EchoServer
package com.sockettest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;public class EchoServer {
private int port = 8000;
private ServerSocket serverSocket;
public EchoServer() throws IOException{
if(serverSocket == null){
//服务器程序通过一直监听端口, 来接受客户程序的连接请求. 在服务器程序中, 需要先创建一个ServerSocket对象,
//在构造方法中指定监听的端口.
//ServerSocket的构造方法负责在操作系统中把当前 进程注册为服务器进程.
serverSocket = new ServerSocket(port); //监听8000端口
System.out.println("服务器启动!");
}
}
public String echo(String msg){
return "echo:" + msg;
}
//Socket类提供getOutputStream()方法,返回输出流OutputSteam对象,程序只需向输出流写数据, 就能向对方发送数据,
//Socket的输出流可以用过滤留来装饰, 这个方法中先获取输出流, 然后用PrintWriter装饰它, PrintWriter的
//println()方法能够写一行数据.
private PrintWriter getWriter(Socket socket) throws IOException{
OutputStream socketOut = socket.getOutputStream();
return new PrintWriter(socketOut,true);//参数true表示每写一行, PrintWriter缓存就自动溢出, 把数据写到目的地
}
//Socket类提供getInputStream()方法, 返回输入流InputStream对象, 程序只需从输入流读数据,就能接收来自对方的数据,
//Socket的输入流也可以用过滤流来装饰, 这个方法中先获取输入流, 然后用BufferedReader装饰它, BufferedReader的
//readLine()方法能够读入一行数据.
private BufferedReader getReader(Socket socket) throws IOException{
InputStream socketIn = socket.getInputStream();
return new BufferedReader(new InputStreamReader(socketIn));
}
/**
* EchoServer 类最主要的方法, 它不断等待客户的连接请求, 当serverSocket.accept()方法返回一个Socket对象时,
* 就意味着与一个客户建立了连接,接下来从Socket对象中得到输出流和输入流, 并且分别用PrintWriter 和BufferedReader
* 来装饰它们. 然后不断调用 BufferedReader 的readLine()方法读取客户发来的字符串XXX, 再调用PrintWriter 的
* println()方法向客户返回字符串 echo:XXX. 当客户发来的字符串为 "bye" 时, 就会结束与客户的通信,
* 调用socket.close() 方法断开连接.
* PS.这个方法无法同时与多个客户通信. EchoServer接收到一个客户连接, 就与客户进行通信, 通信完毕后断开连接,然后再
* 接收下一个客户连接. 假如同时有多个客户请求连接, 这些客户就必须排队等候EchoServer的响应.
*/
public void service(){
while(true){
Socket socket = null;
try{
//服务器程序调用ServerSocket对象的accept()方法, 该方法一直监听端口, 等待客户的连接请求, 如果接受到一个
//连接请求, accept()方法就会返回一个Socket对象, 这个Socket对象与客户端的Scoket对象形成了一条通信线路.
socket = serverSocket.accept(); //等待客户连接
//Socket对象包含了客户的地址和端口
System.out.println("New connection accepted "
+ socket.getInetAddress() + " : " + socket.getPort());
BufferedReader br = getReader(socket);
PrintWriter pw = getWriter(socket);
String msg = null;
while((msg = br.readLine()) != null){
System.out.println(msg); //打印接受的信息
pw.println(echo(msg)); //输出信息到客户端
if(msg.equals("bye")) //如果客户端发送的信息为"bye", 就结束通信
break;
}
}catch(IOException e){
e.printStackTrace();
}finally{
try{
if(socket != null) socket.close(); //断开连接
}catch(IOException e){
e.printStackTrace();}
}
}
}
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
new EchoServer().service();
}}
先运行 EchoServer, 再运行 EchoClient, 在EchoClient的控制台中输入字符串, 当输入的字符串为bye时, 就会结束与EchoServer的通信.