socket实现阻塞式BIO通信

身在创业公司,有很多东东需要写,服务我们可以用tomcat,apache-http, apache-ftp,而有些服务不得不自己写。本文就是尝试使用java io写一个socket服务。

思路:

服务端:服务类(SocketServer),线程监听类(WorkThread),处理类(SocketHandler)

客户端:消息请求类和响应接收类

实现流程:服务端初始化配置,启动一定数量线程监听,监听到请求后,调用SocketHandler进行处理,返回应答给客户端。

分析:这是常见的IO阻塞式服务,效率自然不高,扩展性不强,自己用可以,想学习socket服务及开发思路的新手可以看下,欢迎拍砖

值得学习:SocketServer如何层层调用SocketHandelr实例的。

下面主要讲述服务端及相关代码实现:

1、BaseSocket Socket流数据的读写操作

package com.zhs.common;
/*
 * Socket流数据的读写操作
 */
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

import org.apache.log4j.Logger;
import com.zhs.common.util.ByteBuffer;

public class BaseSocket {
	Socket socket;
    private final static Logger log = Logger.getLogger(BaseSocket.class);
    public BaseSocket(Socket  s)
    {
            socket =s;
    }

    final static int BUF_SIZE=60000;
    byte[] inbuf=new byte[BUF_SIZE];
    int in_len=0,in_used=0;

    byte[] outbuf=new byte[BUF_SIZE];
    int out_used=0;

    public byte getbyte() throws  Exception
    {  
    	if(in_len-in_used<1)
    	{
             int num=  readToBuf();
             if(num<=0) throw new Exception("cannot read enough data from socket!");

    	}
    	in_used++;
        log.debug("getbyte "+inbuf[in_used-1]);
    	return inbuf[in_used-1];
    }

    private int readToBuf() throws IOException
    {
    	InputStream is=socket.getInputStream();
    	if(in_len-in_used>0)
    	{
    		copy(inbuf,in_used,in_len-in_used,inbuf,0);
    		in_len=in_len-in_used;
    		in_used=0;
    	}else if(in_len-in_used==0)
    	{
    		in_len=0;
    		in_used=0;
    	}

    	int num=is.read(inbuf,in_len,BUF_SIZE-in_len);
    	if(num>0)in_len+=num;
    	return num;
    }
    public long getlong()throws IOException, Exception
    {
    	long i0=getint();
    	long i1=getint();
    	return (i0&0xffffffffl) +(i1<<32);
    }
    
    public int getint() throws IOException, Exception
    {  int left=in_len-in_used;
       if(left<4)
       {
    	   int num=  readToBuf();
           if(num<=0) throw new Exception("cannot read enough data from socket!");
           return getint();
       }
       int v=((int)inbuf[in_used++]&0xff);
       v+=((int)inbuf[in_used++]&0xff)<<8;
       v+=((int)inbuf[in_used++]&0xff)<<16;
       v+=((int)inbuf[in_used++]&0xff)<<24;
       log.debug("getint "+v);
       return v;
    }
    
    public String getString(int len) throws IOException,Exception
    {
       if(len==0)return null;
            ByteBuffer bb=new ByteBuffer(len);

       int left=in_len-in_used;
       if(left>=len)
       {  bb.append(inbuf,in_used,len);
          in_used+=len;
       }
       else
       { int num=in_len-in_used;
             bb.append(inbuf,in_used,num);
         in_used=in_len;
         while(readToBuf()>0)
         {
           if(len-num>in_len-in_used)
           {
             num+=in_len-in_used;
             bb.append(inbuf,in_used,in_len);
             in_used=in_len;
           }
           else
           { bb.append(inbuf,in_used,len-num);
             in_used=len-num;
             num=len;
             break;
           }

         }

         if(num!=len)throw new Exception("there are no enough data to read ");
       }
       String s= bb.toString("utf-8");
      // log.debug("getString:"+s);
       return s;
    }
    public int readByteArray(byte [] bb,int begin,int len) throws IOException,Exception
    {
      

       int left=in_len-in_used;
       
       if(left==0)
       {
    	   if(readToBuf()<=0)
    		   throw new Exception("there are no enough data to read");
       }
       left=in_len-in_used;
       
       if(left>=len)
       {  copy(inbuf, in_used, len , bb, begin);
    	  
          in_used+=len;
          return len;
       }
      
       int num=in_len-in_used;
       copy(inbuf, in_used, num , bb, begin);
       in_used=in_len;
       return num;
          
        
       
    }
    public int readByteArray(byte [] bb,int len) throws IOException,Exception
    {
      

       int left=in_len-in_used;
       
       if(left==0)
       {
    	   if(readToBuf()<=0)
    		   throw new Exception("there are no enough data to read");
       }
       left=in_len-in_used;
       
       if(left>=len)
       {  copy(inbuf, in_used, len , bb, 0);
    	  
          in_used+=len;
          return len;
       }
      
       int num=in_len-in_used;
          copy(inbuf, in_used, num , bb, 0);
          in_used=in_len;
          return num;
          
        
       
    }
    private void copy(byte[] from, int begin, int len, byte[] to, int start)
    {
            for(int i=0;i<len;i++)
                    to[start+i]=from[begin+i];

    }

