java工具类(获取访问者的IP、Calendar、判断socket是否已经断开及长连接、UDP丢包及无序问题、clob字段、Url请求方式中文乱码)

1、获取访问者的IP

public String getIp(HttpServletRequest request) {
		String ip= request.getHeader("X-Forwarded-For");
	    if (ip== null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
	        ip= request.getHeader("Proxy-Client-IP");
	    if (ip== null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
	        ip= request.getHeader("WL-Proxy-Client-IP");
	    if (ip== null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
	        ip= request.getHeader("HTTP_CLIENT_IP");
	    if (ip== null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
	        ip= request.getHeader("HTTP_X_FORWARDED_FOR");
	    if (ip== null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
	        ip= request.getRemoteAddr();
	    if ("127.0.0.1".equals(ip) || "0:0:0:0:0:0:0:1".equals(ip)){
	        try {
	            ip= InetAddress.getLocalHost().getHostAddress();
	        }
	        catch (UnknownHostException e) {
	        	e.printStackTrace();
	        }
	    }
	return ip
}

 

/**
     * 获取访问者IP
     * 在一般情况下使用Request.getRemoteAddr()即可,但是经过nginx等反向代理软件后,这个方法会失效。
     * 本方法先从Header中获取X-Real-IP,如果不存在再从X-Forwarded-For获得第一个IP(用,分割),
     * 如果还不存在则调用Request .getRemoteAddr()。
     * 
     * @param request
     * @return
     */
    public static String getIpAddr(HttpServletRequest request) {
        String ip = request.getHeader("X-Real-IP");
        if (!StringUtils.isBlank(ip) && !"unknown".equalsIgnoreCase(ip)) {
            return ip;
        }
        ip = request.getHeader("X-Forwarded-For");
        if (!StringUtils.isBlank(ip) && !"unknown".equalsIgnoreCase(ip)) {
            // 多次反向代理后会有多个IP值,第一个为真实IP。
            int index = ip.indexOf(',');
            if (index != -1) {
                return ip.substring(0, index);
            } else {
                return ip;
            }
        } else {
            return request.getRemoteAddr();
        }
    }

 2、Calendar

public class TestCalendar {

	@Test
	public void test(){
		try {
			Calendar calendar = new GregorianCalendar();//子类实例化
			//获取年、月、日、时、分、秒、毫秒
			System.out.println("年: "+calendar.get(Calendar.YEAR));
			System.out.println("月 "+(calendar.get(Calendar.MONTH)+1));
			System.out.println("日: "+calendar.get(Calendar.DAY_OF_MONTH));
			System.out.println("时: "+calendar.get(Calendar.HOUR_OF_DAY));
			System.out.println("分: "+calendar.get(Calendar.MINUTE));
			System.out.println("秒: "+calendar.get(Calendar.SECOND));
			System.out.println("毫秒 "+calendar.get(Calendar.MILLISECOND));
			
			// 当前月第一天、最后一天
			int currYear = calendar.get(Calendar.YEAR);
			int currMonth = calendar.get(Calendar.MONTH) + 1;
			System.out.print(currYear + "-" + currMonth);
			SimpleDateFormat datef = new SimpleDateFormat("yyyy-MM-dd");
			calendar.set(Calendar.DAY_OF_MONTH, 1);
			Date beginTime = calendar.getTime();
			String sTime = datef.format(beginTime) + " 00:00:00";
			calendar.set(Calendar.DATE, 1);
			calendar.roll(Calendar.DATE, -1);
			Date endTime = calendar.getTime();
			String eTime = datef.format(endTime) + " 23:59:59";
			System.out.println("第一天"+sTime+"最后一天"+eTime);
			
			Calendar cal = Calendar.getInstance(); 
			//指定年月的的第一天、最后一天
			int year = 2015;
			int moth = 3;
			cal.set(Calendar.YEAR,year);
			cal.set(Calendar.MONTH, moth-1);
			//当前月的最后一天   
			cal.set( Calendar.DATE, 1 );
			cal.roll(Calendar.DATE, - 1 );
			Date endTime2=cal.getTime();
			String endTimeStr=datef.format(endTime2)+" 23:59:59";
			//当前月的第一天          
			cal.set(GregorianCalendar.DAY_OF_MONTH, 1); 
			Date beginTime2=cal.getTime();
			String beginTimeStr=datef.format(beginTime2)+" 00:00:00";
			
			System.out.println("指定年月的的第一天"+endTimeStr+"最后一天"+beginTimeStr);
			
			//当前时间的前一天
			SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd:HH");  
			String tmStr = "2015-5-18:00";
			Date d = sdf2.parse(tmStr);  
			System.out.println(d);
			Calendar now = Calendar.getInstance();  
			now.setTime(d);  
			now.set(Calendar.HOUR, now.get(Calendar.HOUR) -1);  
			System.out.println(sdf2.format(now.getTime()));
			
			//最近N天
			SimpleDateFormat df2 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
			cal.add(Calendar.DATE,-7);
			System.out.println("最近7天"+df2.format(cal.getTime()));
			Calendar ca2 =Calendar.getInstance();
			//最近N个月
			ca2.add(Calendar.MONTH,-1);//最近一个月
			System.out.println("最近一个月"+df2.format(ca2.getTime()));
			Calendar ca3 =Calendar.getInstance();
			//最近N个年
			ca3.add(Calendar.YEAR,-1);//最近一年
			System.out.println("最近一年"+df2.format(ca3.getTime()));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

 3、判断socket是否已经断开

  首先 socket类的方法isClosed()、isConnected()、isInputStreamShutdown()、isOutputStreamShutdown()等,但经过试验并查看相关文档,这些方法都是本地端的状态,无法判断远端是否已经断开连接。然后想到是否可以通过OutputStream发送一段测试数据,如果发送失败就表 示远端已经断开连接,类似ping,但是这样会影响到正常的输出数据,远端无法把正常数据和测试数据分开。 最后又回到socket类,发现有一个方法sendUrgentData,查看文档后得知它会往输出流发送一个字节的数据,只要对方Socket的SO_OOBINLINE属性没有打开,就会自动舍弃这个字节,而SO_OOBINLINE属性默认情况下就是关闭的,所以正是我需要的。 

于是,下面一段代码就可以判断远端是否断开了连接: 

try{ 
      socket.sendUrgentData(0xFF); 
}catch(Exception ex){ 
      reconnect(); 
} 

用ping实现 

package com.csdn.test; 
  import java.io.BufferedReader; 
  import java.io.IOException; 
  import java.io.InputStreamReader; 
  public class test { 
  static BufferedReader bufferedReader; 
  public static void main(String[] args) throws IOException { 
  try { 
  Process process = Runtime.getRuntime().exec("ping 192.168.1.104");//判断是否连接的IP; 
  bufferedReader = new BufferedReader(new InputStreamReader(process 
  .getInputStream())); 
  String connectionStr = ""; 
  while ((connectionStr = bufferedReader.readLine()) != null) { 
  System.out.println(connectionStr); 
  } 
  } catch (IOException e) { 
  e.printStackTrace(); 
  } finally { 
  bufferedReader.close(); 
  } 
  } 
  } 

 
ping的方法有个严重的BUG,就是你只能判断对方是否连接网络,而不能判断客户端是否开启,
在写聊天室时经常要注意客户端异常退出的问题(比如客户直接调用任务管理器结束程序进程), 
其实在通过socket.getoutstream和socket.getinputstream流对客户端发送、接受信息时如果socket没连接上是会抛出异常的,这也就是为什么Java会要求网络编程都要写在try里面,所以只要在catch里面写入客户端退出的处理就行了,没必要专门去想什么方法

socket长连接:

package com.yihongyu.exec.modules.crx.service.impl;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.InterruptedIOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.util.Iterator;
import java.util.Properties;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import javax.annotation.PostConstruct;

import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.yihongyu.exec.modules.crx.manager.CrxNotificationManager;
import com.yihongyu.exec.modules.crx.service.CrxNotificationService;
import com.yihongyu.exec.modules.crx.service.CrxSocketClientService;

@Service
public class CrxSocketClientServiceImpl implements CrxSocketClientService {

	private ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(10);

	@Autowired
	private CrxNotificationService crxNotificationService;

	private Socket socket;
	private Properties props = new Properties();
	private String host;
	private String portStr;

	/** 初始化Socket连接信息 */
	@Override
	@PostConstruct
	public void init() {
		try {
			props.load(new FileInputStream("conf/notifications/crx.properties"));
			host = props.getProperty("crx.host", "192.168.1.225");
			portStr = props.getProperty("crx.port", "8222");
			Integer port = Integer.parseInt(portStr);
			connect(host, port);
		} catch (FileNotFoundException e) {
			LoggerFactory.getLogger(getClass()).error("无法找到Crx服务器配置文件,请检查是否存在文件:conf/notifications/crx.properties!");
		} catch (IOException e) {
			LoggerFactory.getLogger(getClass()).error("装载Crx服务器配置文件失败:conf/notifications/crx.properties!", e);
		}
	}

	/** 创建Socket连接 */
	@Override
	public Boolean connect(String host, int port) {
		try {
			destroy();
			this.socket = new Socket(host, port);
			if (!isOrNotServerClose()) {
				System.out.println("----------------Connection Crx socket success!----------------");
			} else {
				System.out.println("----------------Connection Crx socket fail!----------------");
				LoggerFactory.getLogger(getClass()).error("Connection Crx socket fail!");
			}
			socket.setSoTimeout(60000);
			socket.setKeepAlive(true);
			isServerClose();
			return socket.isConnected();
		} catch (Exception e) {
			System.out.println(e.getMessage());
			e.printStackTrace();
			return false;
		}
	}

	/** 检测服务端Socket是否断开 */
	@Override
	public void isServerClose() {
		executor.schedule(new Runnable() {
			@Override
			public void run() {
				try {
					System.out.println("=================sendUrgentData=================");
					socket.sendUrgentData(0XFF);
					isServerClose();
				} catch (IOException e) {// 出现异常,服务端主动断开
					destroy();
					isServerClose();
					LoggerFactory.getLogger(getClass()).error("Crx Server Exception Close!");
					LoggerFactory.getLogger(getClass()).error("重新向服务端发起连接");
					reConnect();// 重新向服务端发起连接
					e.printStackTrace();
				}
			}
		}, 10000, TimeUnit.MILLISECONDS);

	}

	/** 重新创建连接 */
	@Override
	public boolean reConnect() {
		try {
			Integer port = Integer.parseInt(portStr);
			this.socket = new Socket(host, port);
			if (!isOrNotServerClose()) {
				System.out.println("----------------Connection Crx socket success!----------------");
			} else {
				System.out.println("----------------Connection Crx socket fail!----------------");
				LoggerFactory.getLogger(getClass()).error("Connection Crx socket fail!");
			}
			socket.setSoTimeout(60000);
			socket.setKeepAlive(true);
			return true;
		} catch (Exception e) {
			System.out.println(e.getMessage());
			e.printStackTrace();
			return false;
		}
	}

	@Override
	public Boolean isOrNotServerClose() {
		try {
			socket.sendUrgentData(0XFF);
			return false;
		} catch (IOException e) {// 出现异常,服务端主动断开
			LoggerFactory.getLogger(getClass()).error("Crx Server Exception Close!");
			e.printStackTrace();
			return true;
		}
	}

	/** 向Crx服务端发送信息 */
	@Override
	public String process(String contentXml) {
		try {
			System.out.println("Send CrxNotification Xml: " + contentXml);
			if (contentXml != null && contentXml.length() > 0) {
				OutputStream out = this.socket.getOutputStream();
				out.write(contentXml.getBytes("GBK"));
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	/** 获取Crx服务端返回的信息 */
	@Override
	public String getCrxMsg() throws UnsupportedEncodingException, IOException {
		// 服务端返回的消息状态: 0--发送成功, 1--发送失改, 2--超时
		String crxServerMsg = null;
		InputStream in = socket.getInputStream();
		if (in.available() > 0) {
			byte[] buff = new byte[in.available()];
			in.read(buff);
			crxServerMsg = new String(buff);
		}
		return crxServerMsg;
	}

	/** 关闭socket */
	@Override
	public void destroy() {
		try {
			if (this.socket != null) {
				this.socket.close();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

 

4、UDP丢包及无序问题

在项目中做了个验证程序.

发现客户端连续发来1000个1024字节的包,服务器端出现了丢包现象.

纠其原因,是服务端在还未完全处理掉数据,客户端已经数据发送完毕且关闭了.

有没有成熟的解决方案来解决这个问题.

我用过sleep(1),暂时解决这个问题,但是这不是根本解决办法,如果数据量大而多,网络情况不太好的话,还是有可能丢失.

你试着用阻塞模式吧...

select...我开始的时候好像也遇到过..不过改为阻塞模式后就没这个问题了...

采用回包机制,每个发包必须收到回包后再发下一个

UDP丢包是正常现象,因为它是不安全的。

丢包的原因我想并不是“服务端在还未完全处理掉数据,客户端已经数据发送完毕且关闭了”,而是服务器端的socket接收缓存满了(udp没有流量控制,因此发送速度比接收速度快,很容易出现这种情况),然后系统就会将后来收到的包丢弃。你可以尝试用setsockopt()将接收缓存(SO_RCVBUF)加大看看能不能解决问题。

服务端采用多线程pthread接包处理

UDP是无连接的,面向消息的数据传输协议,与TCP相比,有两个致命的缺点,一是数据包容易丢失,二是数据包无序。

要实现文件的可靠传输,就必须在上层对数据丢包和乱序作特殊处理,必须要有要有丢包重发机制和超时机制。

常见的可靠传输算法有模拟TCP协议,重发请求(ARQ)协议,它又可分为连续ARQ协议、选择重发ARQ协议、滑动窗口协议等等。

如果只是小规模程序,也可以自己实现丢包处理,原理基本上就是给文件分块,每个数据包的头部添加一个唯一标识序号的ID值,当接收的包头部ID不是期望中的ID号,则判定丢包,将丢包ID发回服务端,服务器端接到丢包响应则重发丢失的数据包。

模拟TCP协议也相对简单,3次握手的思想对丢包处理很有帮助。

udp是不安全的,如果不加任何控制,不仅会丢失包,还可能收到包的顺序和发送包的顺序不一样。这个必须在自己程序中加以控制才行。

收到包后,要返回一个应答,如果发送端在一定时间内没有收到应答,则要重发。

UDP本来存在丢包现象,现在的解决方案暂时考虑双方增加握手.

这样做起来,就是UDP协议里面加上了TCP的实现方法.

程序中采用的是pthread处理,丢包率时大时小,不稳定可靠

我感觉原因可能有两个,一个是客户端发送过快,网络状况不好或者超过服务器接收速度,就会丢包。

第二个原因是服务器收到包后,还要进行一些处理,而这段时间客户端发送的包没有去收,造成丢包。

解决方法:

方法一:客户端降低发送速度,可以等待回包,或者加一些延迟。

方法二:服务器部分单独开一个线程,去接收UDP数据,存放在一个缓冲区中,又另外的线程去处理收到的数据,尽量减少因为处理数据延时造成的丢包。

有两种方法解决楼主的问题:

方法一:重新设计一下协议,增加接收确认超时重发。(推荐)

方法二:在接收方,将通信和处理分开,增加个应用缓冲区;如果有需要增加接收socket的系统缓冲区。(本方法不能从根本解决问题,只能改善)

 

网络丢包,是再正常不过的了。

既然用UDP,就要接受丢包的现实,否则请用TCP。

如果必须使用UDP,而且丢包又是不能接受的,只好自己实现确认和重传,说白了,就是自己实现TCP(当然是部分和有限的简单实现)。

UDP是而向无连接的,用户在实施UDP编程时,必须制定上层的协议,包括流控制,简单的超时和重传机制,如果不要求是实时数据,我想TCP可能会更适合你!

5、

java读取clob字段的几种方法

第一种
Clob clob = rs.getClob("remark");//java.sql.Clob
String detailinfo = "";
if(clob != null){
    detailinfo = clob.getSubString((long)1,(int)clob.length());
}

第二种:
Clob clob = rs.getClob("remark");//java.sql.Clob
int i = 0;
if(clob != null){
     InputStream input = clob.getAsciiStream();
     int len = (int)clob.length();
     byte by[] = new byte[len];
     while(-1 != (i = input.read(by, 0, by.length))){
      input.read(by, 0, i);
     }
     detailinfo = new String(by, "utf-8");
}

第三种:
Clob clob = rs.getClob("remark");//java.sql.Clob
String value="";
String line="";
if(clob!=null){
    Reader reader=((oracle.sql.CLOB)clob).getCharacterStream();
    BufferedReader br=new BufferedReader(reader);
    while((line=br.readLine())!=null){
         value += line + "\r\n";
    }

} 

第一种方法代码量少,且能避免中文乱码问题;第二种方法与第一种方法效率差不多,也是常使用的一种方法;第三种方法效率极低,如果数据比较大的话建议不要使用。

6、Url请求方式中文乱码问题解决 

jsp中post方式请求一般不会乱码,如果乱码加上这句:
解决办法一
request.setCharacterEncoding("utf-8");
而get方式请求,若url含非西欧编码必然会乱码,处理方式:
request.setCharacterEncoding("utf-8");
//将请求参数使用ISO-8859-1分解成字节数组,再将字节数组解码成字符串
String name = new String(request.getParamet("name").getBytes("ISO-8859-1"),"utf-8");

解决办法二
java.net.URLEncoder.encode()传送字符编码
面贴一下解决这个问题参考的文章:
使用java.net.URLEncoder.encode()可以对要传递的中文进行编码
a.在传参数之前先把参数进行转码:java.net.URLEncoder.encode(param);
取值时用语句java.net.URLDecoder.decode(param);再转回中文
b.在你的Tomcat目录-->conf目录-->server.xml里找出这段:
  <Connector
          port="8080"  maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
                enableLookups="false" redirectPort="8443(www.111cn.net)" acceptCount="100"
                debug="0" connectionTimeout="20000"
                disableUploadTimeout="true"
 <!--在里边加上这个参数-->
URIEncoding="gb2312" />

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值