java网络编程 使用长连接

最近涉及了一些网络编程,在这里总结一下。
项目是一个多域仿真系统,模型简要说明下:局域网中有l台机器,代表l个域,每个域有n个节点,每个节点有m种资源每次仿真时,随机选一个节点申请某个资源,这个资源可能好几个域上都有节点拥有它。选择信誉值最高的节点作为服务者,与其进行交互。
对网络的要求是,可以将一个域上的东西发到另一个节点。最开始的想法是tcp开销较大且局域网丢包概率很小,因此考虑用简单快捷的UDP进行传输。UDP协议是无连接的协议,在JAVA中java.util.DatagramSocket负责接收和发送UDP数据报,Datagrampacket表示UDP数据报。DatagramSocket提供了接收和发送数据报的方法。
public void receive(DatagramPacket dst)throws IOException
public void send(DatagramPacket p)throws IOException

测试时发现会报异常,UDP规定每个包大小不能超过64K,而我们传送的数据至少几百KB。期间考虑过手工编号分组的方法,但没能成功。转而考虑TCP。
TCP中服务器程序监听端口,接收客户程序的连接请求。
ServerSocket server=new ServerSocket(8000);

用accpet()方法等待客户连接请求,这个方法是阻塞的,直到有客户连接才返回一个Socket对象
Socket socket=server.accept();

客户端用
Socket socket=new Socket(addr,port);
建立和server的连接。
项目采用TCP连接后可以正常传输数据了,但是由于仿真一次所需时间只要一、两秒钟时间,频繁创建socket导致程序不稳定。后经导师提醒,采用长连接的方法,即一个域初始化时,就与局域网中其他所有域建立tcp连接,这个连接建立后不关闭,因为每次仿真几乎都要传输数据。修改时碰到第一个问题是,以前accept方法是阻塞的,即需要发数据时客户端建立socket,服务端监听线程就收到一个请求,交与处理线程处理。现在socket保持连接,如何知道客户端要发送数据了呢?
直接说解决方法了。长连接是要把socket保存下来的,新建socket的同时,取得它的输出流,因为要发送对象,用ObjectOutputStream来装饰。
sendObjectStream = new ObjectOutputStream(sendSocket.getOutputStream());

要发送时,只要得到sendObjectStream ,调用writeObject即可。由于每个节点即可能是客户端也可能是服务端,每个域可能同时进行发送和接收,这时可以使用保存的这个socket接收,我是在新建这个socket时,就开一个接收线程,把这个socket传过去用于接收。
public class Receiver implements Runnable{
private Socket s = null;
private ObjectInputStream ois = null;
private static volatile boolean bConnected = false;
private Network network;

public Receiver(Socket s,Network n){
this.s = s;
this.network = n;
bConnected = true;
try {
ois = new ObjectInputStream(s.getInputStream());
} catch (IOException e) {
System.out.println("S关闭了");
e.printStackTrace();
}
}

public void run()
{
try
{
while(bConnected)
{
try
{
long begin = System.currentTimeMillis();
NetParameterPacket npp = (NetParameterPacket) ois.readObject();
long end = System.currentTimeMillis();
System.out.println("readObject用的时间为"+(end-begin)+"ms");
PacketHandle ph = new PacketHandle(npp,network,s);
new Thread(ph).start();
}
catch(EOFException e)
{
System.out.println("此时无数据");
try
{
Thread.sleep(500);
} catch (InterruptedException e1)
{
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
catch (IOException e)
{
e.printStackTrace();
if(e instanceof SocketException)
{
bConnected=false;
}
} catch (ClassNotFoundException e)
{
e.printStackTrace();
}
}
}
finally
{
if(ois!=null){
try
{
ois.close();
} catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
ois = null;
}
if(s!= null){
try {
s.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
s = null;
}
}
}

public static void setBConnected(boolean connected)
{
bConnected = connected;
}
}


关于谁向谁新建TCP的问题:局域网中A域在运行时,这时B域申请加入。B域首先在局域网中广播,A域收到广播后进行响应,new一个socket向其建立tcp连接,并新建接收线程,将这个socket传过去用于接收。B域监听到这个tcp后,得到socket=server.accpet();新开一个接收线程的同时,将这个socket保存下来用于发送TCP。发送TCP代码为
public void sendTCPSingle(Object o , String address)
{
Domain d = getDomainByName(address);
if(d == null)
{
System.out.println("没找到该domain");
return;
}
try
{
sendLock.lock();
ObjectOutputStream oos = d.getSendObjectStream();
oos.writeObject(o);
oos.flush();
} catch (IOException e)
{
System.out.println("发送对象出错!");

//对发送异常进行处理
SocketAddress remoteAddr = new InetSocketAddress(address,TCP_PORT);
try
{
if(d.getSendSocket().isClosed())
{
Socket newSocket = new Socket(address,TCP_PORT);
System.out.println("socket已经关闭,重新向"+address+"发送建立TCP连接请求");
d.setSendSocket(newSocket);
}
else
d.getSendSocket().connect(remoteAddr);
}
catch (IOException e1)
{
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
finally
{
sendLock.unlock();
}
}

其中Domain类表示域。

用加锁的原因是,某次交互可能两个线程同时准备发送,这时发送的一方会报异常[color=red]java.net.SocketException: Software caused connection abort: socket write error[/color],接收方也跟着异常[color=red]java.io.StreamCorruptedException: invalid type code: 00[/color]

网上说长连接要用心跳方法检测对方是否还活着,这里因为是局域网且数据一直要传送,就没使用。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值