    public void close() throws IOException
    {
            socket.close();
    }

    public void save(byte b) throws IOException
    {
         //   log.debug("save byte:"+b);
            if(out_used+1<BUF_SIZE)
             outbuf[out_used++]=b;
            else
            { send();
              save(b);
            }
    }

    public  void send() throws IOException
    {
      OutputStream out=socket.getOutputStream();
      out.write(outbuf,0,out_used);
      out_used=0;
    }
    public void save(int i) throws IOException
    {
            log.debug("save int:"+i);
            if(out_used+4<BUF_SIZE)
            {
                     outbuf[out_used++]=(byte)(i&0xff);
                     outbuf[out_used++]=(byte)(i>>8&0xff);
                     outbuf[out_used++]=(byte)(i>>16&0xff);
                     outbuf[out_used++]=(byte)(i>>24&0xff);
            }
                    else
                    { send();
                      save(i);
                    }
    }
    public void save(long l) throws IOException
    {
            log.debug("save long:"+l);
            int i=(int) (l &0xffffffffl);
            save(i);
            i=(int) (l>>32);
            save(i);
    }
    public void save(byte b[],int begin ,int len ) throws IOException
    {
           // log.debug("save byte[] :len="+len);

            if(len<BUF_SIZE-out_used)
            {
                    copy(b,begin,len,outbuf,out_used);
                out_used+=len;
            }else
            {
                    int left=BUF_SIZE-out_used;
                    copy(b,begin,left,outbuf,out_used);
                    out_used+=left;
                    send();
                    save(b,begin+left,len-left);
            }

    }
    public void send(byte b) throws IOException
    {
        //log.debug("send byte:"+b);
    	save(b);send();
    }
    public void send(int b) throws IOException
    {
        // log.debug("send int:"+b);
    	save(b);send();
    }

    public void write(String text) throws IOException
    {
    	if(text==null) 
    		save(0);
        else
        {
            byte[]bb=text.getBytes("utf-8");
            save(bb.length);
            save(bb,0,bb.length);
        }
    }
    
    public void write(int i) throws IOException
    {
    	save(i);
    }
    
    public void write(long l) throws IOException
    {
    	save(l);
    }
    
    public void write(boolean b) throws IOException
    {   if(b)
    	 save((byte)1);
        else
    	  save((byte)0);
    }
    
    public boolean readboolean() throws Exception
    {
    	byte b=getbyte();
    	return b==1;
    }
    
    public int readint()throws Exception
    {
    	return getint();
    }
    
    public long readlong() throws IOException, Exception
    {
    	return getlong();
    }
    
    public String readString() throws IOException, Exception
    {
    	int l=getint();
    	if(l==0)return null;
    	return getString(l);
    	
    }
    public static void main(String [] argv)
    {
        long l=System.currentTimeMillis();
        int i=(int)l;
        System.out.println(l);
        System.out.println(l&0xffffffffl);
        System.out.println(i);
    }

}

3  常量类

package com.zhs.common;

public class ControlerVariable
{
       //连接成功
	public static final int SUCCESS = 0;

