java网络socket编程(三)之ServerSocket服务器端

一、简介

java提供了一个ServerSocket类表示服务器Socket。服务器Socket在服务器上运行,监听入站ftp连接。每个服务器Socket监听服务器上的一个特定端口。当远程注解上的一个客户端尝试这个端口时,服务器就会被唤醒,协商建立客户端与服务器端的连接,并返回一个常规的Socket对象,表示2台主机之间的Socket。也是就说服务器端Socket接受到客户端Socket发送过来的连接时,服务器端会生成一个常规的Socket对象,用于向客户端发送数据,数据总是通过常规socket进行传输。

二、使用ServerSocket类

ServerSocket类包含了使用java编写服务器所需要的全部内容。其中包括创建新ServerSocket对象的构造函数,在指定端口监听客户端的连接的方法、配置各个服务器Socket选项的方法,以及一些其他常用的方法,如toString()。
在java中,Server服务器的基本生命周期包含以下几个:
1.使用一个ServerSocket()构造函数在一个特定的端口创建一个新的ServerSocket对象。
2.ServerSocket使用他的accept()方法来监听这个端口的入站连接。accept会一直阻塞,直到一个客户端尝试与服务器建立连接,此时accept将返回一个连接客户端和服务器Socket对象。
3.根据服务器的类型,会调用Socket对象的getInputStream或getOutputStream方法,或者这两个方法都调用,以获得客户端通信的输入和输出流。
4.服务器和客户端根据已经协商的协议交互,直到要关闭连接。
5.服务器或客户端关闭连接。
5服务器返回到第2不,等待下一次连接。

2.1  一个完整的接收/响应客户端的例子

<span style="font-family:Microsoft YaHei;">import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * Socket服务器端测试类
 * @author zzj
 * @date Jul 14, 2016 11:35:32 AM
 */
public class ServerSocketTest {

	public static void main(String[] args) {
		testCommon();
	}
	
