一个很棘手java的Socket通信问题

xiongzeng 2007-11-02 10:21:06
一个是Socket的服务端(用Java编写的),另一个是Socket的客户端(这个Socket客户端C++写的)。Socket的服务端可以向Socket的客户端发送信令,这个大家都知道,但现在的问题是Socket的服务端要抛出一个多线程随时接收Socket客户端发过来的信令。
这里面涉及到几个问题:
1.线程之间的通信。当Socket的服务端向Socket客户端发送一个信令时,发送(发送是一个方法)和接收(接收暂时放在多线程里)就要分开,当多线程中的接收收到Socket客户端处理完的结果时,不知道怎么通知发送的那个方法?
2.超时问题。因为每个通信是有时间限制的。发送和接收都不在一个方法中,不知道怎么控制超时?
为了把问题说明清楚,把我写的代码贴出来(共三部分),请大家为我解答:

//A部分.监听端口的Socket服务端
public class SocketServer{
private ServerSocket serverSocket;
public static Socket saveSocket=null;//保存socket的全局变量
public static saveResult=null;//保存通信结果的全局变量
private Socket socket;
public void run(){
serverSocket= new ServerSocket(8188);
socket = serverSocket.accept();
saveSocket=socket用一个全局的变量保存这个Socket
new SocketServerThread(socket).start();//抛出一个多线程,便于可以随时接收客户端信令
......


//B部分.抛出的那个多线程,便于可以随时接收客户端信令
public class SocketServerThread extends Thread {
private Socket socket;
XMLbodyParser xmlParser=new XMLbodyParser();
ByteArrayOutputStream buf = new ByteArrayOutputStream();
public SocketServerThread(Socket s){
this.socket = s;
}
public void run(){
InputStream in=null;
try {
in =new BufferedInputStream(socket.getInputStream());
} catch (IOException e){
}
int readLen=1024;
int count=0;
byte[] readBuf=new byte[readLen];
count=in.read(readBuf);
buf.write(readBuf,0,count);
SocketServer.saveResult=new String(new String(buf.toByteArray()));//保存客户端返回的结果
......


//C部分.在Socket服务端,在一个方法中用那个保存全局变量中的Socket向客户端发送信令
......
Socket socket =SocketServer.saveSocket;
OutputStream out=null;
try {
out = socket.getOutputStream();
} catch (IOException e){
}
//发送
String cmd= "ok ";
try {
byte[] response =cmd.getBytes(Constant.PACKAGE_ENCODE);
out.write(response);
out.flush();
} catch (IOException e){
}
String result=null
int i=0;
while(i <5){//每隔一段时间访问全局变量saveResult,检查客户端是否返回了处理结果
try {
Thread.sleep(50);
} catch (InterruptedException e){
}
i++;
result=SocketServer.saveResult;//将返回的结果值保存
if(result!=null){
break;
}
}
......

我不想每隔一段时间去检查那个全局变量的结果,因为程序运行的性能不是很好,尤其并发的情况下。情况就是这样的,不知大家看懂了没有?如疑问,请告知。请高手指点!
...全文
1190 28 打赏 收藏 转发到动态 举报
写回复
用AI写文章
28 条回复
切换为时间正序
请发表友善的回复…
发表回复
dddeee 2007-11-14
  • 打赏
  • 举报
回复
用mina,一切都简单轻松
xiongzeng 2007-11-14
  • 打赏
  • 举报
回复
谢谢qybao!
这是一个会议系统,只要创建会议(是通过用户在页面点击来调用C部分的方法来发起通信的),都会送信。
这是每次送信的内容,是一个xml体,即:
<iabi><cmd id="3000" sessionid="1412412341"><confid>88888888</confid></cmd></iabi>
跟您说的一样,只管一个送一个收就行。怎么确定信息是C的哪次发信返回的结果呢?就是通过sessionid的值来确定的(不好意思,我没有在上面的代码中表现出来),即我每次在送信前记住这个sessionid,在收信时就找对应的结果(结果也是一个xml体)。每次送信的sessionid都不相同。
qybao,清楚了吗?有没有好的办法?


qybao 2007-11-13
  • 打赏
  • 举报
回复
我不知道你想做什么东西,总之你这样的设计我觉得不好。B一直收信,那么你怎么保证每次收信就是你送信后返回的结果。比如一上来C部分就连续好几次发信,这时候B部分收到信息了,你怎么确定信息是C的哪次发信返回的结果?除非服务器端和客户端定义好一套通信协议。如果你非要在你的基础上达到你的要求,也就是不管哪次收信是哪次送信的结果,只管一个送一个收就行,那也不难做到,但是你要先告诉我,你的送信方法是在什么时候调用的,你是怎么调用的?因为不同的调用,解决方法可以不同。
xiongzeng 2007-11-13
  • 打赏
  • 举报
回复
谢谢大家,谢谢qybao.
您给我改,和我原来的程序都是达到同样的结果,您的或许性能要好一点,但还是一点没有实现:
当我把B部分的循环的注释去掉,即让线程一直处理收信。那么当我用C部分发信时,就(从"//追加处理start"开始的代码)处于无限等待。
我目的就要达到让B部分一直处理收信,同时还可以利用C部分发信.
qybao,有没有好的办法?
xiongzeng 2007-11-12
  • 打赏
  • 举报
回复
谢谢qybao!
1.A部分的run代码是只运行一次,可引起不用多线程接收;
2.B部分是有循环接收。我会在下面把全部的代码贴出来。
A部分:

public class SocketServer extends Thread {
private Logger log= Logger.getLogger(SocketServer.class.getName());
private ServerSocket serverSocket;
private int listenPort =8188;
public SocketServer(int listenPort) {
this.listenPort = listenPort;
}
public static Map clientMap=new HashMap();//保存socket的HashMap
public static String returnResult=null;//保存发出通信的的返回结果
private Socket socket;
private boolean isLoop= false;
public void run(){
isLoop=true;
try {
serverSocket= new ServerSocket(listenPort);
log.info("Starting listen......");
String ccsIp="";
CcsInfo ccsInfo=null;
while(isLoop){
socket = serverSocket.accept();
log.info("Listening->RemoteSocketAddress:"+socket.getRemoteSocketAddress()+" InetAddress:"+socket.getInetAddress());
ccsIp=socket.getRemoteSocketAddress().toString().trim();
ccsIp=ccsIp.substring(1,ccsIp.indexOf(":"));
log.info("RemoteSocketIp:"+ccsIp);
clientMap.put(ccsIpsocket);
log.info("Having listen the IP:" +ccsIp+" The ccsID:"+ccsInfo.getCcsId());
new SocketServerThread(socket).start();
}
} catch (Exception e) {
isLoop = false;
log.error(" "+e);
}
}
}



B部分:


public class SocketServerThread extends Thread {
private Logger log= Logger.getLogger(SocketServerThread.class.getName());
private Socket socket;
private int result;
private boolean isLoop;
ByteArrayOutputStream buf = new ByteArrayOutputStream();
public SocketServerThread(Socket s){
this.socket = s;
}
public void run(){
isLoop=true;
result=0;
InputStream in=null;
try {
in =new BufferedInputStream(socket.getInputStream());
} catch (IOException e){
log.error("The CCS "+socket.getRemoteSocketAddress()+" is unusual:"+e);
}
int readLen=1024;
int count=0;
byte[] readBuf=new byte[readLen];
while(true&&isLoop){
count=0;
try{
do{
count=in.read(readBuf);
if(count==-1){
isLoop=false;
throw new IOException("The receive data length is not right");
}
buf.write(readBuf,0,count);
}while (count==readLen);
log.info("Receive:"+new String(buf.toByteArray()));
}catch(IOException ex){
log.error("The CCS "+socket.getRemoteSocketAddress()+" correspondence is unusual:"+ex);
}
SocketServer.returnResult=new String(buf.toByteArray());//保存返回结果
}
}
}



C部分:



public class Communicate {
public synchronized String conferenceComm(String ip){

OutputStream out=null;
try {
out = socket.getOutputStream();
} catch (IOException e){
log.error("Connecting the CCS that ip is "+socket.getInetAddress()+" is exception:"+e);
return null;
}
//先发送
String cmdXml="request";
try {
byte[] response =cmdXml.getBytes();
out.write(response);
out.flush();
} catch (IOException e){
log.error("Sending command to CCS that ip is "+socket.getInetAddress()+" is exception:"+e);
return null;
}
log.info("Send:"+cmdXml);
String result=null;
int i=0;
while(i<5){
try {
Thread.sleep(50);
} catch (InterruptedException e){
log.error(e);
}
i++;
result=SocketServer.returnResult;
if(result!=null){
break;
}
}
return result;
}
}



怎么修改这三个部分来实现我在最上面提的问题?
xiongzeng 2007-11-12
  • 打赏
  • 举报
回复
谢谢qybao!
1.A部分的run代码是只运行一次,可引起不用多线程接收;
2.B部分是有循环接收。我会在下面把全部的代码贴出来。
A部分:

public class SocketServer extends Thread {
private Logger log= Logger.getLogger(SocketServer.class.getName());
private ServerSocket serverSocket;
private int listenPort =8188;
public SocketServer(int listenPort) {
this.listenPort = listenPort;
}
public static Map clientMap=new HashMap();//保存socket的HashMap
public static String returnResult=null;//保存发出通信的的返回结果
private Socket socket;
private boolean isLoop= false;
public void run(){
isLoop=true;
try {
serverSocket= new ServerSocket(listenPort);
log.info("Starting listen......");
String ccsIp="";
CcsInfo ccsInfo=null;
while(isLoop){
socket = serverSocket.accept();
log.info("Listening->RemoteSocketAddress:"+socket.getRemoteSocketAddress()+" InetAddress:"+socket.getInetAddress());
ccsIp=socket.getRemoteSocketAddress().toString().trim();
ccsIp=ccsIp.substring(1,ccsIp.indexOf(":"));
log.info("RemoteSocketIp:"+ccsIp);
clientMap.put(ccsIpsocket);
log.info("Having listen the IP:" +ccsIp+" The ccsID:"+ccsInfo.getCcsId());
new SocketServerThread(socket).start();
}
} catch (Exception e) {
isLoop = false;
log.error(" "+e);
}
}
}



B部分:


public class SocketServerThread extends Thread {
private Logger log= Logger.getLogger(SocketServerThread.class.getName());
private Socket socket;
private int result;
private boolean isLoop;
ByteArrayOutputStream buf = new ByteArrayOutputStream();
public SocketServerThread(Socket s){
this.socket = s;
}
public void run(){
isLoop=true;
result=0;
InputStream in=null;
try {
in =new BufferedInputStream(socket.getInputStream());
} catch (IOException e){
log.error("The CCS "+socket.getRemoteSocketAddress()+" is unusual:"+e);
}
int readLen=1024;
int count=0;
byte[] readBuf=new byte[readLen];
while(true&&isLoop){
count=0;
try{
do{
count=in.read(readBuf);
if(count==-1){
isLoop=false;
throw new IOException("The receive data length is not right");
}
buf.write(readBuf,0,count);
}while (count==readLen);
log.info("Receive:"+new String(buf.toByteArray()));
}catch(IOException ex){
log.error("The CCS "+socket.getRemoteSocketAddress()+" correspondence is unusual:"+ex);
}
SocketServer.returnResult=new String(buf.toByteArray());//保存返回结果
}
}
}



C部分:



public class Communicate {
public synchronized String conferenceComm(String ip){

OutputStream out=null;
try {
out = socket.getOutputStream();
} catch (IOException e){
log.error("Connecting the CCS that ip is "+socket.getInetAddress()+" is exception:"+e);
return null;
}
//先发送
String cmdXml="request";
try {
byte[] response =cmdXml.getBytes();
out.write(response);
out.flush();
} catch (IOException e){
log.error("Sending command to CCS that ip is "+socket.getInetAddress()+" is exception:"+e);
return null;
}
log.info("Send:"+cmdXml);
String result=null;
int i=0;
while(i<5){
try {
Thread.sleep(50);
} catch (InterruptedException e){
log.error(e);
}
i++;
result=SocketServer.returnResult;
if(result!=null){
break;
}
}
return result;
}
}

seaforce 2007-11-12
  • 打赏
  • 举报
回复
楼主你这么读:count=in.read(readBuf);
不会阻塞吗?我现在就遇到这个函数阻塞了,因为你这么读的话,socket是一直有效的,所以一直等待读头读完!
希望您解答一下,谢谢!
还有,in =new BufferedInputStream(socket.getInputStream());
这个读头用BufferedInputStream有什么不一样吗?
为什么不可以:InputStream in = socket.getInputStream();
谢谢您!
qybao 2007-11-12
  • 打赏
  • 举报
回复
不知道LZ要做什么东西,就在LZ的基础上修改了。
感觉LZ的Communicate是主class,像map这样的全局变量还是移到主类Communicate上比较好

A部分:

public class SocketServer extends Thread {
private Logger log= Logger.getLogger(SocketServer.class.getName());
private ServerSocket serverSocket;
private int listenPort =8188;
public SocketServer(int listenPort) {
this.listenPort = listenPort;
}
//public static Map clientMap=new HashMap();//保存socket的HashMap //这里移到Communicate类里
//public static String returnResult=null;//保存发出通信的的返回结果 //不要用全局变量,没给线程都修改这里,这个全局变量根本没法保存正确的信息
private Socket socket;
//private boolean isLoop= false; //这个一点意义都没有
public void run(){
//isLoop=true;
try {
serverSocket= new ServerSocket(listenPort);
log.info("Starting listen......");
String ccsIp="";
CcsInfo ccsInfo=null;
//while(isLoop){ //改成while(true)就可以了
while(true){
socket = serverSocket.accept();
log.info("Listening->RemoteSocketAddress:"+socket.getRemoteSocketAddress()+" InetAddress:"+socket.getInetAddress());
ccsIp=socket.getRemoteSocketAddress().toString().trim();
ccsIp=ccsIp.substring(1,ccsIp.indexOf(":"));
log.info("RemoteSocketIp:"+ccsIp);
//clientMap.put(ccsIpsocket); //这里改成如下
Communicate.addSocket(ccsId, ccsIpsocket); //不要把map随意暴露出来,可以通过某个方法去操作
//题外话,这里用客户端IP作为key并不能保证唯一,因为同一个客户端可以启动多个程序通信,这样同一个客户端的socket的IP都是一样的,所以光用IP是不够的,这里怎么解决就留给LZ自己思考了(提示:ip+port等等)。我就当你一个客户端只启动一个进程通信了。
log.info("Having listen the IP:" +ccsIp+" The ccsID:"+ccsInfo.getCcsId()); //这里ccsInfo是什么?估计LZ省略了一些代码,我就当这里已经取到值了,否则就出现空指针错误
//new SocketServerThread(socket).start(); //这里移到Communicate类里,从你的处理流程上看,这里也收不到什么信息
yield(); //缓和一下循环,要不要自己决定
}
} catch (Exception e) {
//isLoop = false; //这个一点意义都没有,出异常,上面的循环就退出了,整个线程也结束了
log.error(" "+e);
}
}
}



B部分:

public class SocketServerThread extends Thread {
private Logger log= Logger.getLogger(SocketServerThread.class.getName());
private Socket socket;
private int result;
//private boolean isLoop; //这个也是没意义
ByteArrayOutputStream buf = new ByteArrayOutputStream();
public SocketServerThread(Socket s){
this.socket = s;
}
public void run(){
//isLoop=true;
result=0;
InputStream in=null;
try {
in =new BufferedInputStream(socket.getInputStream());
} catch (IOException e){
log.error("The CCS "+socket.getRemoteSocketAddress()+" is unusual:"+e);
}
int readLen=1024;
int count=0;
byte[] readBuf=new byte[readLen];
//while(true&&isLoop){ //这里没必要循环了,除非你想要线程一直处理收信,但是那样的话,你就要做一个通信命令和返信内容相对应的map了,否则你哪次返信是哪个命令的结果
count=0;
try{
do{
count=in.read(readBuf);
if(count==-1){
isLoop=false;
throw new IOException("The receive data length is not right");
}
buf.write(readBuf,0,count);
}while (count==readLen);
log.info("Receive:"+new String(buf.toByteArray()));
}catch(IOException ex){ //这里LZ自己看情况再追加timeout异常的处理
log.error("The CCS "+socket.getRemoteSocketAddress()+" correspondence is unusual:"+ex);
}
//SocketServer.returnResult=new String(buf.toByteArray());//保存返回结果 //不要用全局变量,可以把结果保存到一个map中
Communicate.addResult(socket, new String(buf.toByteArray()));//保存返回结果
// }
}
}



C部分:


public class Communicate {
private static Map socketMap = new HashMap();
private static Map resultMap = new HashMap();

public static addSocket(String key, Socket socket) {
synchronized(socketMap) {
socketMap.put(key, socket);
}
}

public static addResult(socket, String msg) {
synchronized(socketMap) {
socketMap.put(socket, msg); //题外话,如果B部分循环收信,可以在这里把msg放到一个通信指令和返信对应的map中,然后在放到这个resultMap
}
}

//public synchronized String conferenceComm(String ip){ //这里可以把synchornized去掉了,LZ所说的瓶颈就在这里吧
public String conferenceComm(String ip){
//追加处理start
Socket socket = null;
synchronized(socketMap) {
socket = (Socket)socketMap.get(ip);
}
if (socket == null) {
log.error("socket not found. Ip=" + ip);
return null;
} //追加处理end

OutputStream out=null;
try {
out = socket.getOutputStream();
} catch (IOException e){
log.error("Connecting the CCS that ip is "+socket.getInetAddress()+" is exception:"+e);
return null;
}
//先发送
String cmdXml="request";
try {
byte[] response =cmdXml.getBytes();
out.write(response);
out.flush();
} catch (IOException e){
log.error("Sending command to CCS that ip is "+socket.getInetAddress()+" is exception:"+e);
return null;
}
log.info("Send:"+cmdXml);
String result=null;
/*以下处理不要
int i=0;
while(i<5){
try {
Thread.sleep(50);
} catch (InterruptedException e){
log.error(e);
}
i++;
result=SocketServer.returnResult;
if(result!=null){
break;
}
}
*/ //到此不要

//追加处理start
socket.setSoTimeout(1000*60); //timeout时间自己定
Thread thread = new SocketServerThread(socket); //这里开始收信
thread.start();
thread.join(); //等待收信,实际上收信处理慢的话,这里还是会发生瓶颈

//获取返信结果并返回;
synchronized (resultMap) {
result = resultMap.get(socket);
} //追加处理end

return result;
}
}


不知道LZ要做一个什么样的系统,但是从类结构设计上看,我个人认为这样的设计模式很差,建议LZ最好和你的leader或者Manager好好沟通交流一下。
qybao 2007-11-11
  • 打赏
  • 举报
回复
LZ,恕我直言,你的代码跟例子的代码差远了
首先,人家的MultiThreadServer是用循环来接收客户连接请求的,所以我前面就问过你,你的A部分的run代码是否只运行一次,也就是只连接一次,如果不是,那么就应该有某个方法在循环调用run方法,那么每调用一次run方法就重新new一个serverSocket,假设你new成功,那么也是你服务器端运行多个服务器程序在接收客户请求,即一个服务器程序对应一个客户端程序,所以你根本没必要用多线程接收,所以我才感到疑惑。你也一直没好好回答我的疑惑。
其次,你B部分也没有循环接收,也就是收一次就结束了,所以我疑惑跟上面一样。
最后,C部分处理,我上面也说过了,如果你非要把该方法写在另一个类里,那么你就要把socket和返信内容当参数传进去,因为另一类已经不知道你要给哪个客户端发信了,除非你用一个全局map保存你的socket,然后送信的时候指定map的key,然后找到相应的socket,然后再送信。
要做到你的要求并不难,首先MultiThreadServer类和Handler类你可以采用例子的,然后在MultiThreadServer追加一个static的送信方法

public static void send(Socket socket, String msg) {
//把C部分代码写在这里,其中相应的socket和result换成相应的参数,catch以后的代码全不要
}
然后在Handler的run的地方追加,比如
try {
//想先送信就先调用send方法
MultiThreadServer.send(socket, "ok");
...
//中间代码还是保留原样
//收到客户端信息后再返信
//pw.println(echo(msg));其实人家这里就是返信,所以LZ如果你看明白人家的程序你就知道该怎么做了
MultiThreadServer.send(socket, msg); //这里就是返信
if(msg.equals("bye"))
break;

如果send方法不想用static,那么就要把MultiThreadServer的实例也传给Handler,也就是Handler的构造函数多加一个参数
都说到这个份了,LZ还不明白,那我也不知道再怎么指点了。
同时,我希望LZ能明确地回答,你C部分的代码原本打算什么时候,怎么样去调用的?这个问题很关键,你不说清楚我也只好把它放到Handler类中去调用了。





xiongzeng 2007-11-10
  • 打赏
  • 举报
回复
谢谢qybao,irvine007,谢谢大家。
请先看一下http://blog.csdn.net/goby2008/archive/2007/10/21/1836244.aspx
其实我的A部分相当于MultiThreadServer类;B部分相当于Handler类。它是模拟客户端多用户向同一服务器端发送请求。那能不能在它的基础上来完成我的功能?即在另外的类中发信(像我的C部分一样),然后利用Handler类接收。请高手指点
Ji秋风 2007-11-08
  • 打赏
  • 举报
回复
楼主,个人觉得你设计有问题。
全局变量乱用导致程序混乱。

new SocketServerThread(socket).start() //在c下面已经算是fork一个进程了。
这个时候socket已经独立在处理客户端的请求了,业务逻辑处理完成之后也是“自己”返回给客户端应答。

即使这个时候你想把返回应答报文,作为一个独立的函数或者方法来写
你只要把socket以及buffer作为参数传递过去即可。

超时控制,每个socket都可以设置超时的,具体查一下doc即可。

你把以下两个静态变量去掉,就应该能够解决你的问题了。
public static Socket saveSocket=null;//保存socket的全局变量
public static saveResult=null;//保存通信结果的全局变量
zxianwu 2007-11-07
  • 打赏
  • 举报
回复
请问谁能帮忙我解决这个问题,在线等待.......
zxianwu 2007-11-07
  • 打赏
  • 举报
回复
对呀,我觉得也不是很棘手的问题呀,我不是很明白你的意识,你能够把意识说清楚一点,或许我也可以帮忙你 解决,我也在做一个socket长连接碰到一个问题,既然这么多人在这里讨论socket我也把我的问题贴出来吧。
我和银行建立socket长连接时出现如下异常:
java.net.Socket Exception:Software caused connection abort:socket write error 抛出这个异常之后连接就断开了,找不到原因。
qybao 2007-11-07
  • 打赏
  • 举报
回复
不是这个意思。LZ好像没好好看问题,我觉得没法交流了。
你还是一个一个问题回答吧
1 你的服务器端的run方法是什么时候,怎么被调用的?
答:请把答案写在这里

2 A部分代码
socket = serverSocket.accept();
这里获得客户端的Socket连接,然后
new SocketServerThread(socket).start();
这里把Socket通信处理(通信包括收信送信)交给线程去做,如果再有客户端连接,就会获得新的socket,也会创建新的线程去处理新的socket,这样,对于一个socket来说,只有一个线程在处理通信,不存在多个线程同时处理一个socket,这样,B部分处理
SocketServer.saveResult=new String(new String(buf.toByteArray()));
为什么不在这里(线程中对socket收信结束后)就直接返信?你非要把线程中的处理结果传到外面,由另外一个线程去返信,这样做目的是什么?
答:

3 C部分代码
Socket socket =SocketServer.saveSocket;
这里的scoket是全局变量的引用,如果A部分的代码
saveSocket=socket
被调用1次以上,也就是有多个客户端连接的时候,saveSocket保存的是最后一个客户端连接的socket,那么第一次客户端的socket收信处理完后,B部分的
SocketServer.saveResult=new String(new String(buf.toByteArray()));
这个结果还能在C部分里被正确返信给第一个客户端吗?
答:

4 C部分代码是在什么时候,怎么被调用的?
答:

5 C部分的代码为什么不用多线程处理?如果想等待客户端的回信,为什么不直接把B部分的接收处理
count=in.read(readBuf);
buf.write(readBuf,0,count);
之类的写在C部分里?in.read本身就会造成堵塞,直到收到数据为止,这样你C部分就没必要用
while(i <5){//
这样的循环去监视是否收到数据了,而且如果客户端回信延时的话,你这样监视的处理根本得不到正确的结果。你这样处理的本意是什么?
答:

6 从你的代码里,我没看出你用多线程来处理socket通信(即同时有多个线程对同一个socket进行收信或送信),只看到你用一个线程来收信,用主线程来送信,是这样吗?你这样分开成2个线程对socket处理的目的是什么?从你的代码来看,你的本意好像是C代码执行后,等待B代码的结果,这样,你为什么不把B和C和放在一起用一个线程处理?
答:

从你的问题和代码上看,我个人并不认为这是什么棘手的问题,只是你可能没理清楚处理的流程和线程的意义。也可能是我没明白你的意思,对于多线程的通信处理,我的第一印象是,由一个或多个线程同时对socket收信,然后把收信内容保存到queue里,然后再由一个或多个线程从queue中取出信息并处理,处理完后把结果返信。从你的问题和代码中,我并没有看到这样的迹象。

xiongzeng 2007-11-07
  • 打赏
  • 举报
回复
呵呵,谢谢qybao.
您上面的意思是指服务端先收信然后再发信吧?
其实并不是客户端先送信服务端才收信(如您所说的"收信送信");服务端也可以先发信,然后再收信。
zoeg 2007-11-07
  • 打赏
  • 举报
回复
我用new io实现了一个HTTP二级代理服务器,只用了一个线程,这样处理你的这些问题应该就比较方便了(其实你写了一大堆我根本没完全理解,不过大概是那意思了吧),你只要安心的写接收数据怎么处理,准备要发送的数据就OK了!
有兴趣可以联系我:QQ:124340767
qybao 2007-11-07
  • 打赏
  • 举报
回复
看来LZ没能领会我说的问题的意思
LZ是不是对线程和Socket通信有点概念模糊?
这样吧,我就针对你代码题几个问题,你好好看了以后再回答
1 你的服务器端是不是接收完客户端处理后程序就结束,即说收信送信就发生一次而不是多次?是这样吗?如果不是,那就说明在A部分的run方法以外有某个方法在循环调用run方法,是这样吗?如果是这样,每次调用run方法就会重新创建一个ServerSocket对象
serverSocket= new ServerSocket(8188);
这样做有什么特殊的意义吗?

2 A部分的代码
socket = serverSocket.accept();
saveSocket=socket用一个全局的变量保存这个Socket
new SocketServerThread(socket).start();//抛出一个多线程,便于可以随时接收客户端信令
LZ明白这里为什么要用多线程处理吗?
如果不用多线程处理,那么调用run方法的服务器端的线程就要自己处理客户端发过来的信息,如果这部分处理很花时间,那么该服务器端的线程就相当于堵塞在这里,所以交由一个线程去处理,该服务器端的线程就可以在这里返回(即run方法调用结束)而继续执行run以后的处理。

3 B部分是线程处理,从A部分来看,对于同一个Socket,只有一个线程在收信,对不对?这里并不存在多个线程同时接收同一个Socket的问题,那么,为什么不在收完信后就返信?你在收完信以后把返信信息保存在全局变量里,然后让服务器端的线程返信,这样做有什么特殊的目的吗?而且通过全局变量的方式还存在问题,如果线程在收信结束并把返信结果保存以后,服务器端的线程还没来得及返信,这时如果又有新的客户端连接,此时saveSocket就被改变,那么你怎么保证把返信内容正确地送给第一个客户端?




xiongzeng 2007-11-06
  • 打赏
  • 举报
回复
真的很谢谢qybao耐心的解答。
先说明一下,客户端必须先向服务端链接,这样服务端才得到一个链接(Socket)
正如你所说的:
服务器先起来,然后客户端向服务器端建立一个连接,服务器端收到连接后,用一个全局变量保存起来;
在这里保存起来有两个目的:1.保持这个链接,不需要以后重新再链接,2.可以在其它方法里(比如C方部分)利用这个保存的Socket向客户端发信。
各个部分的含义:
A部分,服务器端收到客户端的连接,并将这个链接(socket)保存起来,注意,服务器端收到连接后并不是立即给客户端发送信令,具体向客户端发信的是通过C部分实现的;然后new SocketServerThread(socket).start(),便于可以随时接收客户端的信令。
B部分,接收客户端的信令。因为要可以随时接收不同客户端的信令(业务要求),所以要用到多线程。当通过C部分向客户端发送信令后,那么只能通过这个多线程接收了。"读了1024个字节,然后就把结果保存",这是因为当通过C部分向客户端发送信令后,要通过这个多线程接收客户端信令的返回。因为Socket的通信机制是先发送,再等待返回。那当多线程收到客户端信令的返回时,怎么通知C部分(已经收到返回了)呢?所以就在多线程里用一个全局变量把这个结果保存起来,便于C部分可以通过轮询这个变量,已得知客户端已返回。这是一个瓶颈,尤其在多线程里,这正是我提问题的原因所在。
C部分,利用全局变量保存的socket向客户端发送信令;轮询这个变量,多线程(B部分)是否收到客户端信令的返回。

如果还有不清楚地方,及时告所我。先谢谢大家了。
qybao 2007-11-05
  • 打赏
  • 举报
回复
没明白LZ的意思。多线程接收?也就是说对于一个client的socket,同时在server端用多个线程接收,比如,第一个线程接收100个字节,然后第二线程继续接收100个字节,第n个线程接收最后的字节,每个线程受到信息后独自处理,多个线程同时处理完了才返信。是这个意思吗?还是说每个线程独自处理,处理结束后就返信,不用管其他线程是否处理结束?


qybao 2007-11-05
  • 打赏
  • 举报
回复
我还是有点没明白。
你的意思是,在你的B部分
SocketServer.saveResult=new String(new String(buf.toByteArray()));//保存客户端返回的结果
这个地方把客户端的结果保存,然后让多个线程来执行保存下来的信息吗?如果不是这样,为何不在此处返信?如果是这样,应该把信息保存到一个queue里,然后让多个线程从queue中取出数据进行处理

刚开始的时候,是服务器端先向客户端发送信令吗?如果是这样,也就是服务器端要向客户端建立一个socket,那么客户端本身也是个服务器,是这样吗?
能否把你的整个流程清楚地说一下,比如说服务器先起来,然后客户端向服务器端建立一个连接,服务器端收到连接后给客户端发送信令,客户端处理完后给服务器端一个处理完毕报告,服务器端收到该处理完毕报告后给客户端发送一个ok,这里面那个环节中是用多线程处理,其中A部分是某某环节的代码,B部分是某某环节的代码,C部分是某某环节的代码。这样说我才知道你到底想干嘛,光看你的三段代码和说明,我一直模棱两可。你的全局变量的本意是什么,为什么要分成这三部分处理,等等。
A部分的new SocketServerThread(socket).start();//这里并不代表你用多线程去接收客户端的信息,只是说明你在服务器端启动一个线程来接收,这样服务器端不被堵塞,可以继续处理下一个连接。
B部分的public void run(){ 中并没有循环读取数据,读了1024个字节,然后就把结果保存,你这样保存结果的目的是什么
C部分的Socket socket =SocketServer.saveSocket; 到底是客户端连接好了以后服务端发送一个ok指令,然后等待客户端处理和回应,还是B部分接收信息并处理完后,才发给客户端送ok指令并等待回应?
LZ还是好好把你的问题描述一边吧。





加载更多回复(8)

62,623

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