`
ysen
  • 浏览: 122158 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

javaSocket与C通信

阅读更多

前段时间写了个web端与C服务端之间的通信

 

不过用的是短连接 非堵塞的方式,一直想使用长连接,使tomcat启动的时候就和C服务端进行通信,但是一直没找到方法

希望je的朋友能给点思路。

 

先来看我现在的具体实现

 

通信的核心类

public class newsSockBase 
{
    private SocketChannel sc;  
    private final int MAX_LENGTH = 8192;  
    private ByteBuffer r_buff ;  
    private ByteBuffer w_buff ;  
    private static String host ;  
    private static int port;  

	int		sendBufTotalLen;
	int     sendBufLen;
	int		sendBufStart;
	byte[]  sendBuf;
	
	int		recvBufTotalLen;
	int     recvBufLen;
	int		recvBufStart;
	byte[]  recvBuf;
	
	int		timeout;
	String  msg;
    
    public newsSockBase()
    {
    	r_buff = ByteBuffer.allocate(MAX_LENGTH);  
     	w_buff = ByteBuffer.allocate(MAX_LENGTH); 
     	
		sendBufTotalLen = MAX_LENGTH;
		sendBufLen = sendBufStart = 0;
		sendBuf = new byte[MAX_LENGTH];
		
		recvBufTotalLen = MAX_LENGTH;
		recvBufLen = recvBufStart = 0;
		recvBuf = new byte[MAX_LENGTH];
		
		timeout = 6;
    }
    
    public void setIPandPort(String str,int pt)
    {
    	host = str;
    	port  = pt;
    }
    
    //这两个函数一定要注意 形参是基类 而实际传入的参数是子类,到时候也是调用子类的参数来做
    public void getBufFrompara(InewsDetail nD)
    {
    	int len = nD.encode(sendBuf, sendBufStart, sendBufTotalLen-sendBufStart-sendBufLen);
    	
    	sendBufLen += len;
    	
    }
    
    public int decodeBufToPara(InewsDetail nD)
    {
    	int len = nD.decode(recvBuf, recvBufStart, recvBufLen);
    	if (len>0)	//解码正确的时候才做
    	{
        	recvBufLen -= len;
        	recvBufStart += len;		
    	}
     	
    	return len;	
    }
    
    public void start(InewsDetail nD)
    {    
    
    	//这里需要先根据传入的参数来
    	getBufFrompara(nD);
    	
        try {  
            InetSocketAddress addr = new InetSocketAddress(host, port);  
            // 生成一个socketchannel  
            sc = SocketChannel.open();  
            sc.configureBlocking(false);// 
            // 连接到server  
            sc.connect(addr);  
            while (!sc.finishConnect())  
                ;  
            System.out.println("connection has been established!…");  

           // while (true) 
            {  
                // 回射消息    // 复位,清空            
            	w_buff.clear();  
                w_buff.put(sendBuf,sendBufStart,sendBufLen);  
                w_buff.flip();   // 转到最开始 


                // 发送消息  
                while (w_buff.hasRemaining())  
                    sc.write(w_buff);  
                w_buff.clear();  

                // 进入接收状态 
                while (true)
                {
                	int ss=0;
                    int count;  
                    r_buff.clear(); 
                    while(ss<timeout*100)
                    {           	         
            	        count = sc.read(r_buff);
            	        if (count>0)
            	        	break;
            	        ss++;
            	        Thread.currentThread().sleep(10); 
                    }  
                    
                    if (ss==timeout)
                    {
                    	break;
                    }
                    
                    r_buff.flip();  
                    
                    //判断recvBuf能不能放下接收到的数据
                    if (r_buff.limit()+recvBufStart+recvBufLen>recvBufTotalLen)
                    {
                    	//放不下了
                    	//那就先看看前面是不是有空余
                    	if (recvBufStart>0)
                    	{
                    		for(int i=0;i<recvBufStart;i++)
                    		{
                    			recvBuf[i] = recvBuf[i+recvBufStart];
                    		}
                    		recvBufStart = 0;

                    	}
                    	
                    	if (r_buff.limit()+recvBufStart+recvBufLen>recvBufTotalLen)
                		{
                			//这个时候就是真的说数据区长度不够了,属于致命错误
                    		System.err.println("致命错误! 缓冲区长度过小!");
                		}
                    }
                    else
                    {   // 也可以转化为字符串,不过需要借助第三个变量了。 
                    	
                    	r_buff.get(recvBuf,recvBufStart+recvBufLen,r_buff.limit());
                    	//得到了一次数据就要试着做一次解码,如果能够解码,那就完成解码,不能则表示数据不完整,继续等待新数据
                    	//这里注意返回值 如果是0  表示数据不完整 如果是正数 就是解码的字节数 负数表示解码出错
                    	recvBufLen += r_buff.limit();
                    	if (decodeBufToPara(nD)!=0)
                    		break;
                    }
                     
        	        System.out.println("reply is " + r_buff.limit() + " long " );  
                }
            }  
            sc.socket().close();
            
        } catch (IOException ioe) {  
            ioe.printStackTrace();  
        } catch (InterruptedException ie) {  
            ie.printStackTrace();  
        }  
        
        System.out.println("Exit App....... " ); 
    }

	public static void main(String[] args) 
	{
		newsDetailNewsSum nDNS = new newsDetailNewsSum();
		newsSockBase nsb = new newsSockBase();
		nsb.setIPandPort("192.168.0.106",8888);
		nsb.start(nDNS);
		
		System.out.println("Exit Allllll....... " );
	}

}

 

 

 

下面是报文协议的基类

 

 

//此类试图将所有的通讯协议的编解码都包含在内!按照一次交互的过程来区分
public class newsDetail implements InewsDetail
{
	protected int		netErr;	//用来表示是不是网络出错了,主要是超时。这个时候就不需要检查其他参数了。
	protected int		type;	//就是对应具体的操作类型码
	protected byte[]	StreamID=new byte[16];	//对应具体的流水号
	protected byte[]	asyn = new byte[2];
	
	//这个还是有问题 不能达到预计效果 需要修改
	static private int 	seq=0;	//生成流水号后2位的时候使用的
	static private Calendar	lastCa;
	
	public newsDetail() 
	{
		getStreamID();
	}
	
	
	
	public int getType() {
		return type;
	}



	public void setType(int type) {
		this.type = type;
	}



	//基类中的编解码函数没有作用,具体使用的编解码函数在各个子类中需要重新实现
	//必须有返回值 因为调用者需要根据返回值做一些操作
	//这里的参数 buf是需要填写的缓冲区  start 是缓冲区开始位置 len 是可用的缓冲区长度
	public int encode(byte[] buf,int start,int len)
	{
		return 0;
	}
	//这里的参数 buf是需要需要解码的缓冲区  start 是缓冲区开始位置 len 是需要解码的长度
	public int decode(byte[] buf,int start,int len)
	{
		return 0;
	}

	public void getStreamID()
	{
		Calendar ca = Calendar.getInstance();
	      int year = ca.get(Calendar.YEAR);//获取年份
	      int month=ca.get(Calendar.MONTH)+1;//获取月份 
	      int day=ca.get(Calendar.DATE);//获取日
	      int minute=ca.get(Calendar.MINUTE);//分 
	      int hour=ca.get(Calendar.HOUR);//小时 
	      int second=ca.get(Calendar.SECOND);//秒
	      int am_pm=ca.get(Calendar.AM_PM);
	      if (am_pm==Calendar.PM)
	    	  hour += 12;
	      if (hour>=24)
	    	  hour -= 24;
	      
	    System.out.println(seq);
	       
	      if (lastCa!=ca)
	      {
	    	  lastCa = ca;
	    	  seq = 12;
	      }
	      else
	      {
	    	  seq++;
	    	  if (seq>=100)
	    		  seq = 0;
	      }
	      
	      //现在根据上面的字段组成StreamID字符串
	      //目前先使用手工的办法来写,效率高一些     
	      StreamID[0] = (byte)(year/1000+'0');
	      StreamID[1] = (byte)((year-(StreamID[0]-'0')*1000)/100+'0');
	      StreamID[2] = (byte)((year-(StreamID[0]-'0')*1000-(StreamID[1]-'0')*100)/10+'0');
	      StreamID[3] = (byte)(year-(StreamID[0]-'0')*1000-(StreamID[1]-'0')*100-(StreamID[2]-'0')*10+'0');
	      
	      StreamID[4] = (byte)(month/10+'0');
	      StreamID[5] = (byte)((month-(StreamID[4]-'0')*10)+'0');
	      
	      StreamID[6] = (byte)(day/10+'0');
	      StreamID[7] = (byte)((day-(StreamID[6]-'0')*10)+'0');
	      
	      StreamID[8] = (byte)(hour/10+'0');
	      StreamID[9] = (byte)((hour-(StreamID[8]-'0')*10)+'0');
	      
	      StreamID[10] = (byte)(minute/10+'0');
	      StreamID[11] = (byte)((minute-(StreamID[10]-'0')*10)+'0');
	      
	      StreamID[12] = (byte)(second/10+'0');
	      StreamID[13] = (byte)((second-(StreamID[12]-'0')*10)+'0');
	      
	      StreamID[14] = (byte)(seq/10+'0');
	      StreamID[15] = (byte)((seq-(StreamID[14]-'0')*10)+'0');   
	      
	      System.out.println("现在时间");
	      System.out.println("用Calendar.getInstance().getTime()方式显示时间: " + ca.getTime());
	      System.out.println("用Calendar获得日期是:" + year +"年"+ month +"月"+ day + "日");      
	      System.out.println("用Calendar获得时间是:" + hour +"时"+ minute +"分"+ second +"秒");
	
	      
	}
	

	public static void main(String[] args) 
	{
		{
			newsDetail nn1 = new newsDetail();
		}
		try {
			Thread.currentThread().sleep(3000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} 
		{
			newsDetail nn2 = new newsDetail();
		}
	      	


	}

}

 

分享到:
评论
20 楼 ysen 2009-12-18  
topcode 写道
楼主不是很喜欢发设计模式的帖子么

谢谢关注O(∩_∩)O哈哈~
19 楼 topcode 2009-12-18  
楼主不是很喜欢发设计模式的帖子么
18 楼 ysen 2009-12-16  
pwz1985 写道
我最近刚好做了个javaSocket与C通信的组件

//连接成功flag
boolean connSuccess = false;
while (!connSuccess) {
try {
socket = new Socket(ip,port);
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
//连接失败,3秒后重新连接
System.out.println("(IP="+ip+","+"PORT="+port+")连接失败,3秒后重连");
try {
Thread.sleep(3000);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
continue;
}
connSuccess = true;
}

这个问题 java 或 c 有一边 转换下就好了
17 楼 bcw104 2009-12-16  
linliangyi2007 写道
楼主类名不规范的说

支持

JAVA与C通讯得考虑大端小端问题吧
16 楼 ysen 2009-12-16  
tjgamejx2 写道
ysen 写道
tjgamejx2 写道
楼主,如果是长连接的话,每次调用一次doGet()或者doPost()向C德socket发送一个报文,然后C处理后回一个消息,当并发高的时候,不需要很高,发送A,C回A,发送B,C回B。如果并发了,JAVA发条A,紧接着发了条B,C接着回了条消息,楼主,C回的这个消息,您能确定这条消息是A的结果还是B的结果吗。
短连接的话,每条消息都对应一个socket,你能确定回的消息就是本次处理的结果。

发送的报文有流水号啊,根据流水号判断,在转发个客户端,这样的话可能要用到
缓存和服务器推技术,不知道我说的对不对

不管什么标志都是没有用的,你在一个doPost方法里发送了一条消息,你就必须在这个方法里得到回应的消息。比如你发的是00001流水号订单,接到的结果是00002,你怎么办,另外一个方法里发的00002,却得到了00001,它咋办。

我说一个方法吧,楼主socket得到的消息全部存在全局变量里,这样每个方法才能共享资源。你发的00001,你就一直查全局变量里是否有00001回应的消息。但是效率明显很烂。

你的需求应该是要用到短连接阻塞

一开始就是 短连接堵塞, 效果也不错
后来改成上面的样子 短连接非堵塞,有的时候发过去接不到返回的信息,很明显不行
15 楼 tjgamejx2 2009-12-16  
ysen 写道
tjgamejx2 写道
楼主,如果是长连接的话,每次调用一次doGet()或者doPost()向C德socket发送一个报文,然后C处理后回一个消息,当并发高的时候,不需要很高,发送A,C回A,发送B,C回B。如果并发了,JAVA发条A,紧接着发了条B,C接着回了条消息,楼主,C回的这个消息,您能确定这条消息是A的结果还是B的结果吗。
短连接的话,每条消息都对应一个socket,你能确定回的消息就是本次处理的结果。

发送的报文有流水号啊,根据流水号判断,在转发个客户端,这样的话可能要用到
缓存和服务器推技术,不知道我说的对不对

不管什么标志都是没有用的,你在一个doPost方法里发送了一条消息,你就必须在这个方法里得到回应的消息。比如你发的是00001流水号订单,接到的结果是00002,你怎么办,另外一个方法里发的00002,却得到了00001,它咋办。

我说一个方法吧,楼主socket得到的消息全部存在全局变量里,这样每个方法才能共享资源。你发的00001,你就一直查全局变量里是否有00001回应的消息。但是效率明显很烂。

你的需求应该是要用到短连接阻塞
14 楼 ysen 2009-12-16  
tjgamejx2 写道
楼主,如果是长连接的话,每次调用一次doGet()或者doPost()向C德socket发送一个报文,然后C处理后回一个消息,当并发高的时候,不需要很高,发送A,C回A,发送B,C回B。如果并发了,JAVA发条A,紧接着发了条B,C接着回了条消息,楼主,C回的这个消息,您能确定这条消息是A的结果还是B的结果吗。
短连接的话,每条消息都对应一个socket,你能确定回的消息就是本次处理的结果。

发送的报文有流水号啊,根据流水号判断,在转发个客户端,这样的话可能要用到
缓存和服务器推技术,不知道我说的对不对
13 楼 tjgamejx2 2009-12-16  
楼主,如果是长连接的话,每次调用一次doGet()或者doPost()向C德socket发送一个报文,然后C处理后回一个消息,当并发高的时候,不需要很高,发送A,C回A,发送B,C回B。如果并发了,JAVA发条A,紧接着发了条B,C接着回了条消息,楼主,C回的这个消息,您能确定这条消息是A的结果还是B的结果吗。
短连接的话,每条消息都对应一个socket,你能确定回的消息就是本次处理的结果。
补充下:如果你使用长连接,想要每条信息都得到自己对应的结果,你必须用阻塞。处理效果,可想而知。

问下楼主,请问短连接非阻塞,为的是什么,非阻塞和多线程的区别在于,多线程,每个线程独立,发的信息回得到其对应结果。
貌似楼主应该用短连接阻塞。你的业务才能清晰,聊天程序非阻塞还有点道理,毕竟我这次回的消息,不一定就是你上个问题。
12 楼 pwz1985 2009-12-16  
我最近刚好做了个javaSocket与C通信的组件

//连接成功flag
boolean connSuccess = false;
while (!connSuccess) {
try {
socket = new Socket(ip,port);
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
//连接失败,3秒后重新连接
System.out.println("(IP="+ip+","+"PORT="+port+")连接失败,3秒后重连");
try {
Thread.sleep(3000);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
continue;
}
connSuccess = true;
}
11 楼 myworkfirst 2009-12-16  
ysen 写道
for_cyan 写道
如果网络断开或者程序出现异常该如何重连呢?

这个问题不知道改如何解决

   写一个重连方法,放到异常里,如果断开了,就会抛异常,调用重连方法,判断标志位,如果一直未连接上,每间隔几秒发一次连接请求,如果连接上后,修改标志位,并跳到业务代码,走业务。

    这是基本的方法
10 楼 ysen 2009-12-16  
for_cyan 写道
如果网络断开或者程序出现异常该如何重连呢?

这个问题不知道改如何解决
9 楼 ysen 2009-12-16  
trydofor 写道
有没有遇到 字节序的问题。
-----------------------------
a) Little-Endian就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。
b) Big-Endian就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。
c) 网络字节序:TCP/IP各层协议将字节序定义为Big-Endian,因此TCP/IP协议中使用的字节序通常称之为网络字节序。

C服务端将 java 的低字节与高字节的 位子交换了
8 楼 trydofor 2009-12-16  
有没有遇到 字节序的问题。
-----------------------------
a) Little-Endian就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。
b) Big-Endian就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。
c) 网络字节序:TCP/IP各层协议将字节序定义为Big-Endian,因此TCP/IP协议中使用的字节序通常称之为网络字节序。
7 楼 for_cyan 2009-12-16  
如果网络断开或者程序出现异常该如何重连呢?
6 楼 diggywang 2009-12-16  
开发一个servlet,初始化的时候就用你那个核心类去连接服务器,并将servlet设置成loadonstartup
5 楼 ysen 2009-12-15  
我错了,汗死我了
4 楼 ugibb510 2009-12-15  
linliangyi2007 写道
楼主类名不规范的说

3 楼 linliangyi2007 2009-12-15  
楼主类名不规范的说
2 楼 ysen 2009-12-14  
nishizhutoua 写道
汗死我,大家都好喜欢测试用8888端口啊... ...

8080 Eclipse 占了  8081被 netbean 占了  ╮(╯▽╰)╭
1 楼 nishizhutoua 2009-12-14  
汗死我,大家都好喜欢测试用8888端口啊... ...

相关推荐

    JAVA与C的各种相互之间的Socket编程

    在Java和C这两种语言中,Socket编程提供了丰富的功能,使得跨平台的网络通信变得可能。本篇文章将深入探讨Java和C如何进行Socket编程,并提供相关的实践示例。 首先,我们来了解Socket的基本概念。Socket可以被看作...

    java socket通信 一对多

    1)设计程序,分别构建通信的两端:服务器端和客户端应用程序,套接字类型为面向连接的Socket,自己构建双方的应答模式,实现双方的数据的发送和接收(S发给C,C发给S)。 2)服务端程序能响应单个或任意多个客户端...

    总结javasocket编程.doc

    在客户机/服务器(C/S)架构中,服务器通常作为守护进程持续运行,监听特定端口,等待客户端发起连接请求。一旦客户端连接,服务器创建一个新的服务进程来处理请求,同时自身继续监听其他客户端的连接。客户端在需要...

    javasocket实现聊天室毕业设计

    Java Socket 实现聊天室毕业设计是一项综合性的编程任务,它涉及到客户端-服务器(C/S)架构、网络通信、用户身份验证以及文件传输等核心概念。在这个项目中,开发者需要运用Java语言来创建一个功能完善的聊天应用...

    php与java通过socket通信的实现代码

    代码如下: 复制代码 代码如下:import java.io.*; import java.net.*; public class Server { public static void main(String[] args) throws IOException{ System.out.println(“Server started !...

    Java网络通信对ICQ的实现.pdf

    Java 网络通信对 ICQ 的实现 ICQ 是一种基于网络的即时通信工具,可以实现点对点的网络聊天系统。ICQ 的成功推动了国内的本土化,出现了许多国产的 ICQ 软件,如 OICQ 和 Tomq 等。 ICQ 的功能主要是即时信息交流,...

    Java版socket编程 局域网聊天工具

    每个客户端通过Socket连接到服务器,实现与其他客户端的通信。 9. **安全性**: 虽然题目未提及,但安全是任何通信工具的重要方面。Java的SSL/TLS库可以用于加密Socket连接,保护用户的隐私信息。 10. **多线程**...

    java CRC16位校验码

    最近在开发javaSocket和C的通信,其中有数据校验就是采用CCITT方式,在网上找了好多,都不切合实际使用,经过一个星期的奋斗查资料,再根据网上搜获,写出了一套标准的校验方法,结果和C语言的校验一直,其中考虑了...

    Java网络编程 Socket编程

    UDP套接字与TCP套接字不同,它不需要在通信前建立连接,直接发送和接收数据报即可。 在更复杂的应用中,比如聊天程序或多人网络游戏,仅有一个服务器主线程处理所有客户端的连接请求将无法满足需求。这时就需要利用...

    socket 和jaxb

    在IT领域,尤其是在Java开发中,Socket编程与JAXB(Java Architecture for XML Binding)是两个极为重要的技术点,它们各自解决着不同的问题,但都扮演着数据通信与处理中的关键角色。 ### Socket编程 Socket是一...

    软件工程师的大学规划.pdf

    JavaSocket用于网络编程,RMI和CORBA都支持不同计算机上对象之间的通信。MIS(管理信息系统)是企业信息化管理的基础。 总结来说,软件工程师的大学规划应当包括但不限于对C/C++、C#、Java语言的深入学习,熟练掌握...

Global site tag (gtag.js) - Google Analytics