       //服务忙
	public static final int ERR_BUSY = 1;

	
	public static final int CONNECT_STATUS = 70;
	public static final int ASKPERSON = 71;
	public static final int ERROR_ASKPERSON = 72;
	public static final int LOOPACK = 100;
	
	//尝试连接
	public static final int ACK = 211;
	
	public static final int ERROR_REQUEST_ITEM=219;
	public static final int EQUALS_REQUEST_ITEM=220;
	public static final int LOWER_REQUEST_ITEM=221;
	public static final int LARGER_REQUEST_ITEM=222;
	
	public static final String SOCK_IP="system.net.sock.ip";
	public static final String SOCK_QUERY_PORT="system.net.sock.query.port";
	public static final String SOCK_TIMEOUT_S="socket.timeout.second";
	public static final String SYS_CHARSET="system.charset";
}

3、SocketServer  server端的服务类,调用了SocketHandler和WorkThread类作为其成员,进行请求的处理

package com.zhs.common.server;

import java.net.ServerSocket;
import java.util.ArrayList;
import java.util.List;

import com.zhs.common.BaseSocket;
import com.zhs.common.client.ControlerVariable;

public class SocketServer {

	
	ServerSocket server;
	int workthreadnum;
	int status;
	public static final int RUN=0;
	public static final int QUIT=1;
	List handlers=new ArrayList(8);
	WorkThread [] workers;
	
	public SocketServer(int port,int wn) throws Exception
	{  
	   workthreadnum=wn;
	   server=new ServerSocket(port);
	   status= RUN; 
	 
	}
	
	public void registerHandler(SocketHandler sl)
	{ 
		handlers.add(sl);
	}  
	
	public void startup()throws Exception
	{
		for(int i=0;i<handlers.size();i++)
		{  
			SocketHandler sl=(SocketHandler)handlers.get(i);
			sl.onServerStart();	//初始化动作		   
		}	 
		
	    System.out.println("socket server startup with "+workthreadnum+" working threads");
	    //初始化工作线程,进行监听
	    workers=new WorkThread[workthreadnum];
		for(int i=0;i<workthreadnum;i++)
		{
			workers[i]=new WorkThread(this,i);
			
			for(int j=0;j<handlers.size();j++)
			{  
				SocketHandler sh=(SocketHandler)handlers.get(j);
				sh.onThreadStart(workers[i]);//可根据需要进行扩展,此处没有相应实现   
			}
			workers[i].start();
		}	
	}
	
	public synchronized BaseSocket getRequest()throws Exception
	{
		return new BaseSocket(server.accept());
	}
	
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
	}

	public void handle(WorkThread wt, int command, BaseSocket client) throws Exception
	{
		while(true)
		{	
			boolean responsed=false;
			for(int i=0;i<handlers.size();i++)
			{  
				SocketHandler sl=(SocketHandler)handlers.get(i);
				if(sl.handle(wt,command,client)) 
				{	
					responsed=true;  
					break;
				}
			}	 
		 
	        if(!responsed)		
	        {
	        	client.send(14);
	        } 	 
			int ack=client.readint();
			if(ack!=ControlerVariable.LOOPACK)
				break;
			command=client.getint();
		}
		
	}

	public void setquit() 
	{
		status=QUIT;
	}
}

4、服务端处理类

package com.zhs.common.server;

import com.zhs.common.BaseSocket;

public class SocketHandler {

	public boolean handle(WorkThread wt,int command ,BaseSocket sscocket)throws Exception
	{
		return false;
	}
	
	public void onServerStart()throws Exception
	{
		
	}
	public void onServerClose()
	{
		 
	}
	public void onThreadStart(WorkThread wt)throws Exception
	{
		
	}
	public void onThreadClose(WorkThread wt)
	{
		 
	}

}

5、工作线程WorkThread,在ss.getRequest()处阻塞,监听socket请求,交由handler进行处理
package com.zhs.common.server;

import com.zhs.common.BaseSocket;

public class WorkThread extends Thread{

	SocketServer ss;
	public int seq;
	
	public WorkThread(SocketServer socketServer, int i) {
		this.ss = socketServer;
		this.seq = i;
	}
	