	/**
	 * 1.测试普通的server
	 * @author zzj
	 */
	public static void testCommon(){
		ServerSocket serverSocket=null;
		try {
			serverSocket = new ServerSocket(22);
			while(true){
				System.out.println("wait receive message from client...");
				//接收客户端连接的socket对象
				Socket connection =null;
				try {
					//接收客户端传过来的数据,会阻塞
					connection=serverSocket.accept();
					
					System.out.println("****received message from client******");
					
					//读取客户端传过来的数据
					readMessageFromClient(connection.getInputStream());
					
					System.out.println("****received message from client end******");
					
					//向客户端写入数据
					writeMsgToClient(connection.getOutputStream(),"I am server message!!!");
					
					connection.close();
				} catch (Exception e) {
					e.printStackTrace();
				}finally{
					if (connection!=null) {
						connection .close();
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			if (serverSocket!=null) {
				try {
					serverSocket.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	/**
	 * 读取客户端信息
	 * @param inputStream
	 */
	private static void readMessageFromClient(InputStream inputStream) throws IOException {
		Reader reader = new InputStreamReader(inputStream);
		 BufferedReader br=new BufferedReader(reader);
		String a = null;
		while((a=br.readLine())!=null){
			System.out.println(a);
		}
	}

	/**
	 * 响应客户端信息
	 * @param outputStream
	 * @param string
	 */
	private static void writeMsgToClient(OutputStream outputStream, String string) throws IOException {
		Writer writer = new OutputStreamWriter(outputStream);
		writer.append("I am server message!!!");
		writer.flush();
		writer.close();
	}
}</span>
<span style="font-family:Microsoft YaHei;">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;

/**
 * Socket客户端
 * @author zzj
 * @date Jul 13, 2016 2:47:13 PM
 */
public class SocketTest {
	public static void main(String[] args) {
		writeToServer("localhost",22);
	}
	
	/**
	 * 向服务器写数据
	 * @param serverHost socket服务器地址
	 * @param port 端口
	 * @author zzj
	 */
	public static void writeToServer(String serverHost,int port){
		Socket socket = null;
		try {
			//1.建立客户端socket连接,指定服务器位置及端口  
            socket =new Socket("localhost",22);  
            
            //2.得到socket读写流  
            OutputStream os=socket.getOutputStream();  
            PrintWriter pw=new PrintWriter(os);  
            
            //输入流  
            InputStream is=socket.getInputStream();  
            BufferedReader br=new BufferedReader(new InputStreamReader(is));  
            
            //3.利用流按照一定的操作,对socket进行读写操作  
            String info="用户名:1,用户密码:1";  
            pw.write(info);  
            pw.flush();  
            socket.shutdownOutput();  
            //接收服务器的相应  
            String reply=null;  
            while(!((reply=br.readLine())==null)){  
                System.out.println("接收服务器的信息:"+reply);  
            }  
            //4.关闭资源  
            br.close();  
            is.close();  
            pw.close();  
            os.close();  
            socket.close();  
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			if (socket!=null) {
				try {
					socket.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
}
</span>

2.2 多线程接收客户端信息

由于服务器再等待客户端连接并处理时,会处理完一个才会处理下一个,导致资源浪费或者客户端等待时间过长,这里做一次简单的优化,每次收到客户端的请求连接时,就开启一个线程来处理响应,只需要将上面的server代码简单的改下并封装即可:
<span style="font-family:Microsoft YaHei;">**
 * Socket服务器端开启线程测试类
 * @author zzj
 * @date Jul 14, 2016 11:35:32 AM
 */
public class ServerSocketThreadTest  {

	public static void main(String[] args) {
		testCommon();
	}
	
	/**
	 * 1.测试普通的server
	 * @author zzj
	 */
	public static void testCommon(){
		ServerSocket serverSocket=null;
		try {
			serverSocket = new ServerSocket(22);
			while(true){
				System.out.println("wait receive message from client...");
				//接收客户端连接的socket对象
				Socket connection =null;
				//接收客户端传过来的数据,会阻塞
				connection=serverSocket.accept();
				new SubThread(connection).start();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			if (serverSocket!=null) {
				try {
					serverSocket.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

class SubThread extends Thread{
	private Socket connection;
	public SubThread(Socket conSocket){
		this.connection=conSocket;
	}
	
	public void run(){
		try {
			
			System.out.println("****received message from client******");
			
			//读取客户端传过来的数据
			readMessageFromClient(connection.getInputStream());
			
			System.out.println("****received message from client end******");
			System.out.println();
			
			//向客户端写入数据
			writeMsgToClient(connection.getOutputStream(),"I am server message!!!");
			
			connection.close();
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			if (connection!=null) {
				try {
					connection .close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
	
	/**
	 * 读取客户端信息
	 * @param inputStream
	 */
	private static void readMessageFromClient(InputStream inputStream) throws IOException {
		Reader reader = new InputStreamReader(inputStream);
		 BufferedReader br=new BufferedReader(reader);
		String a = null;
		while((a=br.readLine())!=null){
			System.out.println(a);
		}
	}

	/**
	 * 响应客户端信息
	 * @param outputStream
	 * @param string
	 */
	private static void writeMsgToClient(OutputStream outputStream, String string) throws IOException {
		Writer writer = new OutputStreamWriter(outputStream);
		writer.append("I am server message!!!");
		writer.flush();
		writer.close();
	}
}</span>


2.3 线程池的方式改进多并发请求服务器问题

如果每次来一个请求,服务器端开启一个线程的话,有可能在同时来成千上万个请求时导致内存暴增而导致程序的崩溃,为了减轻开启线程导致的内存过高问题,这里采用线程池来缓解这个问题,下面为主要代码:
package com.hq.socket;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import com.itextpdf.text.pdf.PdfStructTreeController.returnType;

/**
 * Socket服务器端开启线程测试类
 * @author zzj
 * @date Jul 14, 2016 11:35:32 AM
 */
public class ServerSocketPoolTest  {

	public static void main(String[] args) {
		testCommon();
	}
	
	/**
	 * 1.测试普通的server
	 * @author zzj
	 */
	public static void testCommon(){
		ServerSocket serverSocket=null;
		//定义一个容量为50的线程
		ExecutorService service = Executors.newFixedThreadPool(50);
		try {
			serverSocket = new ServerSocket(220);
			while(true){
				System.out.println("wait receive message from client...");
				//接收客户端连接的socket对象
				Socket connection =null;
				//接收客户端传过来的数据,会阻塞
				connection=serverSocket.accept();
				service.submit(new SubThread(connection));
			}
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			if (serverSocket!=null) {
				try {
					serverSocket.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

class SubPolThread implements Callable<Boolean>{
	private Socket connection;
	public SubPolThread(Socket conSocket){
		this.connection=conSocket;
	}
	
	/**
	 * 读取客户端信息
	 * @param inputStream
	 */
	private static void readMessageFromClient(InputStream inputStream) throws IOException {
		Reader reader = new InputStreamReader(inputStream);
		 BufferedReader br=new BufferedReader(reader);
		String a = null;
		while((a=br.readLine())!=null){
			System.out.println(a);
		}
	}

	/**
	 * 响应客户端信息
	 * @param outputStream
	 * @param string
	 */
	private static void writeMsgToClient(OutputStream outputStream, String string) throws IOException {
		Writer writer = new OutputStreamWriter(outputStream);
		writer.append("I am server message!!!");
		writer.flush();
		writer.close();
	}

	/* (non-Javadoc)
	 * @see java.util.concurrent.Callable#call()
	 */
	@Override
	public Boolean call() throws Exception {
		try {
			
			System.out.println("****received message from client******");
			
			//读取客户端传过来的数据
			readMessageFromClient(connection.getInputStream());
			
			System.out.println("****received message from client end******");
			System.out.println();
			
			//向客户端写入数据
			writeMsgToClient(connection.getOutputStream(),"I am server message!!!");
			
			connection.close();
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}finally{
			if (connection!=null) {
				try {
					connection .close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return true;
	}
}


  • 3
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值