c/s通信或线程通信问题
先贴代码再说
服务器端代码
import java.awt.event.*;
import java.io.*;
import java.net.*;
import java.util.*;
class Customer extends Object implements Serializable
{
String custName, custPwd;
}
public class AppServer implements Runnable
{
//建立一个ServerSocket 对象 ,注意没有初始化
ServerSocket serverSocket;
Socket client;
Thread serverThread;
public AppServer()
{
try
{//ServerSocket对象初始化
serverSocket = new ServerSocket(1001);
serverThread = new Thread(this);
serverThread.start();
}
catch (IOException e)
{
fail(e, "不能启动服务器 ");
}
System.out.println( "服务器开始运行..... ");
}
public static void fail(Exception e , String Str)
{
System.out.println(Str + " . " + e);
}
public void run()
{
try
{
while(true)
{
Socket client = serverSocket.accept();
Connection con = new Connection(client);
}
}
catch (IOException e)
{ //调用自定义的fail方法
fail(e, "没有监听 ");
}
}
public static void main(String[] args)
{
//声明一个对象
new AppServer();
}
}
class Connection extends Thread
{
Socket netClient;
ObjectInputStream fromClient;
PrintStream toClient;
Customer clientMessage;
static Vector vector = new Vector(1,1);
static int messageCount;//To count the total number of messages
//stored
private int localMsgCount;// To count local messages
public Connection(Socket client)
{
netClient = client;
try
{
// 用来获得客户端传过来的信息
fromClient = new ObjectInputStream(client.getInputStream());
// 用来向向客户端写出信息
toClient = new PrintStream(client.getOutputStream());
}
catch (IOException e)
{
try
{
netClient.close();
}
catch (IOException el)
{
System.out.println( "不能建立流 "+ el);
return ;
}
}
this.start();
}
public void run()
{
try
{
clientMessage = (Customer)fromClient.readObject();
//toClient.print(clientMessage.custName+ " Connected ");
//System.out.println(clientMessage.custName);
for(;;)
{
System.out.println( "yes ");
clientMessage = (Customer)fromClient.readObject();//(1)
System.out.println( "no ");
if(clientMessage == null)
System.out.println( "no ");
writeMessage(clientMessage);//store message
String message=readMessage();//read messages stored
toClient.print(message);
toClient.println( "接收来自 "+ clientMessage.custName + " 的数据 ");
System.out.println( "接收到了来自 "+ clientMessage.custName + " 的数据 ");
System.out.println( "用户: "+ clientMessage.custName);
System.out.println( "密码: "+ clientMessage.custPwd);
}
}
catch (IOException e)
{
}
catch (Exception el)
{
System.out.println( "读取 "+el+ " 对象时错误 ");
}
finally
{
try
{
netClient.close();
}
catch (IOException e)
{
}
}
}
synchronized void writeMessage(Customer cust)
{
vector.addElement(cust);
++messageCount;
++localMsgCount;
notifyAll();
}
//Method to retrieve message
synchronized String readMessage()
{
String str= " ";
while(localMsgCount> =messageCount)
{
try
{
wait();
}
catch(InterruptedException e)
{System.out.println( "Customer class could not be found ");}
}
for(int i=localMsgCount;i <=messageCount; i++)
{
str=str+vector.elementAt(i);
}
notifyAll();
return str;
}
}
客户端代码
import java.awt.event.*;
import java.io.*;
import java.net.*;
import javax.swing.*;
import java.awt.*;
class Customer extends Object implements java.io.Serializable
{
String custName, custPwd;
}
public class CustomerApplet extends JApplet
{
int custAge;
int flag = 0;
static JPanel po;
JLabel j1,j2,j3,j4, j5;
JButton buttonAccept;
JTextField tname,tno,tage;
JComboBox cpack;
JPasswordField pwd;
public void init()
{
po = new JPanel();
getContentPane().add(po);
j1 = new JLabel( "姓名: ");
j2 = new JLabel( "号码: ");
j3 = new JLabel( "年龄: ");
j4 = new JLabel( "package: ");
j5 = new JLabel( "密码: ");
buttonAccept = new JButton( "接受 ");
pwd = new JPasswordField(15);
tname = new JTextField(20);
tno = new JTextField(15);
tage = new JTextField(2);
String pack[] = { "Exec ", "Standard "};
cpack = new JComboBox(pack);
po.add(j1);
po.add(tname);
po.add(j5);
po.add(pwd);
pwd.setEchoChar( '* ');
po.add(j2);
po.add(tno);
po.add(j3);
po.add(tage);
po.add(j4);
po.add(cpack);
//添加 "接受 "按钮
po.add(buttonAccept);
LoginAction la = new LoginAction();
buttonAccept.addActionListener(la);
}
class LoginAction implements ActionListener
{
public void actionPerformed(ActionEvent evt)
{
Object obj= evt.getSource();
if(obj == buttonAccept)
{
Customer data= new Customer();
//data.custName = tname.getText();
//data.custPwd = new String(pwd.getPassword());
data.custName = "1 ";
data.custPwd = "1 ";
try
{
Socket toServer;
//注意:在不同的机器上面要修改成对应的IP
toServer = new Socket( "127.0.0.1 ",1001);
ObjectOutputStream streamToServer = new ObjectOutputStream(toServer.getOutputStream());
streamToServer.writeObject((Customer)data);
BufferedReader fromServer = new BufferedReader(new InputStreamReader(toServer.getInputStream()));
//String status = fromServer.readLine(); (5)
System.out.println( "000 ");
//System.out.println(status);(6)
//getAppletContext().showStatus(status);(7)
streamToServer.close();
fromServer.close();
}
catch (InvalidClassException e)
{
getAppletContext().showStatus( "Customer类无效 "+ e);
}
catch (NotSerializableException e)
{
getAppletContext().showStatus( "对象没有序列化 "+ e);
}
catch (IOException e)
{
getAppletContext().showStatus( "不能写入到服务器端 "+ e);
}
}
}
}
}
问题有两个 (a) 如果按照现在的代码运行的话,客户端会正常的把序列化后的对象传给服务器端,但是服务器端不能正常接受每个对象,也就是在服务器端会输出 yes, 但不会输出 no 。为什么?
(b) 如果将客户端代码的(5)的注释去掉,会造成客户端程序的无响应(死掉),why?
[解决办法]
(a) 服务器端不能接受对象是因为客户端每次创建一个客户连接只发送一次object,但被server端的Connection Class里的
public void run() {
try {
clientMessage = (Customer) fromClient.readObject();
这一句接受掉了。
进入for循环后第二次试图接受object
for (;;) {
System.out.println( "yes ");
clientMessage = (Customer) fromClient.readObject();// (1)
自然接受不到东西了,于是server端的该条客户连接线程阻塞于此。而由于客户端连接已关闭,客户端再次发送的object,不管是不是来自原来的同一客户端,都回创建新的客户连接线程,不会再向该阻塞线程发送object。
(b)由于(a),server不会回应客户端message,所以客户端读message那一句uncomment后就会阻塞在那里。其实即使把(a)改好(即去掉第一次读object语句),问题(b)依然存在,因为server connection线程在readMessage里用wait挂起,再不会运行到发回message的语句。wait的锁是当前connection的实例,如果有其他客户端连上来从而触发notifyAll是通知等待新connection实例锁的进程,所以老conncetion线程永不被唤醒。
一些错误使得于我不能完全读明白这个程序的需求。有点像要实现每次提交后返回已经登录人名单功能吧,又有点像实现 "群组通话 "功能,所以也不知道怎么提改进建议.