	@Override
	public void run(){
		while(ss.status != SocketServer.QUIT) {
			BaseSocket client = null;
			try {
				client = ss.getRequest();
				for(int i= 0; i < 32; i++) {
					 client.getbyte();
				}
				int command=client.getint();
				ss.handle(this,command,client);
				client.close();
				if(ss.status==SocketServer.QUIT) 
					break;
			} catch (Exception e) {
				e.printStackTrace();
				if(client!=null)
				try{
					client.close();
				}catch(Exception ex) {}
			}
		}
	}
}
上面的都是服务的core类型的类,下面实现,main起来

6、配置文件放在了properties类型的文件中

自己使用前注意修改system.net.sock.ip

#
system.net.sock.query.port = 7888
system.net.sock.ip = 192.168.32.242
#server
get.thread.number=2
add.thread.number=1

system.charset = GBK
socket.timeout.second=3000


 
7、自己扩展实现SocketHandler 的子类MyHandler 

import org.apache.log4j.Logger;

import com.zhs.common.BaseSocket;
import com.zhs.common.ControlerVariable;
import com.zhs.common.server.SocketHandler;
import com.zhs.common.server.SocketServer;
import com.zhs.common.server.WorkThread;
import com.zhs.common.util.ByteBuffer;


public class MyHandler extends SocketHandler {
	
	private SocketServer ss;
	final static Logger log = Logger.getLogger(MyHandler.class);
	public MyHandler(SocketServer ss) {
		this.ss = ss;
	}
	
	@Override
	public void onServerStart() throws Exception {
		// TODO Auto-generated method stub
		super.onServerStart();
		
		
	}
	
	@Override
	public boolean handle(WorkThread wt,int command, BaseSocket client) throws Exception{
		switch(command){
			case 0://??
				 client.readString();
				 ss.setquit();
				 client.save(0);
				 client.save(0);
				 client.send();
				 
				 return true;
			case ControlerVariable.CONNECT_STATUS://
				log.info("receive connect request");
				client.readString();
				client.save(0); 
				client.save(0);
				client.send();
				
				return true;
			case ControlerVariable.ASKPERSON://
				log.info("receive askperson:"+ControlerVariable.ASKPERSON);
				
				
				//query from db or index server
				//response
				if(true){
					ByteBuffer buffer = new ByteBuffer();
					buffer.append("ssssssssssssssss");
					client.save(0);//request flag 
					client.save(buffer.getUsed());//返回数据长度,不包括自身
					client.save(buffer.array(),0,buffer.getUsed()); //如果长度大于0,就save字节数据
					client.send();
					return true;
				}
				client.save(ControlerVariable.ERROR_ASKPERSON); 
				client.save(0);
				client.send();
				return true;
				
			
			case ControlerVariable.ACK://
		}
		return false;
	}
}


package com.zhs.common.myserver;


import org.apache.log4j.PropertyConfigurator;

import com.zhs.common.ControlerVariable;
import com.zhs.common.conf.ConfFactory;
import com.zhs.common.conf.Configuration;
import com.zhs.common.server.SocketServer;

public class ServerExampleMain {
	Configuration conf;

	public void init(String conf_file) throws Exception{
		conf = ConfFactory.getConf(conf_file);
	}
	
	public void startup() throws Exception{
		
		
		int qport=conf.getInt(conf.get(ControlerVariable.SOCK_QUERY_PORT),7888);
		int qtn=conf.getInt(conf.get("get.thread.number"),2);
		SocketServer qrys=new SocketServer(qport,qtn);
		qrys.registerHandler(new MyHandler(qrys));
		qrys.startup();
	}
	
	/**
	 * @param args
	 */
	public static void main(String[] args) throws Exception {
		// TODO Auto-generated method stub
		ServerExampleMain security = new ServerExampleMain();
		security.init("socket.properties");
		PropertyConfigurator.configure("conf/log4j.properties");
		security.startup();
	}

}
 

运行ServerExampleMain和ClientExampleMain可以看到serverExample的打印日志,MyHandler接受到了askperson的请求,当然这只是一个例子,ClientExampleMain没有打印返回值。

代码见:http://download.csdn.net/detail/lzlchangqi/8175207

下载后导入Eclipse,add log4j.***.jar 到path后 ,运行即可,更详细可以打断点调试


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值