Socket网络编程
Socket套接字。在java.net.Socket包下。
1)网络通信模型:C/S:client/server,客户端/服务器端;B/S:browser/server,浏览器端/服务器端;C/S结构的优点:应用的针对性强,画面绚丽,应用功能复杂。缺点:不易维护。B/S结构的优点:易于维护。缺点:效果差,交互性不强。
2)Socket:封装着本地的地址,服务端口等信息。ServerSocket:服务端的套接字。
服务器:使用ServerSocket监听指定的端口,端口可以随意指定(由于1024以下的端口通常属于保留端口,在一些操作系统中不可以随意使用,所以建议使用大于1024的端口),等待客户连接请求,客户连接后,会话产生;在完成会话后,关闭连接。
客户端:使用Socket对网络上某一个服务器的某一个端口发出连接请求,一旦连接成功,打开会话;会话完成后,关闭Socket。客户端不需要指定打开的端口,通常临时的、动态的分配一个1024以上的端口。
3)永远都是Socket去主动连接ServerSocket。一个ServerSocket可以接收若干个Socket的连接。网络通信的前提:一定要捕获异常。
4)Socket连接基于TCP/IP协议,是一种长连接(长时间连着)。
5)读取服务器信息会阻塞,写操作不会。
6)建立连接并向服务器发送信息步骤:①通过服务器的地址及端口与服务器连接,而创建Socket时需要以上两个数据。②连接成功后可以通过Socket获取输入流和输出流,使用输入流接收服务端发送过来的信息。③关闭连接。
7)连接服务器:一旦Socket被实例化,那么它就开始通过给定的地址和端口号去尝试与服务器进行连接(自动的)。这里的地址"localhost"是服务器的地址,8088端口是服务器对外的端口。我们自身的端口是系统分配的,我们无需知道。
8)和服务器通信(读写数据):使用Socket中的getInputStream()获取输入流,使用getOutputStream()获取输出流。
9)ServerSocket构造方法要求我们传入打开的端口号,ServerSocket对象在创建的时候就向操作系统申请打开这个端口。
10)通过调用ServerSocket的accept方法,使服务器端开始等待接收客户端的连接。该方法是一个阻塞方法,监听指定的端口是否有客户端连接。直到有客户端与其连接并接收客户端套接字,否则该方法不会结束。
eg1.1:客户端ClientDemo类
private Socket socket;
public void send(){
try{ System.out.println("开始连接服务器"); socket=new Socket("localhost",8088);
InputStream in=socket.getInputStream();//获取输入流
OutputStream out=socket.getOutputStream();//获取输出流
/**将输出流变成处理字符的缓冲字符输出流*/
PrintWriter writer=new PrintWriter(out); writer.println("你好!服务器!");
/**注意,写到输出流的缓冲区里了,并没有真的发给服务器。想真的发送就要作真实的写操作,清空缓冲区*/
writer.flush();
/**将输入流转换为缓冲字符输入流*/
BufferedReader reader=new BufferedReader(new InputStreamReader(in));
/**读取服务器发送过来的信息*/
String info=reader.readLine();//读取服务器信息会阻塞 System.out.println(info);
writer.println("再见!服务器!"); writer.flush();
info=reader.readLine(); System.out.println(info);
}catch(Exception e){ e.printStackTrace(); } }
public static void main(String[] args){
ClientDemo demo=new ClientDemo(); demo.send();//连接服务器并通信 }
eg1.2:服务器端ServerDemo类(不使用线程)
private ServerSocket socket=null; private int port=8088;
/**构建ServerDemo对象时就打开服务端口*/
public ServerDemo(){
try{ socket=new ServerSocket(port); }catch(Exception e){ e.printStackTrace(); } }
/**开始服务,等待收受客户端的请求并与其通信*/
public void start(){
try{ System.out.println("等待客户端连接……"); Socket s=socket.accept();
//获取与客户端通信的输入输出流
InputStream in=s.getInputStream(); OutputStream out=s.getOutputStream();
//包装为缓冲字符流
PrintWriter writer=new PrintWriter(out);
BufferedReader reader=new BufferedReader(new InputStreamReader(in));
//先听客户端发送的信息
String info=reader.readLine();//这里同样会阻塞 System.out.println(info);
//发送信息给客户端
writer.println("你好!客户端"); writer.flush();
info=reader.readLine(); System.out.println(info);
writer.println("再见!客户端"); writer.flush();
socket.close();//关闭与客户端的连接
}catch(Exception e){ e.printStackTrace(); } }
public static void main(String[] args){ System.out.println("服务器启动中……");
ServerDemo demo=new ServerDemo(); demo.start(); }
eg2:服务器端ServerDemo类(使用线程),start()方法的修改以及Handler类
public void start(){
try{while(true){ System.out.println("等待客户端连接……"); Socket s=socket.accept();
/** 当一个客户端连接了,就启动一个线程去接待它 */
Thread clientThread=new Thread(new Handler(s)); clientThread.start(); }
}catch(Exception e){ e.printStackTrace(); } }
/** 定义线程体,该线程的作用是与连接到服务器端的客户端进行交互操作 */
class Handler implements Runnable{
private Socket socket;//当前线程要进行通信的客户端Socket
public Handler(Socket socket){//通过构造方法将客户端的Socket传入
this.socket=socket; }
public void run(){
try{ //获取与客户端通信的输入输出流
InputStream in=socket.getInputStream();OutputStream out=socket.getOutputStream();
PrintWriter writer=new PrintWriter(out);//包装为缓冲字符流
BufferedReader reader=new BufferedReader(new InputStreamReader(in));
String info=reader.readLine();//先听客户端发送的信息,这里同样会阻塞
System.out.println(info);
//发送信息给客户端
writer.println("你好!客户端"); writer.flush();
info=reader.readLine(); System.out.println(info);
writer.println("再见!客户端"); writer.flush();
socket.close();//关闭与客户端的连接
}catch(Exception e){ e.printStackTrace(); } } }
public static void main(String[] args){ System.out.println("服务器启动中……");
ServerDemo demo=new ServerDemo(); demo.start(); }