多线程March18th
4:java多线程
新建状态(start) 可运行状态(runnable) 运行状态(run) 阻塞装填(sleep) 死亡状态(stop,dead)
start—>Runnable—-> Running—>Dead | | | | Threads implements
Running—>Wait—>Blocked—>notify—->Runnable
Running—>sleep—>Runnable
线程和进程的区别? 一个进程占用实际的内存资源,线程依赖于进程,一个进程可以有多个线程 进程大,线程小 每一个程序运行都运行在一个进程内,而不是线程 一个进程可分为多个线程
售票案例 问题: 第70张票还没售完不能售第73张的票,但是实际却是销售了?
/**
* 解释:
*/
package TestMultiThreads;
/**
* @author 叶昭良
* @time 2015年3月18日下午9:09:27
* @version TestMultiThreadsTestPesonThread V1.0
* 功能:
步骤:
* 注意:
* 掌握:
思考:
* 回顾:
*/
public class TestPesonThread
{
/**
* @param args
* 原因:
* 解决:
* 功能:
* 思考:
* 步骤:
*/
public static void main(String[] args)
{
// TODO Auto-generated method stub
A aa = new A();
Thread t1 = new Thread(aa);
Thread t2 = new Thread(aa);
Thread t3 = new Thread(aa);
/* B bb = new B();
Thread t1 = new Thread(bb);
Thread t2 = new Thread(bb);
Thread t3 = new Thread(bb);*/
t1.start();
t2.start();
t3.start();
}
}
class A implements Runnable
{
private static int tickets = 0;
@Override
public void run()
{
// TODO Auto-generated method stub
while(true)
{
//synchronized("aaa")
{
if(tickets < 10)
{
System.out.println(Thread.currentThread().getName()+tickets);
tickets++;
}else
{
break;
}
}
}
}
}
class B implements Runnable
{
private static int tickets = 0;
@Override
public synchronized void run()
{
// TODO Auto-generated method stub
while(true)
{
if(tickets < 10)
{
System.out.println(Thread.currentThread().getName()+tickets);
tickets++;
}else
{
break;
}
}
}
}
解决方案1: 采用synchronized(“aaa”) 或者 synchronized(TestPesonThread.class) 都是可以的, 相当于在一扇门中 塞入了口香糖,得拿走了才能打开!一个坑的作用
@Override
public void run()
{
// TODO Auto-generated method stub
while(true)
{
//synchronized("aaa")
synchronized(TestPesonThread.class)
{
if(tickets < 10)
{
System.out.println(Thread.currentThread().getName()+tickets);
tickets++;
}else
{
break;
}
}
}
}
结果:(注意加入Thread.sleep(1000)不然都会在thread0 或者thread1 thread2中执行,因为少)
Thread-0正在售10
Thread-2正在售9
Thread-1正在售8
Thread-2正在售7
Thread-2正在售6
Thread-2正在售5
Thread-0正在售4
Thread-2正在售3
Thread-2正在售2
Thread-2正在售1
解决方案2 : 采用synchronized的run方法也是可以的! 相当于锁定函数代码块,霸占着 直到用完! 但是这个方法仅仅针对一个线程,执行完!一个对象霸占一个方法
/**
* 解释:
*/
package TestMultiThreads;
/**
* @author 叶昭良
* @time 2015年3月18日下午9:09:27
* @version TestMultiThreadsTestPesonThread V1.0
* 功能:
步骤:
* 注意:
* 掌握:
思考:
* 回顾:
*/
public class TestPesonThread
{
/**
* @param args
* 原因:
* 解决:
* 功能:
* 思考:
* 步骤:
*/
public static void main(String[] args)
{
// TODO Auto-generated method stub
A aa = new A();
Thread t1 = new Thread(aa);
Thread t2 = new Thread(aa);
Thread t3 = new Thread(aa);
/* B bb = new B();
Thread t1 = new Thread(bb);
Thread t2 = new Thread(bb);
Thread t3 = new Thread(bb);*/
t1.start();
t2.start();
t3.start();
}
}
class A implements Runnable
{
private static int tickets = 0;
@Override
public synchronized void run()
{
// TODO Auto-generated method stub
while(true)
{
//synchronized("aaa")
//synchronized(TestPesonThread.class)
{
if(tickets < 10)
{
System.out.println(Thread.currentThread().getName()+"正在售"+tickets);
tickets++;
}else
{
break;
}
}
}
}
}
class B implements Runnable
{
private static int tickets = 0;
@Override
public synchronized void run()
{
// TODO Auto-generated method stub
while(true)
{
if(tickets < 10)
{
System.out.println(Thread.currentThread().getName()+tickets);
tickets++;
}else
{
break;
}
}
}
}
结果:
Thread-0正在售10
Thread-0正在售9
Thread-0正在售8
Thread-0正在售7
Thread-0正在售6
Thread-0正在售5
Thread-0正在售4
Thread-0正在售3
Thread-0正在售2
Thread-0正在售1
由此我们可以得知: 方法的霸占解决掉乱序,但是其实仅仅只是一个线程再执行,不能达到目的; 而采用synchronized对象 比如字符串对象"aaa",不然类的class :TestPesonThread.class 放在循环快中都能够很好的解决多线程共用一个模块,必须执行一块完毕,才能执行下一块的问题!!perfect
案例2:生产和消费
目的:通过线程模拟一个在生产 一个在消费,并且当量小于三的时候,必须再生产,在量
wait方法的作用是使得当前调用wait方法所在部分(代码块)的线程(当前线程)停止执行,并释放当前获得的调用wait所在的代码块的锁,并在其他线程调用notify或者notifyAll方法时恢复到竞争锁状态(一旦获得锁就恢复执行)。 有点类似巫医的弹弹乐效果,你被弹了,就停止,当别人被弹了,你就可以运动了
注意: wait被调用的时候必须在拥有锁(即synchronized修饰的)的代码块中。 恢复执行后,从wait的下一条语句开始执行,因而wait方法总是应当在while循环中调用,以免出现恢复执行后继续执行的条件不满足却继续执行的情况。 notify方法通知调用了wait方法,但是尚未激活的一个线程进入线程调度队列(即进入锁竞争),注意不是立即执行。并且具体是哪一个线程不能保证。另外一点就是被唤醒的这个线程一定是在等待wait所释放的锁。 notifyAll方法则唤醒所有调用了wait方法,尚未激活的进程进入竞争队列。
有一个wait框,同时有一个监工notify,负责叫醒被暂停的waiter,去继续做他的被中断的工作 synstack 类定义了synchronized的push和pop方法 producer 进行push调用(可以被中断,当达到生产线程的上限时 就中断) customer 进行pop调用(可以被中断,当没有产品的时候 并在产品数为3的时候通知生产)
/**
* 解释:
*/
package TestMultiThreads;
/**
* @author 叶昭良
* @time 2015年3月18日下午9:54:48
* @version TestMultiThreadsTestProducerCustomer V1.0
* 功能:
步骤:
* 注意:
* 掌握:
思考:
* 回顾:
*/
public class TestProducerCustomer
{
/**
* @param args
* 原因:
* 解决:
* 功能:
* 思考:
* 步骤:
*/
public static void main(String[] args)
{
// TODO Auto-generated method stub
SynStack ss = new SynStack();
Producer pro = new Producer(ss);
Customer custom = new Customer(ss);
//Thread tt = new Thread();
/* pro.run();
custom.run();*/
Thread t1 = new Thread(pro);
t1.start();
Thread t2 = new Thread(custom);
t2.start();
}
}
class SynStack
{
private char[] ss = new char[6];
//private static int count = 0;
private int count = 0;
public synchronized void push(char c)
{
try
{
Thread.sleep(500);
} catch (InterruptedException e1)
{
// TODO Auto-generated catch block
e1.printStackTrace();
}
//if(count == ss.length)
while(count == ss.length)
{
try
{
System.out.println("生产已达上限,再生产容易造成供需不平衡");
this.wait();
} catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/* if(count == 3)
{
this.notify(); //如果达到3的时候通知他们进行生产
}*/
if(count > 1)
{
System.out.println(count+"大家可以来消费了");
this.notify(); //一有则通知大家进行消费
}
System.out.printf("It generates %d product.It is %c\n",count,c);
ss[count] = c;
count++;
}
//synchronized 如果不加,则报错
public synchronized char pop()
{
try
{
Thread.sleep(100);
} catch (InterruptedException e1)
{
// TODO Auto-generated catch block
e1.printStackTrace();
}
//if(count == 0)
while(count == 0)
{
//this.notify();//通知push线程进行生产
try
{
System.out.println("产品已售空,请联系生产商");
this.wait();
} catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(count == 3)
{
System.out.println("该督促生产了,并且可以继续销售");
this.notify(); //如果达到3的时候通知他们进行生产(push)
}
//this.notify();
count--;
//必须放在count--之后 才可以调用ss[count]
System.out.printf("It ate %d product.It is %c\n",count,ss[count]);
return ss[count];
}
}
class Producer implements Runnable
{
private SynStack ss = null;
public Producer(SynStack ss)
{
this.ss =ss;
}
@Override
public void run()
{
// TODO Auto-generated method stub
// ss.push('a');
/* try
{
Thread.sleep(1000);
} catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}*/
char ch ;
for(int i =0 ; i < 20 ; i++)
{
ch = (char)('a'+i);
ss.push(ch);
}
}
}
class Customer implements Runnable
{
private SynStack ss = null;
public Customer(SynStack ss)
{
this.ss = ss;
}
@Override
public void run()
{
// TODO Auto-generated method stub
// System.out.println(ss.pop());
/* try
{
Thread.sleep(1000);
} catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}*/
for(int i = 0; i < 20 ; i++)
{
System.out.println(ss.pop());
}
}
}
结果:
It generates 0 product.It is a
It generates 1 product.It is b
2大家可以来消费了
It generates 2 product.It is c
该督促生产了,并且可以继续销售
It ate 2 product.It is c
c
2大家可以来消费了
It generates 2 product.It is d
该督促生产了,并且可以继续销售
It ate 2 product.It is d
d
It ate 1 product.It is b
b
It generates 1 product.It is e
It ate 1 product.It is e
e
It generates 1 product.It is f
2大家可以来消费了
It generates 2 product.It is g
该督促生产了,并且可以继续销售
It ate 2 product.It is g
g
2大家可以来消费了
It generates 2 product.It is h
3大家可以来消费了
It generates 3 product.It is i
It ate 3 product.It is i
i
3大家可以来消费了
It generates 3 product.It is j
4大家可以来消费了
It generates 4 product.It is k
5大家可以来消费了
It generates 5 product.It is l
生产已达上限,再生产容易造成供需不平衡
It ate 5 product.It is l
l
It ate 4 product.It is k
k
It ate 3 product.It is j
j
该督促生产了,并且可以继续销售
It ate 2 product.It is h
2大家可以来消费了
It generates 2 product.It is m
h
3大家可以来消费了
It generates 3 product.It is n
It ate 3 product.It is n
n
3大家可以来消费了
It generates 3 product.It is o
It ate 3 product.It is o
o
3大家可以来消费了
It generates 3 product.It is p
4大家可以来消费了
It generates 4 product.It is q
It ate 4 product.It is q
q
4大家可以来消费了
It generates 4 product.It is r
It ate 4 product.It is r
r
4大家可以来消费了
It generates 4 product.It is s
5大家可以来消费了
It generates 5 product.It is t
It ate 5 product.It is t
t
It ate 4 product.It is s
s
It ate 3 product.It is p
p
该督促生产了,并且可以继续销售
It ate 2 product.It is m
m
It ate 1 product.It is f
f
It ate 0 product.It is a
a
注意:至于notify放置在代码的哪个地方,有待进一步的比较。
5.网络编程
一个网络程序需要考虑协议,IP,端口号。
协议: 用于网络传输的规则、准则。在不同的计算机传输层中(7层架构) 有不同的数据包, 不同 层封包的不同方法
IP: 唯一标识互联网中的计算机 端口号:qq有自己的端口 mysql的3306端口 smtp的23端口等
协议分为UDP,TCP UDP的特点:无需等待对方确认,比如写信 有可能收不到。 TCP的特点:需要等待 对方连接,比如QQ视频聊天,打电话。三重连接
UDP编程: 知识点: DatagramSocket 是一艘轮船(传递数据包) DatagramPacket 是一个轮船上的货物(获取数据包)
ByteArrayInputStream and DataInputStream 用于打包港口货物 ByteArrayOutputStream and DataOutputStream 也用于打包港口货物
案例1:简单实现一个UDP的传输过程,服务器开启,客户端发送数据(暂时未实现服务器的发送 客户端的接受,实现简单,dos对应的dis dis对应的dos) Server:
/**
* 解释:
*/
package TestNetwork;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
/**
* @author 叶昭良
* @time 2015年3月19日下午1:49:36
* @version TestNetworkTestDatagramSocket V1.0
* 功能: 测试UDP
步骤:
* 注意:
* 掌握:
思考:
* 回顾:
*/
public class TestDatagramSocket
{
/**
* @param args
* 原因:
* 解决:
* 功能:
* 思考:
* 步骤:
*/
public static void main(String[] args)
{
// TODO Auto-generated method stub
DatagramSocket ds = null;
DatagramPacket dp = null;
try
{
ds = new DatagramSocket(5566);
byte[] apple = new byte[1025];
/*for(int i =0 ; i < 20; i++)
{
apple = (byte) ((byte)'a'+i);
}*/
dp = new DatagramPacket(apple, apple.length);
while(true)
{
try
{
ds.receive(dp);
ByteArrayInputStream bais = new ByteArrayInputStream(dp.getData());
DataInputStream dis = new DataInputStream(bais);
System.out.println(dis.readLong());
} catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} catch (SocketException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Client
/**
* 解释:
*/
package TestNetwork;
import java.io.*;
import java.net.*;
/**
* @author 叶昭良
* @time 2015年3月21日上午1:39:22
* @version TestNetworkTestDatagramClient V1.0
* 功能:
步骤:
* 注意:
* 掌握:
思考:
* 回顾:
*/
public class TestDatagramClient
{
/**
* @param args
* 原因:
* 解决:
* 功能:
* 思考:
* 步骤:
*/
public static void main(String[] args)
{
// TODO Auto-generated method stub
DatagramSocket ds = null;
DatagramPacket dp = null;
try
{
ds = new DatagramSocket();
long m = 1000l;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
dos.writeLong(m);
byte[] apple = baos.toByteArray();
dp = new DatagramPacket(apple, apple.length,new
InetSocketAddress("127.0.0.1",5566));
ds.send(dp);
}catch(IOException e)
{
}
}
}
结果:
先开启Server,再运行Client
1000
TCP编程: 知识点: 1.通过ServerSocket产生一个运行的所需的端口号 2.利用Socket进行编程,Socket源自于unix操作系统
案例1: 模拟UDP的类似程序,即服务器端等待数据 进行读取 客户端发送数据
Server:
/**
* 解释:
*/
package TestNetwork;
import java.io.*;
import java.net.*;
/**
* @author 叶昭良
* @time 2015年3月19日下午2:30:08
* @version TestNetworkTestServer1 V1.0
* 功能:
步骤:
* 注意:
* 掌握:
思考:
* 回顾:
*/
public class TestServer1
{
/**
* @param args
* 原因:
* 解决:
* 功能:
* 思考:
* 步骤:
*/
public static void main(String[] args)
{
// TODO Auto-generated method stub
try
{
ServerSocket ss = new ServerSocket(5566);
while(true)
{
Socket s1 =ss.accept();
DataInputStream dis = new DataInputStream(s1.getInputStream());
System.out.println(dis.readUTF());
dis.close();
}
} catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Client:
/**
* 解释:
*/
package TestNetwork;
import java.io.*;
import java.net.*;
/**
* @author 叶昭良
* @time 2015年3月19日下午2:30:23
* @version TestNetworkTestClient1 V1.0
* 功能:
步骤:
* 注意:
* 掌握:
思考:
* 回顾:
*/
public class TestClient1
{
/**
* @param args
* 原因:
* 解决:
* 功能:
* 思考:
* 步骤:
*/
public static void main(String[] args)
{
// TODO Auto-generated method stub
Socket s1 =null;
OutputStream os = null;
DataOutputStream dos = null;
try
{
s1 = new Socket("127.0.0.1",5566);
os = s1.getOutputStream();
dos = new DataOutputStream(os);
dos.writeUTF("I am coming ,Sir");
dos.flush();
} catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}finally
{
try
{
dos.close();
os.close();
s1.close();
} catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
案例2:改进案例1 是的服务器端也能够发送 客户端也能接受数据
Server:
<font size="3">/**
* 解释:
*/
package TestNetwork;
import java.io.*;
import java.net.*;
/**
* @author 叶昭良
* @time 2015年3月19日下午2:38:37
* @version TestNetworkTestServer2 V1.0
* 功能:
步骤:
* 注意:
* 掌握:
思考:
* 回顾:
*/
public class TestServer2
{
/**
* @param args
* 原因:
* 解决:
* 功能:
* 思考:
* 步骤:
*/
public static void main(String[] args)
{
// TODO Auto-generated method stub
Socket s1 = null ;
DataOutputStream dos = null;
DataInputStream dis = null;
BufferedReader br = null;
try
{
ServerSocket ss = new ServerSocket(5566);
s1 = ss.accept();
dis = new DataInputStream(s1.getInputStream());
dos = new DataOutputStream(s1.getOutputStream());
br = new BufferedReader(new InputStreamReader(System.in));
while(true)
{
String apple = dis.readUTF();
System.out.println("客户端:"+dis.readUTF());
if(apple.equals("bye"))
{
break;
}
apple = br.readLine();
dos.writeUTF(apple);
}
} catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}finally
{
try
{
dos.close();
dis.close();
s1.close();
} catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
</font>
Client:
/**
* 解释:
*/
package TestNetwork;
import java.io.*;
import java.net.*;
/**
* @author 叶昭良
* @time 2015年3月19日下午2:38:52
* @version TestNetworkTestClient2 V1.0
* 功能:
步骤:
* 注意:
* 掌握:
思考:
* 回顾:
*/
public class TestClient2
{
/**
* @param args
* 原因:
* 解决:
* 功能:
* 思考:
* 步骤:
*/
public static void main(String[] args)
{
// TODO Auto-generated method stub
Socket s1 = null;
OutputStream os = null;
InputStream is = null;
DataOutputStream dos = null;
DataInputStream dis = null;
BufferedReader br = null;
try
{
s1 = new Socket("127.0.0.1",5566);
os = s1.getOutputStream();
is = s1.getInputStream();
dos = new DataOutputStream(os);
dis = new DataInputStream(is);
dos.writeUTF("Come to the world of net server");
dos.flush();
br = new BufferedReader(new InputStreamReader(System.in));
while(true)
{
String str = br.readLine();
dos.writeUTF(str);
if(str.equalsIgnoreCase("byebye"))
break;
str = dis.readUTF();
System.out.println("服务器 said that" + str+"\n");
if(str.equalsIgnoreCase("byebye"))
break;
}
} catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
结果:可以在字符界面下进行对话 客户端:hi hi 服务器 said thathi too,smida
出现的问题是,无法独立的切换客户端和服务器,client接受信息后,一直存在于while循环中,于是使用了线程,让他们自己对话吧!!
Server的实现: 一定要注意无论是Server还是Client的实现,try—catch之后不能有finally关闭,否则异常,socket中止
/**
* 解释:
*/
package TestNetwork;
/**
* @author 叶昭良
* @time 2015年3月21日下午12:43:24
* @version TestNetworkTestServer3 V1.0
* 功能:
步骤:
* 注意:
* 掌握:
思考:
* 回顾:
*/
import java.io.*;
import java.net.*;
public class TestServer3 //throws Exception
{
/**
* @param args
* 原因:
* 解决:
* 功能:
* 思考:
* 步骤:
*/
public static void main(String[] args)
{
// TODO Auto-generated method stub
ServerSocket ss = null;
Socket s = null;
DataOutputStream dos = null;
DataInputStream dis = null;
try
{
ss = new ServerSocket(8888);
s = ss.accept();
dos = new DataOutputStream(s.getOutputStream());
dis = new DataInputStream(s.getInputStream());
new ServerRead(dis).start();
new ServerWrite(dos).start();
} catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class ServerRead extends Thread
{
private DataInputStream dis = null;
public ServerRead(DataInputStream dis)
{
this.dis = dis;
}
public void run()
{
while(true)
{
try{
String str = dis.readUTF();
System.out.println("客户端说:"+str);
if(str.equalsIgnoreCase("byebye"))
break;
}catch(Exception e)
{
e.printStackTrace();
}
}
}
}
class ServerWrite extends Thread
{
private DataOutputStream dos = null;
public ServerWrite(DataOutputStream dos)
{
this.dos = dos;
}
public void run()
{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
while(true)
{
try{
String str = br.readLine();
dos.writeUTF(str);
if(str.equalsIgnoreCase("byebye"))
break;
}catch(Exception e)
{
e.printStackTrace();
}//加入finally 关闭 dos.close()则异常。。。于是删掉Client & Server的四个finally即可!!!问题解决 原因未知!!!
}
}
}
Client的实现:
/**
* 解释:
*/
package TestNetwork;
import java.io.*;
import java.net.*;
/**
* @author 叶昭良
* @time 2015年3月21日下午12:47:03
* @version TestNetworkTestClient3 V1.0
* 功能:
步骤:
* 注意:
* 掌握:
思考:
* 回顾:
*/
public class TestClient3
{
/**
* @param args
* 原因:
* 解决:
* 功能:
* 思考:
* 步骤:
*/
public static void main(String[] args) throws Exception
{
// TODO Auto-generated method stub
Socket s = new Socket("127.0.0.1",8888);
DataOutputStream dos = new DataOutputStream(s.getOutputStream());
DataInputStream dis = new DataInputStream(s.getInputStream());
new ClientRead(dis).start();
new ClientWrite(dos).start();
}
}
class ClientRead extends Thread
{
private DataInputStream dis = null;
public ClientRead(DataInputStream dis)
{
this.dis = dis;
}
public void run()
{
while(true)
{
try{
String str = dis.readUTF();
System.out.println("服务器说:"+str);
if(str.equalsIgnoreCase("byebye"))
break;
}catch(Exception e)
{
e.printStackTrace();
}
}
}
}
class ClientWrite extends Thread
{
private DataOutputStream dos = null;
public ClientWrite(DataOutputStream dos)
{
this.dos = dos;
}
public void run()
{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
while(true)
{
try{
String str = br.readLine();
dos.writeUTF(str);
if(str.equalsIgnoreCase("byebye"))
break;
}catch(Exception e)
{
e.printStackTrace();
}
}
}
}
结果:
客户端的控制台:
--------------------------------------------------------
客户端说:fs
fsdf
客户端说:heihei
吃了吗
客户端说:我吃了啊,下午去哪里玩?
----------------------------------------------------------
服务器端的控制台:
----------------------------------------------------------
fs
服务器说:fsdf
heihei
服务器说:吃了吗
我吃了啊,下午去哪里玩?
服务器说:不然咱们去闯红灯吧
----------------------------------------------------------
问题:这里面的线程切换,没有生产和消费的明显,还需要进一步理解线程的切换,为什么就能够从Server的读线程,然后写入信息给client就可以切换到Client的读线程? 仅仅加入了extends thread?
案例4: 在原先的基础上 加入了窗体,并进一步抽象到TCPClient & TCPServer两个类下,通过TCPClient.launch TCPServer.launch进行启动 美化效果操作:
Font font = new Font("Dialog", Font.PLAIN, 12); //一下是改变默认的组建上显示的字体,这样更加美观一些
UIManager.put("MenuBar.font", font);
UIManager.put("MenuItem.font", font);
UIManager.put("Menu.font", font);
UIManager.put("PopupMenu.font", font);
UIManager.put("ToolBar.font", font);
UIManager.put("ToolTip.font", font);
UIManager.put("TabbedPane.font", font);
UIManager.put("Label.font", font);
UIManager.put("List.font", font);
UIManager.put("ComboBox.font", font);
UIManager.put("Button.font", font);
UIManager.put("Table.font", font);
UIManager.put("TableHeader.font", font);
UIManager.put("Tree.font", font);
UIManager.put("TextField.font", font);
UIManager.put("TextArea.font", font);
UIManager.put("TitledBorder.font", font);
UIManager.put("OptionPane.font", font);
UIManager.put("RadioButton.font", font);
UIManager.put("CheckBox.font", font);
UIManager.put("ToggleButton.font", font);
UIManager.put("Dialog.font", font);
UIManager.put("Panel.font", font);
Server的实现:
/**
* 解释:
*/
package TestNetwork;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
//some error swing : controler
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
/**
* @author 叶昭良
* @time 2015年3月19日下午3:20:34
* @version TestNetworkTestServer4 V1.0
* 功能:
步骤:
* 注意:
* 掌握:
思考:
* 回顾:
*/
public class TestServer4
{
/**
* @param args
* 原因:
* 解决:
* 功能:
* 思考:
* 步骤:
*/
public static void main(String[] args)
{
// TODO Auto-generated method stub
Font font = new Font("Dialog", Font.PLAIN, 12); //一下是改变默认的组建上显示的字体,这样更加美观一些
UIManager.put("MenuBar.font", font);
UIManager.put("MenuItem.font", font);
UIManager.put("Menu.font", font);
UIManager.put("PopupMenu.font", font);
UIManager.put("ToolBar.font", font);
UIManager.put("ToolTip.font", font);
UIManager.put("TabbedPane.font", font);
UIManager.put("Label.font", font);
UIManager.put("List.font", font);
UIManager.put("ComboBox.font", font);
UIManager.put("Button.font", font);
UIManager.put("Table.font", font);
UIManager.put("TableHeader.font", font);
UIManager.put("Tree.font", font);
UIManager.put("TextField.font", font);
UIManager.put("TextArea.font", font);
UIManager.put("TitledBorder.font", font);
UIManager.put("OptionPane.font", font);
UIManager.put("RadioButton.font", font);
UIManager.put("CheckBox.font", font);
UIManager.put("ToggleButton.font", font);
UIManager.put("Dialog.font", font);
UIManager.put("Panel.font", font);
new TCPServer().launch();
}
}
class TCPServer
{
// class variables
// class variables
// connect
private ServerSocket ss = null;
private Socket s = null;
// data flow
private DataOutputStream dos = null;
private DataInputStream dis = null;
// UI
private Frame f = null;
private TextArea ta = null;
private TextField tf = null;
private Button bn = null;
public void launch()
{
createUI();
connect();
new ServerRead().start();
new ServerWrite().start();
}
// construct member
/*
public TCPServer()
{
}
*/
public void createUI()
{
Frame f = new Frame("服务器");
ta = new TextArea();
tf = new TextField();
Panel p = new Panel(new BorderLayout());
bn = new Button("发送");
p.add(tf,BorderLayout.CENTER);
p.add(bn,BorderLayout.EAST);
f.add(ta,BorderLayout.CENTER);
f.add(p,BorderLayout.SOUTH);
f.setSize(400,200);
f.setVisible(true);
//f.setVisable(true);
f.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
}
public void connect()
{
try{
ss = new ServerSocket(5599);
s = ss.accept();
dis = new DataInputStream(s.getInputStream());
dos = new DataOutputStream(s.getOutputStream());
}catch(Exception e)
{
System.exit(0);
}
}
public void close()
{
try{
dis.close();
dos.close();
ss.close();
s.close();
}catch(Exception e)
{
System.exit(0);
}
}
class ServerRead extends Thread
{
public void run()
{
while(true)
{
try{
String str = dis.readUTF();
// System.out.println("对方说:"+str);
ta.append("客户端说:"+str+"\n");
if(str.equalsIgnoreCase("再见"))
{
close();
System.exit(0);
}
}catch(Exception e)
{
JOptionPane.showMessageDialog(f,"客户端已离开");
return;
// e.printStackTrace();
}
}
}
}
class ServerWrite extends Thread
{
public void run()
{
tf.addActionListener(new TCPServerListener());
bn.addActionListener(new TCPServerListener());
}
}
class TCPServerListener implements ActionListener
{
@Override
public void actionPerformed(ActionEvent e)
{
try{
String str = tf.getText();
tf.setText("");
ta.append("服务器说:"+str+"\n");
dos.writeUTF(str);
if(str.equalsIgnoreCase("再见"))
{
close();
System.exit(0);
}
}catch(Exception e1)
{
// e.printStackTrace();
System.exit(0);
}
}
}
}
Client的实现:
/**
* 解释:
*/
package TestNetwork;
import java.net.*;
import java.io.*;
// some error swing : controler
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
/**
* @author 叶昭良
* @time 2015年3月19日下午3:22:27
* @version TestNetworkTestClient4 V1.0
* 功能:
步骤:
* 注意:
* 掌握:
思考:
* 回顾:
*/
public class TestClient4
{
/**
* @param args
* 原因:
* 解决:
* 功能:
* 思考:
* 步骤:
*/
public static void main(String[] args)
{
// TODO Auto-generated method stub
Font font = new Font("Dialog", Font.PLAIN, 12); //一下是改变默认的组建上显示的字体,这样更加美观一些
UIManager.put("MenuBar.font", font);
UIManager.put("MenuItem.font", font);
UIManager.put("Menu.font", font);
UIManager.put("PopupMenu.font", font);
UIManager.put("ToolBar.font", font);
UIManager.put("ToolTip.font", font);
UIManager.put("TabbedPane.font", font);
UIManager.put("Label.font", font);
UIManager.put("List.font", font);
UIManager.put("ComboBox.font", font);
UIManager.put("Button.font", font);
UIManager.put("Table.font", font);
UIManager.put("TableHeader.font", font);
UIManager.put("Tree.font", font);
UIManager.put("TextField.font", font);
UIManager.put("TextArea.font", font);
UIManager.put("TitledBorder.font", font);
UIManager.put("OptionPane.font", font);
UIManager.put("RadioButton.font", font);
UIManager.put("CheckBox.font", font);
UIManager.put("ToggleButton.font", font);
UIManager.put("Dialog.font", font);
UIManager.put("Panel.font", font);
new TCPClient().launch();
}
}
class TCPClient
{
// class variables
// class variables
// connect
private Socket s = null;
// data flow
private DataOutputStream dos = null;
private DataInputStream dis = null;
// UI
private Frame f = null;
private TextArea ta = null;
private TextField tf = null;
private Button bn = null;
public void launch()
{
createUI();
connect();
new ClientRead().start();
new ClientWrite().start();
}
// construct member
/*
public TCPClient()
{
}
*/
public void createUI()
{
Frame f = new Frame("客户端");
ta = new TextArea();
tf = new TextField();
Panel p = new Panel(new BorderLayout());
bn = new Button("发送");
p.add(tf,BorderLayout.CENTER);
p.add(bn,BorderLayout.EAST);
f.add(ta,BorderLayout.CENTER);
f.add(p,BorderLayout.SOUTH);
f.setSize(400,200);
f.setVisible(true);
//f.setVisable(true);
f.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
}
public void connect()
{
try{
s = new Socket("127.0.0.1",5599);
dis = new DataInputStream(s.getInputStream());
dos = new DataOutputStream(s.getOutputStream());
}catch(Exception e)
{
System.exit(0);
}
}
public void close()
{
try{
dis.close();
dos.close();
s.close();
}catch(Exception e)
{
System.exit(0);
}
}
class ClientRead extends Thread
{
public void run()
{
while(true)
{
try{
String str = dis.readUTF();
// System.out.println("对方说:"+str);
ta.append("服务器说:"+str+"\n");
if(str.equalsIgnoreCase("再见"))
{
close();
System.exit(0);
}
}catch(Exception e)
{
JOptionPane.showMessageDialog(f,"客户端已离开");
return;
// e.printStackTrace();
}
}
}
}
class ClientWrite extends Thread
{
public void run()
{
tf.addActionListener(new TCPClientListener());
bn.addActionListener(new TCPClientListener());
}
}
class TCPClientListener implements ActionListener
{
@Override
public void actionPerformed(ActionEvent e)
{
try{
String str = tf.getText();
tf.setText("");
ta.append("客户端说:"+str+"\n");
dos.writeUTF(str);
if(str.equalsIgnoreCase("再见"))
{
close();
System.exit(0);
}
}catch(Exception e1)
{
// e.printStackTrace();
System.exit(0);
}
}
}
}
##重写TCPServer1 & TCPClient1 并加入了 String ip = s.getInetAddress().getHostAddress(); 并且调整了编写风格,把所有类使用的变量都放在类中,并把socket创建和连接 接受和发送都放在构造函数中。类似于OOGTK的编写风格http://www.rupeng.com/forum/forum.php?mod=viewthread&tid=44377。
/**
* 解释:
*/
package TestNetwork;
/**
* @author 叶昭良
* @time 2015年3月22日下午3:49:11
* @version TestNetworkRewriteTestServer1 V1.0
* 功能:
步骤:
1: Server绑定端口
2:接受
3:循环接收
* 注意: socket的主要作用就是 getOutputStream 和getInputStream来确定
* 表明socket交流的方向
* 掌握:
思考:
* 回顾:
*/
import java.io.*;
import java.net.*;
public class RewriteTestServer1
{
/**
* @param args
* 原因:
* 解决:
* 功能:
* 思考:
* 步骤:
*/
ServerSocket ss = null;
Socket s = null;
DataInputStream dis = null;
public RewriteTestServer1()
{
try
{
ss = new ServerSocket(5566);
s = ss.accept();
String ip = s.getInetAddress().getHostAddress();
dis = new DataInputStream(s.getInputStream());
System.out.println("客户端"+ip+" said: "+dis.readUTF());
} catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}finally
{
try
{
dis.close();
s.close();
}catch(IOException e)
{
}
}
}
public static void main(String[] args)
{
// TODO Auto-generated method stub
new RewriteTestServer1();
}
}
TCPClient1:
/**
* 解释:
*/
package TestNetwork;
/**
* @author 叶昭良
* @time 2015年3月22日下午3:54:39
* @version TestNetworkRewriteTestClient1 V1.0
* 功能:
步骤:
1: Socket的建立
2: 发送数据
3:。。
* 注意:
* 掌握:
思考:
* 回顾:
*/
import java.io.*;
import java.net.*;
public class RewriteTestClient1
{
/**
* @param args
* 原因:
* 解决:
* 功能:
* 思考:
* 步骤:
*/
Socket s = null;
DataOutputStream dos = null;
public RewriteTestClient1()
{
try
{
s = new Socket("127.0.0.1",5566);
dos = new DataOutputStream(s.getOutputStream());
dos.writeUTF("Hello");
dos.flush();
} catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void main(String[] args)
{
// TODO Auto-generated method stub
new RewriteTestClient1();
}
}
重写了TestServer2 功能: 在第一版本服务器的基础上 修正了客户端的只发送 不接受
-
以及服务器端的只接受不发送
-
并用BufferedReader获取System.in的内容输送到客户端
当客户端 也就是ss.accept之后,服务器来了一声“欢迎,ohni sang 光临” 紧接着客户端做了动作,接收到了服务器来的欢迎词,然后开始写入 一些话提交给服务器 反复执行这个过程
/**
* 解释:
*/
package TestNetwork;
/**
* @author 叶昭良
* @time 2015年3月22日下午4:08:24
* @version TestNetworkRewriteTestServer2 V1.0
* 功能:
* 在第一版本服务器的基础上 修正了客户端的只发送 不接受
* 以及服务器端的只接受不发送
* 并用BufferedReader获取System.in的内容输送到客户端
步骤:
* 注意:
* 掌握:
思考:
* 回顾: 回顾了第一版本的ServerSocket ss.accept() 以及DataInputStream(s.getInputStream());
*/
import java.io.*;
import java.net.*;
public class RewriteTestServer2
{
/**
* @param args
* 原因:
* 解决:
* 功能:
* 思考:
* 步骤:
*/
ServerSocket ss = null;
Socket s = null;
DataInputStream dis = null;
DataOutputStream dos = null;
BufferedReader br = null;
String wordsFromClient =null;
String wordsFromServer =null;
public RewriteTestServer2()
{
createConnection();
String ip = s.getInetAddress().getHostAddress();
while(true)
{
try
{
wordsFromClient = dis.readUTF();
System.out.println("客户端IP:"+ip+" said:"+wordsFromClient);
if(wordsFromClient.equalsIgnoreCase("bye*"))
{
break;
}
System.out.println("服务器,请您输入你想对客户端说什么:");
wordsFromServer = br.readLine();
dos.writeUTF(wordsFromServer);
} catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}finally
{
try
{
dos.close();
dis.close();
s.close();
} catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
/**
* 功能: 创建一个读的连接,用于读取客户端的socket数据
* 创建一个写的连接,通过BufferedReader进行缓冲读取System.in的数据
* 并通过DataOutputStream发送出去
* 思考:
* 步骤:
*/
private void createConnection()
{
try
{
ss = new ServerSocket(5566);
s = ss.accept();
dis = new DataInputStream(s.getInputStream());
dos = new DataOutputStream(s.getOutputStream());
//增加了下面两句
dos.writeUTF("您好!我是服务器想要聊些什么:");
dos.flush();
br = new BufferedReader(new InputStreamReader(System.in));
}catch(IOException e)
{
System.out.println("创建连接问题");
}
}
public static void main(String[] args)
{
// TODO Auto-generated method stub
new RewriteTestServer2();
}
}
RewriteTestClient2:
/**
* 解释:
*/
package TestNetwork;
import java.io.*;
import java.net.*;
/**
* @author 叶昭良
* @time 2015年3月19日下午2:38:52
* @version TestNetworkTestClient2 V1.0
* 功能:
步骤:
* 注意:
* 掌握:
思考:
* 回顾:
*/
public class TestClient2
{
/**
* @param args
* 原因:
* 解决:
* 功能:
* 思考:
* 步骤:
*/
public static void main(String[] args)
{
// TODO Auto-generated method stub
Socket s1 = null;
OutputStream os = null;
InputStream is = null;
DataOutputStream dos = null;
DataInputStream dis = null;
BufferedReader br = null;
try
{
s1 = new Socket("127.0.0.1",5566);
os = s1.getOutputStream();
is = s1.getInputStream();
dos = new DataOutputStream(os);
dis = new DataInputStream(is);
dos.writeUTF("Come to the world of net server");
dos.flush();
br = new BufferedReader(new InputStreamReader(System.in));
while(true)
{
String str = br.readLine();
dos.writeUTF(str);
if(str.equalsIgnoreCase("byebye"))
break;
str = dis.readUTF();
System.out.println("服务器 said that" + str+"\n");
if(str.equalsIgnoreCase("byebye"))
break;
}
} catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
我遇到了一个问题:
java.net.SocketException: socket closed
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.read(SocketInputStream.java:150)
at java.net.SocketInputStream.read(SocketInputStream.java:121)
at java.net.SocketInputStream.read(SocketInputStream.java:203)
at java.io.DataInputStream.readUnsignedShort(DataInputStream.java:337)
at java.io.DataInputStream.readUTF(DataInputStream.java:589)
at java.io.DataInputStream.readUTF(DataInputStream.java:564)
at TestNetwork.RewriteTestClient2.<init>(RewriteTestClient2.java:52)
at TestNetwork.RewriteTestClient2.main(RewriteTestClient2.java:100)
于是我删掉了两个finally解决了问题,但是我不明白为什么?
重写了RewriteTestServer3 & RewriteTestClient3 : 出现的问题: 在客户端连接上后,客户端发送数据可以到服务器端,接着切换到服务器端,但是服务器的输入出现了问题,无论你输入一个字还是n个字都无法切换回客户端了 主要不同点:
-
1: 把读和写的过程 从while中脱离出来 变成线程的形式(采用了implements形式出现而不是 extends threads)
-
即读线程和写线程
-
2:把BufferedReader放入到写出线程中 ,因为只有在那边读取控制台的输入字符 3: 把线程的开启部分放在了构造函数中 而不是 main函数中。 步骤: 测试了: 把implements Runnable 该回到extends threads结果一样
把线程的定义和开启放在main函数,并让dos dis变为static变量,也不能解决问题,依旧是卡在服务器中,无法切回到客户端
一个有趣的问题:(是不是跟synchronized有关系,没关系,加进去也没什么效果)
class ServerReWrite21 extends Thread
{
private DataOutputStream dos = null;
private BufferedReader br = null;
private String wordsFromServer = null;
public ServerReWrite21(DataOutputStream dos)
{
this.dos = dos;
}
@Override
public void run()
{
//System.out.println("dos="+dos); 如果在ServerReWrite21中加入了这句话,则运行客户端之后 直接跳到这边 为什么???????
有问题的RewriteTestServer3
/**
* 解释:
*/
package TestNetwork;
/**
* @author 叶昭良
* @time 2015年3月22日下午4:49:25
* @version TestNetworkRewriteTestServer3 V1.0
* 功能:
* 1: 把读和写的过程 从while中脱离出来 变成线程的形式
* 即读线程和写线程
* 2:把BufferedReader放入到写出线程中 ,因为只有在那边读取控制台的输入字符
步骤:
* 注意:
* 掌握:
思考:
* 回顾:
*/
import java.io.*;
import java.net.*;
public class RewriteTestServer31
{
/**
* @param args
* 原因:
* 解决:
* 功能:
* 思考:
* 步骤:
*/
ServerSocket ss = null;
Socket s = null;
DataInputStream dis = null;
DataOutputStream dos = null;
public RewriteTestServer31()
{
createConnection();
ServerReRead21 sr= new ServerReRead21(dis);
ServerReWrite21 sw = new ServerReWrite21(dos);
sr.start();
sw.start();
}
/**
* 功能: 创建一个读的连接,用于读取客户端的socket数据
* 创建一个写的连接,通过BufferedReader进行缓冲读取System.in的数据
* 并通过DataOutputStream发送出去
* 思考:
* 步骤:
*/
private void createConnection()
{
try
{
ss = new ServerSocket(5566);
s = ss.accept();
dis = new DataInputStream(s.getInputStream());
dos = new DataOutputStream(s.getOutputStream());
//增加了下面两句
dos.writeUTF("您好!我是服务器想要聊些什么:");
dos.flush();
}catch(IOException e)
{
System.out.println("创建连接问题");
}
}
public static void main(String[] args)
{
// TODO Auto-generated method stub
new RewriteTestServer31();
}
}
class ServerReRead21 extends Thread
{
private DataInputStream dis = null;
private String wordsFromClient = null;
public ServerReRead21(DataInputStream dis)
{
this.dis = dis;
}
@Override
public void run()
{
// TODO Auto-generated method stub
try
{
wordsFromClient = dis.readUTF();
System.out.println("客户端对你说:"+wordsFromClient);
} catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class ServerReWrite21 extends Thread
{
private DataOutputStream dos = null;
private BufferedReader br = null;
private String wordsFromServer = null;
public ServerReWrite21(DataOutputStream dos)
{
this.dos = dos;
}
@Override
public void run()
{
//System.out.println("dos="+dos);
// TODO Auto-generated method stub
br = new BufferedReader(new InputStreamReader(System.in));
while(true)
{
try
{
wordsFromServer = br.readLine();
dos.writeUTF(wordsFromServer);
//要不要break????? if(bye){break}
}catch(IOException e)
{
}
}
}
}
有问题的RewriteTestClient31:
/**
* 解释:
*/
package TestNetwork;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @author 叶昭良
* @time 2015年3月22日下午5:12:56
* @version TestNetworkRewriteTestClient3 V1.0
* 功能:
步骤:
* 注意:
* 掌握:
思考:
* 回顾:
*/
public class RewriteTestClient31
{
Socket s = null;
DataInputStream dis = null;
DataOutputStream dos = null;
public RewriteTestClient31()
{
createConnection();
ClientReRead31 sr = new ClientReRead31(dis);
ClientReWrite31 sw = new ClientReWrite31(dos);
sr.start();
sw.start();
}
/**
* 功能: 创建一个读的连接,用于读取客户端的socket数据
* 创建一个写的连接,通过BufferedReader进行缓冲读取System.in的数据
* 并通过DataOutputStream发送出去
* 思考:
* 步骤:
*/
private void createConnection()
{
try
{
s= new Socket("127.0.0.1",5566);
dis = new DataInputStream(s.getInputStream());
dos = new DataOutputStream(s.getOutputStream());
//增加了下面两句
}catch(IOException e)
{
System.out.println("创建连接问题");
}
}
public static void main(String[] args)
{
// TODO Auto-generated method stub
new RewriteTestClient31();
}
}
//主要更换地方 wordsFromServer 和服务器的不同地方
class ClientReRead31 extends Thread
{
private DataInputStream dis = null;
private String wordsFromServer = null;
public ClientReRead31(DataInputStream dis)
{
this.dis = dis;
}
@Override
public void run()
{
// TODO Auto-generated method stub
try
{
wordsFromServer = dis.readUTF();
System.out.println("服务器端对你说:"+wordsFromServer);
} catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
//主要更换地方 wordsFromClient 和服务器的不同地方
class ClientReWrite31 extends Thread
{
private DataOutputStream dos = null;
private BufferedReader br = null;
private String wordsFromClient= null;
public ClientReWrite31(DataOutputStream dos)
{
this.dos = dos;
}
@Override
public void run()
{
// TODO Auto-generated method stub
br = new BufferedReader(new InputStreamReader(System.in));
while(true)
{
try
{
wordsFromClient = br.readLine();
dos.writeUTF(wordsFromClient);
//要不要break????? if(bye){break}
}catch(IOException e)
{
}
}
}
}
如果我把所有的变量定义在main当中则是没有问题的(在implements Runnable)
没问题的版本 RewriteTestServer3extends
/**
* 解释:
*/
package TestNetwork;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @author 叶昭良
* @time 2015年3月22日下午5:57:16
* @version TestNetworkRewriteTestServer3extends V1.0
* 功能:
步骤:
* 注意:
* 掌握:
思考:
* 回顾:
*/
public class RewriteTestServer3extends
{
/**
* @param args
* 原因:
* 解决:
* 功能:
* 思考:
* 步骤:
*/
public static void main(String[] args)
{
// TODO Auto-generated method stub
// TODO Auto-generated method stub
ServerSocket ss = null;
Socket s = null;
DataOutputStream dos = null;
DataInputStream dis = null;
try
{
ss = new ServerSocket(8888);
s = ss.accept();
dos = new DataOutputStream(s.getOutputStream());
dis = new DataInputStream(s.getInputStream());
new Thread(new ServerReadImp(dis)).start();
new Thread(new ServerWriteImp(dos)).start();
} catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class ServerReadImp implements Runnable
{
private DataInputStream dis = null;
public ServerReadImp(DataInputStream dis)
{
this.dis = dis;
}
public void run()
{
while(true)
{
try{
String str = dis.readUTF();
System.out.println("客户端说:"+str);
if(str.equalsIgnoreCase("byebye"))
break;
}catch(Exception e)
{
e.printStackTrace();
}
}
}
}
class ServerWriteImp implements Runnable
{
private DataOutputStream dos = null;
public ServerWriteImp(DataOutputStream dos)
{
this.dos = dos;
}
public void run()
{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
while(true)
{
try{
String str = br.readLine();
dos.writeUTF(str);
if(str.equalsIgnoreCase("byebye"))
break;
}catch(Exception e)
{
e.printStackTrace();
}
}
}
}
问题较少的RewriteTestClient3extends
/**
* 解释:
*/
package TestNetwork;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.net.Socket;
/**
* @author 叶昭良
* @time 2015年3月22日下午5:57:44
* @version TestNetworkRewriteTestClient3extends V1.0
* 功能:
步骤:
* 注意:
* 掌握:
思考:
* 回顾:
*/
public class RewriteTestClient3extends
{
/**
* @param args
* 原因:
* 解决:
* 功能:
* 思考:
* 步骤:
*/
public static void main(String[] args) throws Exception
{
// TODO Auto-generated method stub
Socket s = new Socket("127.0.0.1",8888);
DataOutputStream dos = new DataOutputStream(s.getOutputStream());
DataInputStream dis = new DataInputStream(s.getInputStream());
new Thread(new ClientReadImp(dis)).start();
new Thread(new ClientWriteImp(dos)).start();
}
}
class ClientReadImp implements Runnable
{
private DataInputStream dis = null;
public ClientReadImp(DataInputStream dis)
{
this.dis = dis;
}
public void run()
{
while(true)
{
try{
String str = dis.readUTF();
System.out.println("服务器说:"+str);
if(str.equalsIgnoreCase("byebye"))
break;
}catch(Exception e)
{
e.printStackTrace();
}
}
}
}
class ClientWriteImp implements Runnable
{
private DataOutputStream dos = null;
public ClientWriteImp(DataOutputStream dos)
{
this.dos = dos;
}
public void run()
{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
while(true)
{
try{
String str = br.readLine();
dos.writeUTF(str);
if(str.equalsIgnoreCase("byebye"))
break;
}catch(Exception e)
{
e.printStackTrace();
}
}
}
}
可以看到的确是和变量放在哪里有着很大的关系,但是暂时不知道怎么解释? 还有网络编程,当服务器发送过后,是如何切换控制权的?(服务器的run处于while循环中,如何切换,有notify?(在生产和消费当中有wait and notify进行通知)
另外前两个版本(RewriteTCPServer1 RewriteTCPServer2)我进行重新改写则是没有问题,第三个版本(RewriteTCPServer3)加入了线程则是有问题, 所以程序线程+程序变量肯定有一个存在问题,具体不清楚
通过学习第十五节的网络编程,了解到while(true){ss.accept() ; new线程的作用} 之前TCPServer3和RewriteServer3并没有多线程的效果,得在 服务器当中添加如下代码:增加一个while(true) 让服务器一直在等待连接(不然再ctrl+F11 并没有新的客户端连接服务器)
private void createConnection()
{
try
{
ss = new ServerSocket(5566);
while(true)
{
s = ss.accept();
ServerReRead2 sr = new ServerReRead2(dis);
ServerReWrite2 sw = new ServerReWrite2(dos);
Thread t1 = new Thread(sr);
Thread t2 = new Thread(sw);
t1.start();
t2.start();
dis = new DataInputStream(s.getInputStream());
dos = new DataOutputStream(s.getOutputStream());
//增加了下面两句
dos.writeUTF("您好!我是服务器想要聊些什么:");
dos.flush();
}
}catch(IOException e)
{
System.out.println("创建连接问题");
}
}
有了这个意识之后,我发现之前的所谓的多线程的UI版本的TCPServer4 & TCPClient4也是lier,根本没有实现。 具体是: 一个服务器端对应一个客户端,虽然你写着extends thread,也start了,但是在ss.accept 那边根本就没有循环的接收,暂时进行修改未通过,这是另一个问题。
有进步了。改进了TCPServer4 1:客户端可以新建多个(但是这个多个还是有问题)—>具体分析socket的while循环放的有问题(有待进一步测试)
问题1:服务器无法发送出去数据 问题2:客户端不切换 还好可以,一切换之后发送一条就卡死了
有问题的 RewriteServer4:
/**
* 解释:
*/
package TestNetwork;
/**
* @author 叶昭良
* @time 2015年3月22日下午10:08:18
* @version TestNetworkRewriteTestServer4 V1.0
* 功能:
步骤:
* 注意:
* 掌握:
思考:
* 回顾:
*/
import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Frame;
import java.awt.Panel;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.*;
import java.net.*;
import javax.swing.JOptionPane;
public class RewriteTestServer4
{
/**
* @param args
* 原因:
* 解决:
* 功能:
* 思考:
* 步骤:
*/
public static void main(String[] args)
{
// TODO Auto-generated method stub
new TCPServer1().launch();
}
}
class TCPServer1
{
// class variables
// class variables
// connect
private ServerSocket ss = null;
private Socket s = null;
// data flow
private DataOutputStream dos = null;
private DataInputStream dis = null;
// UI
private Frame f = null;
private TextArea ta = null;
private TextField tf = null;
private Button bn = null;
public void launch()
{
createUI();
connect();
}
// construct member
/*
public TCPServer()
{
}
*/
public void createUI()
{
Frame f = new Frame("服务器");
ta = new TextArea();
tf = new TextField();
Panel p = new Panel(new BorderLayout());
bn = new Button("发送");
p.add(tf,BorderLayout.CENTER);
p.add(bn,BorderLayout.EAST);
f.add(ta,BorderLayout.CENTER);
f.add(p,BorderLayout.SOUTH);
f.setSize(400,200);
f.setVisible(true);
//f.setVisable(true);
/* tf.addActionListener(new TCPServerListener());
bn.addActionListener(new TCPServerListener());*/
f.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
}
public void connect()
{
try
{
ss = new ServerSocket(5599);
while(true)
{
//阻塞这边 等待客户端的连接
s = ss.accept();
dis = new DataInputStream(s.getInputStream());
dos = new DataOutputStream(s.getOutputStream());
dos.writeUTF("欢迎你的到来"+s.getInetAddress().getHostAddress());
new UserReadThread().start();
new UserWriteThread().start();
System.out.println(Thread.currentThread().getName()+"已开启");
}
}catch(Exception e)
{
System.out.println("服务器端异常");
// System.exit(0);
}
}
public void close()
{
try{
ss.close();
s.close();
}catch(Exception e)
{
System.exit(0);
}
}
class UserReadThread extends Thread
{
@Override
public void run()
{
while(true)
{
try{
String str = dis.readUTF();
// System.out.println("对方说:"+str);
ta.append("客户端说:"+str+"\n");
if(str.equalsIgnoreCase("再见"))
{
close();
System.exit(0);
}
}catch(Exception e)
{
JOptionPane.showMessageDialog(f,"客户端已离开");
return;
// e.printStackTrace();
}
}
}
}
class UserWriteThread extends Thread
{
@Override
public void run()
{
tf.addActionListener(new TCPServerListener());
bn.addActionListener(new TCPServerListener());
}
}
class TCPServerListener implements ActionListener
{
@Override
public void actionPerformed(ActionEvent e)
{
try{
String str = tf.getText();
if(!str.isEmpty())
{
tf.setText("");
ta.append("服务器说:"+str+"\n");
dos.writeUTF(str);
if(str.equalsIgnoreCase("再见"))
{
close();
System.exit(0);
}
}
}catch(Exception e1)
{
// e.printStackTrace();
System.exit(0);
}
}
}
}
RewriteTestClient4: /**
- 解释: */ package TestNetwork;
import java.awt.BorderLayout; import java.awt.Button; import java.awt.Frame; import java.awt.Panel; import java.awt.TextArea; import java.awt.TextField; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.DataInputStream; import java.io.DataOutputStream; import java.net.Socket;
import javax.swing.JOptionPane;
import TestNetwork.TCPClient.TCPClientListener;
/**
-
@author 叶昭良
-
@time 2015年3月22日下午10:08:37
-
@version TestNetworkRewriteTestClient4 V1.0
-
功能: 步骤:
-
注意:
-
掌握: 思考:
-
回顾: */ public class RewriteTestClient4 {
/** * @param args * 原因: * 解决: * 功能: * 思考: * 步骤: */ public static void main(String[] args) { // TODO Auto-generated method stub new TCPClient1().launch(); }
}
class TCPClient1 { // class variables // class variables // connect private Socket s = null; // data flow private DataOutputStream dos = null; private DataInputStream dis = null; // UI private Frame f = null; private TextArea ta = null; private TextField tf = null; private Button bn = null;
public void launch()
{
createUI();
connect();
// new ClientRead().start();
// new ClientWrite().start();
}
// construct member
/*
public TCPClient()
{
}
*/
public void createUI()
{
Frame f = new Frame("客户端");
ta = new TextArea();
tf = new TextField();
Panel p = new Panel(new BorderLayout());
bn = new Button("发送");
p.add(tf,BorderLayout.CENTER);
p.add(bn,BorderLayout.EAST);
f.add(ta,BorderLayout.CENTER);
f.add(p,BorderLayout.SOUTH);
f.setSize(400,200);
f.setVisible(true);
//f.setVisable(true);
tf.addActionListener(new TCPClientListener());
bn.addActionListener(new TCPClientListener());
f.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
}
public void connect()
{
try{
s = new Socket("127.0.0.2",5599);
dis = new DataInputStream(s.getInputStream());
dos = new DataOutputStream(s.getOutputStream());
try{
//少了这个while不行!则不能循环接收
while(true)
{
String str = dis.readUTF();
//System.out.println("对方说:"+str);
ta.append("服务器说:"+str+"\n");
if(str.equalsIgnoreCase("再见"))
{
close();
System.exit(0);
}
}
}catch(Exception e)
{
JOptionPane.showMessageDialog(f,"客户端已离开");
return;
}
}catch(Exception e)
{
System.out.println("客户端异常");
//System.exit(0);
}
}
public void close()
{
try{
dis.close();
dos.close();
s.close();
}catch(Exception e)
{
System.exit(0);
}
}
class TCPClientListener implements ActionListener
{
@Override
public void actionPerformed(ActionEvent e)
{
try{
String str = tf.getText();
tf.setText("");
ta.append("客户端说:"+str+"\n");
dos.writeUTF(str);
if(str.equalsIgnoreCase("再见"))
{
close();
System.exit(0);
}
}catch(Exception e1)
{
// e.printStackTrace();
System.exit(0);
}
}
}
}
结果:
![有问题的Server和Client图](/images/java/结果.png)
1: 改用在main方法中定义循环的socket等待试试,即
ServerSocket ss = new ServerSocket(5599);
while(true)
{
new TCPServer1(ss.accept()).launch();
}
复制代码
去掉ServerRead那边的一种Connection函数中的循环的socket.accept:
```java
public void connect()
{
try
{
//while(true)
//{
//阻塞这边 等待客户端的连接
dis = new DataInputStream(s.getInputStream());
dos = new DataOutputStream(s.getOutputStream());
dos.writeUTF("欢迎你的到来"+s.getInetAddress().getHostAddress());
new UserReadThread().start();
new UserWriteThread().start();
System.out.println(Thread.currentThread().getName()+"已开启");
//}
2: 使用静态代码段的方法,只创建一个UI界面,不再把createUI放在launch当中,否则 只有在连接上客户端会创建客户端的同时也创建了服务器 服务器一直在增加,这不是我们所想的,只要一个服务器,于是采用static方法
至少在这边已经开始注意一个问题: 到底Client的read & write要不要加入线程? 这边是没有,仅仅是服务器的read & write加入了线程?
具体的RewreiteTestServer4:
/**
* 解释:
*/
package TestNetwork;
/**
* @author 叶昭良
* @time 2015年3月22日下午10:08:18
* @version TestNetworkRewriteTestServer4 V1.0
* 功能:
步骤:
* 注意:
* 掌握:
思考:
* 回顾:
*/
import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Frame;
import java.awt.Panel;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.*;
import java.net.*;
import javax.swing.JOptionPane;
public class RewriteTestServer4
{
/**
* @param args
* 原因:
* 解决:
* 功能:
* 思考:
* 步骤:
*/
public static void main(String[] args) throws Exception
{
// TODO Auto-generated method stub
ServerSocket ss = new ServerSocket(5599);
while(true)
{
new TCPServer1(ss.accept()).launch();
}
}
}
class TCPServer1
{
// class variables
// class variables
// connect
private Socket s = null;
// data flow
private DataOutputStream dos = null;
private DataInputStream dis = null;
// UI
private static Frame f = null;
private static TextArea ta = null;
private static TextField tf = null;
private static Button bn = null;
static
{
createUI();
}
public TCPServer1(Socket s)
{
this.s = s;
}
public void launch()
{
connect();
}
// construct member
/*
public TCPServer()
{
}
*/
public static void createUI()
{
Frame f = new Frame("服务器");
ta = new TextArea();
tf = new TextField();
Panel p = new Panel(new BorderLayout());
bn = new Button("发送");
p.add(tf,BorderLayout.CENTER);
p.add(bn,BorderLayout.EAST);
f.add(ta,BorderLayout.CENTER);
f.add(p,BorderLayout.SOUTH);
f.setSize(400,200);
f.setVisible(true);
//f.setVisable(true);
/* tf.addActionListener(new TCPServerListener());
bn.addActionListener(new TCPServerListener());*/
f.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
}
public void connect()
{
try
{
//while(true)
//{
//阻塞这边 等待客户端的连接
dis = new DataInputStream(s.getInputStream());
dos = new DataOutputStream(s.getOutputStream());
dos.writeUTF("欢迎你的到来"+s.getInetAddress().getHostAddress());
new UserReadThread().start();
new UserWriteThread().start();
System.out.println(Thread.currentThread().getName()+"已开启");
//}
}catch(Exception e)
{
System.out.println("服务器端异常");
// System.exit(0);
}
}
public void close()
{
try{
s.close();
}catch(Exception e)
{
System.exit(0);
}
}
class UserReadThread extends Thread
{
@Override
public void run()
{
while(true)
{
try{
String str = dis.readUTF();
// System.out.println("对方说:"+str);
ta.append("客户端说:"+str+"\n");
if(str.equalsIgnoreCase("再见"))
{
close();
System.exit(0);
}
}catch(Exception e)
{
JOptionPane.showMessageDialog(f,"客户端已离开");
return;
// e.printStackTrace();
}
}
}
}
class UserWriteThread extends Thread
{
@Override
public void run()
{
tf.addActionListener(new TCPServerListener());
bn.addActionListener(new TCPServerListener());
}
}
class TCPServerListener implements ActionListener
{
@Override
public void actionPerformed(ActionEvent e)
{
try{
String str = tf.getText();
if(!str.isEmpty())
{
tf.setText("");
ta.append("服务器说:"+str+"\n");
dos.writeUTF(str);
if(str.equalsIgnoreCase("再见"))
{
close();
System.exit(0);
}
}
}catch(Exception e1)
{
// e.printStackTrace();
System.exit(0);
}
}
}
}
RewriteClient4:
/**
* 解释:
*/
package TestNetwork;
import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Frame;
import java.awt.Panel;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;
import javax.swing.JOptionPane;
import TestNetwork.TCPClient.TCPClientListener;
/**
* @author 叶昭良
* @time 2015年3月22日下午10:08:37
* @version TestNetworkRewriteTestClient4 V1.0
* 功能:
步骤:
* 注意:
* 掌握:
思考:
* 回顾:
*/
public class RewriteTestClient4
{
/**
* @param args
* 原因:
* 解决:
* 功能:
* 思考:
* 步骤:
*/
public static void main(String[] args)
{
// TODO Auto-generated method stub
new TCPClient1().launch();
}
}
class TCPClient1
{
// class variables
// class variables
// connect
private Socket s = null;
// data flow
private DataOutputStream dos = null;
private DataInputStream dis = null;
// UI
private Frame f = null;
private TextArea ta = null;
private TextField tf = null;
private Button bn = null;
public void launch()
{
createUI();
connect();
// new ClientRead().start();
// new ClientWrite().start();
}
// construct member
/*
public TCPClient()
{
}
*/
public void createUI()
{
Frame f = new Frame("客户端");
ta = new TextArea();
tf = new TextField();
Panel p = new Panel(new BorderLayout());
bn = new Button("发送");
p.add(tf,BorderLayout.CENTER);
p.add(bn,BorderLayout.EAST);
f.add(ta,BorderLayout.CENTER);
f.add(p,BorderLayout.SOUTH);
f.setSize(400,200);
f.setVisible(true);
//f.setVisable(true);
tf.addActionListener(new TCPClientListener());
bn.addActionListener(new TCPClientListener());
f.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
}
public void connect()
{
try{
s = new Socket("127.0.0.1",5599);
dis = new DataInputStream(s.getInputStream());
dos = new DataOutputStream(s.getOutputStream());
try{
//少了这个while不行!则不能循环接收
while(true)
{
String str = dis.readUTF();
//System.out.println("对方说:"+str);
ta.append("服务器说:"+str+"\n");
if(str.equalsIgnoreCase("再见"))
{
close();
System.exit(0);
}
}
}catch(Exception e)
{
JOptionPane.showMessageDialog(f,"客户端已离开");
return;
}
}catch(Exception e)
{
System.out.println("客户端异常");
//System.exit(0);
}
}
public void close()
{
try{
dis.close();
dos.close();
s.close();
}catch(Exception e)
{
System.exit(0);
}
}
class TCPClientListener implements ActionListener
{
@Override
public void actionPerformed(ActionEvent e)
{
try{
String str = tf.getText();
tf.setText("");
ta.append("客户端说:"+str+"\n");
dos.writeUTF(str);
if(str.equalsIgnoreCase("再见"))
{
close();
System.exit(0);
}
}catch(Exception e1)
{
// e.printStackTrace();
System.exit(0);
}
}
}
}
结果:
通过分析发现,的确是需要把socket.accept放在main中,而不是connection中! 为什么? 也许是connection会跳出? 具体的不太清楚,如果有人知道求证明之。。。。
另外还存在一个问题,为什么服务器发出的数据只是放在第一个连接的地方? 这是不是跟Client没有线程有关系,如果我也建个clientRead & clientWrite线程?
1:增加了两个ClientRead & ClientWrite两个线程,尝试看一下是否可以群发, 2: 并把socket的创建 提到main当中
Client的重写:
/**
* 解释:
*/
package TestNetwork;
import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Frame;
import java.awt.Panel;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import javax.swing.JOptionPane;
/**
* @author 叶昭良
* @time 2015年3月22日下午10:08:37
* @version TestNetworkRewriteTestClient4 V1.0
* 功能:
步骤:
* 注意:
* 掌握:
思考:
* 回顾:
*/
public class RewriteTestClient4
{
/**
* @param args
* 原因:
* 解决:
* 功能:
* 思考:
* 步骤:
*/
public static void main(String[] args)
{
// TODO Auto-generated method stub
Socket s = null;
try
{
s = new Socket("127.0.0.1",5599);
} catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
new TCPClient1(s).launch();
}
}
class TCPClient1
{
// class variables
// class variables
// connect
private Socket s = null;
// data flow
private DataOutputStream dos = null;
private DataInputStream dis = null;
// UI
private Frame f = null;
private TextArea ta = null;
private TextField tf = null;
private Button bn = null;
public TCPClient1(Socket s)
{
this.s = s;
}
public void launch()
{
createUI();
connect();
new ClientRead(dis).start();
new ClientWrite(dos).start();
}
// construct member
/*
public TCPClient()
{
}
*/
public void createUI()
{
Frame f = new Frame("客户端");
ta = new TextArea();
tf = new TextField();
Panel p = new Panel(new BorderLayout());
bn = new Button("发送");
p.add(tf,BorderLayout.CENTER);
p.add(bn,BorderLayout.EAST);
f.add(ta,BorderLayout.CENTER);
f.add(p,BorderLayout.SOUTH);
f.setSize(400,200);
f.setVisible(true);
//f.setVisable(true);
tf.addActionListener(new TCPClientListener());
bn.addActionListener(new TCPClientListener());
f.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
}
public void connect()
{
try{
dis = new DataInputStream(s.getInputStream());
dos = new DataOutputStream(s.getOutputStream());
}catch(Exception e)
{
System.out.println("客户端异常");
//System.exit(0);
}
}
public void close()
{
try{
dis.close();
dos.close();
s.close();
}catch(Exception e)
{
System.exit(0);
}
}
class ClientRead extends Thread
{
private DataInputStream dis = null;
public ClientRead(DataInputStream dis)
{
this.dis = dis;
}
public void run()
{
while(true)
{
try{
String str = dis.readUTF();
ta.append("服务器说:"+str+"\n");
if(str.equalsIgnoreCase("再见"))
{
close();
System.exit(0);
}
System.out.println("服务器说:"+str);
if(str.equalsIgnoreCase("byebye"))
break;
}catch(Exception e)
{
JOptionPane.showMessageDialog(f,"客户端已离开");
return;
}
}
}
}
class ClientWrite extends Thread
{
private DataOutputStream dos = null;
public ClientWrite(DataOutputStream dos)
{
this.dos = dos;
}
public void run()
{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
while(true)
{
try{
String str = br.readLine();
dos.writeUTF(str);
if(str.equalsIgnoreCase("byebye"))
break;
}catch(Exception e)
{
e.printStackTrace();
}
}
}
}
class TCPClientListener implements ActionListener
{
@Override
public void actionPerformed(ActionEvent e)
{
try{
String str = tf.getText();
tf.setText("");
ta.append("客户端说:"+str+"\n");
dos.writeUTF(str);
if(str.equalsIgnoreCase("再见"))
{
close();
System.exit(0);
}
}catch(Exception e1)
{
// e.printStackTrace();
System.exit(0);
}
}
}
}
结果: 结果分析发现,并没有丝毫的改进。what a pity.
参考此文进行修改:
1:加入了三个主要集合
用户 user_list
线程集
消息集
2:服务器端的主要修正:
1.在线程类的构造函数都添加 了start() 开启线程
2.删掉了ServerWrite &ServerRead线程 都整合到ThreadServer类中
3.增加了一个PrintOutThread用于向客户端发送消息
3:客户端的主要修正:
新建了一个readLineThread() 读取服务器数据,并传递客户端数据
删除了ClientReader & ClientWriter & launch等函数
基本能够实现转发的功能,但是 存在的问题:开启多个则计算机变得特别卡
RewriteServer5的实现:
/**
* 解释:
*/
package TestNetwork;
import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Frame;
import java.awt.Panel;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.*;
/**
* @author 叶昭良
* @time 2015年3月23日上午12:41:10
* @version TestNetworkRewriteTestServer5 V1.0
* 功能:
步骤:
* 注意:
* 掌握:
思考:
* 回顾:
*/
public class RewriteTestServer5
{
/**
* @param args
* 原因:
* 解决:
* 功能:
* 思考:
* 步骤:
*/
public static void main(String[] args) throws Exception
{
// TODO Auto-generated method stub
ServerSocket ss = new ServerSocket(5599);
while(true)
{
new TCPServer2(ss.accept());
}
}
}
class TCPServer2
{
// class variables
// class variables
// connect
private Socket s = null;
// data flow
private DataOutputStream dos = null;
private DataInputStream dis = null;
// UI
private static Frame f = null;
private static TextArea ta = null;
private static TextField tf = null;
private static Button bn = null;
private static boolean isPrint = false;//是否输出消息标志
private static List user_list = new ArrayList();//登陆用户集合
private static List<ServerThread> thread_list = new ArrayList<ServerThread>();//服务器已启用的线程集合
private static LinkedList<String> message_list = new LinkedList<String>();//存放消息队列
static
{
createUI();
}
public TCPServer2(Socket s) throws Exception
{
this.s = s;
new PrintOutThread();//创建向客户端发送消息线程
new ServerThread(this.s);
//本以为可以让其线程安全的,没想到没什么效果 反而更卡
/* Collections.synchronizedList(user_list);
Collections.synchronizedList(thread_list);
Collections.synchronizedList(message_list);*/
// launch();
}
// construct member
/*
public TCPServer()
{
}
*/
public static void createUI()
{
Frame f = new Frame("服务器");
ta = new TextArea();
tf = new TextField();
Panel p = new Panel(new BorderLayout());
bn = new Button("发送");
p.add(tf,BorderLayout.CENTER);
p.add(bn,BorderLayout.EAST);
f.add(ta,BorderLayout.CENTER);
f.add(p,BorderLayout.SOUTH);
f.setSize(400,200);
f.setVisible(true);
//f.setVisable(true);
/* tf.addActionListener(new TCPServerListener());
bn.addActionListener(new TCPServerListener());*/
f.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
}
public void close()
{
try{
s.close();
}catch(Exception e)
{
System.exit(0);
}
}
class TCPServerListener implements ActionListener
{
@Override
public void actionPerformed(ActionEvent e)
{
try{
String str = tf.getText();
if(!str.isEmpty())
{
tf.setText("");
ta.append("服务器说:"+str+"\n");
dos.writeUTF(str);
if(str.equalsIgnoreCase("再见"))
{
close();
System.exit(0);
}
}
}catch(Exception e1)
{
// e.printStackTrace();
System.exit(0);
}
}
}
/**
* 服务器线程类
*/
class ServerThread extends Thread{
private Socket client;
private String name = null;
public ServerThread(Socket s) throws Exception
{
this.client = s;
dis = new DataInputStream(client.getInputStream());
dos = new DataOutputStream(client.getOutputStream());
dos.writeUTF("成功连上服务器"+client.getInetAddress().getHostAddress());
dos.writeUTF(Thread.currentThread().getName()+"已开启"+"请输入你的姓名");
this.start();
tf.addActionListener(new TCPServerListener());
bn.addActionListener(new TCPServerListener());
}
@Override
public void run()
{
try
{
int flag = 0 ;
String line = dis.readUTF();
while(!"bye".equals(line))
{
//查看在线用户列表
if ("showuser".equals(line)) {
dos.writeUTF(this.listOnlineUsers());
line = dis.readUTF();
}
//第一次进入,保存名字
if(flag++ ==0){
name = line;
user_list.add(name);
thread_list.add(this);
System.out.println("this="+this);
dos.writeUTF(name +"你好,可以开始聊天了...");
this.pushMessage("Client<" + name +">进入聊天室...+\n");
ta.append("Client<" + name +">进入聊天室...\n");
}else{
ta.append("Client<" + name +"> say : " + line+"\n");
this.pushMessage("Client<" + name +"> say : " + line+"\n");
}
line = dis.readUTF();
}
}catch(IOException e)
{
}
}
//放入消息队列末尾,准备发送给客户端
private void pushMessage(String msg){
message_list.addLast(msg);
isPrint =true;
}
//向客户端发送一条消息(在PrintOutThread 有使用 于是变成了public修饰)
public void sendMessage(String msg) throws Exception{
dos.writeUTF(msg);
dos.flush();
}
//统计在线用户列表 需要在showuser中使用 改为public
private String listOnlineUsers() {
String s ="--- 在线用户列表 ---\015\012";
for (int i =0; i < user_list.size(); i++) {
s +="[" + user_list.get(i) +"]\015\012";
}
s +="--------------------";
return s;
}
}
/**
* 监听是否有输出消息请求线程类,向客户端发送消息
*/
class PrintOutThread extends Thread
{
public PrintOutThread()
{
start();
}
@Override
public void run()
{
while(true){
if(isPrint){//将缓存在队列中的消息按顺序发送到各客户端,并从队列中清除。
String message = message_list.getFirst();
for (ServerThread thread : thread_list)
{
try
{
thread.sendMessage(message);
} catch (Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
message_list.removeFirst();
isPrint = message_list.size() >0 ?true :false;
}
}
}
}
}
RewriteClient5的实现:
/**
* 解释:
*/
package TestNetwork;
import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Frame;
import java.awt.Panel;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import javax.swing.JOptionPane;
import TestNetwork.TCPClient1.ClientRead;
import TestNetwork.TCPClient1.ClientWrite;
import TestNetwork.TCPClient1.TCPClientListener;
/**
* @author 叶昭良
* @time 2015年3月23日上午1:57:54
* @version TestNetworkRewriteTestClient5 V1.0
* 功能:
步骤:
* 注意:
* 掌握:
思考:
* 回顾:
*/
public class RewriteTestClient5
{
/**
* @param args
* 原因:
* 解决:
* 功能:
* 思考:
* 步骤:
*/
public static void main(String[] args) throws Exception
{
// TODO Auto-generated method stub
Socket s = null;
s = new Socket("127.0.0.1",5599);
new TCPClient5(s);
}
}
class TCPClient5
{
// class variables
// class variables
// connect
private Socket s = null;
// data flow
private DataOutputStream dos = null;
private DataInputStream dis = null;
// UI
private Frame f = null;
private TextArea ta = null;
private TextField tf = null;
private Button bn = null;
/**
* 与服务器连接,并输入发送消息
*/
public TCPClient5(Socket s) throws Exception
{
this.s = s;
dis = new DataInputStream(s.getInputStream());
dos = new DataOutputStream(s.getOutputStream());
createUI();
//new ClientRead(dis);
//new ClientWrite(dos);
new readLineThread();
}
/**
* 用于监听服务器端向客户端发送消息线程类
*/
public void createUI()
{
Frame f = new Frame("客户端");
ta = new TextArea();
tf = new TextField();
Panel p = new Panel(new BorderLayout());
bn = new Button("发送");
p.add(tf,BorderLayout.CENTER);
p.add(bn,BorderLayout.EAST);
f.add(ta,BorderLayout.CENTER);
f.add(p,BorderLayout.SOUTH);
f.setSize(400,200);
f.setVisible(true);
//f.setVisable(true);
tf.addActionListener(new TCPClientListener());
bn.addActionListener(new TCPClientListener());
f.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
}
public void close()
{
try{
dis.close();
dos.close();
s.close();
}catch(Exception e)
{
System.exit(0);
}
}
/**
* 用于监听服务器端向客户端发送消息线程类
*/
class readLineThread extends Thread
{
private BufferedReader buff = null;;
public readLineThread(){
start();
}
@Override
public void run() {
try {
while(true){
String result = dis.readUTF();
if("byeClient".equals(result)){//客户端申请退出,服务端返回确认退出
break;
}else{//输出服务端发送消息
//ta.append(result);
System.out.println(result);
// dos.writeUTF(result);
ta.append("服务器传递说:"+result+"\n");
if(result.equalsIgnoreCase("再见"))
{
close();
System.exit(0);
}
}
}
}catch (Exception e) {
}
}
}
class TCPClientListener implements ActionListener
{
@Override
public void actionPerformed(ActionEvent e)
{
try{
String str = tf.getText();
tf.setText("");
ta.append("客户端说:"+str+"\n");
dos.writeUTF(str);
if(str.equalsIgnoreCase("再见"))
{
close();
System.exit(0);
}
}catch(Exception e1)
{
// e.printStackTrace();
System.exit(0);
}
}
}
}
结果: 还有一问题待解决,运行多个界面,程序占用内存过大!!!!!! 如何解决??
还有一个问题:服务器说的话只能发给一个人。
解决办法: 修改TCPServerListener的实现:
private ServerThread st = null;
public TCPServerListener(ServerThread s)
{
st =s ;
}
并在actionPerformed中增加 st.pushMessage();
try{
String str = tf.getText();
if(!str.isEmpty())
{
tf.setText("");
ta.append("服务器说:"+str+"\n");
dos.writeUTF(str);
if(str.equalsIgnoreCase("再见"))
{
close();
System.exit(0);
}
//ta.append("Client<" + name +"> say : " + line+"\n");
st.pushMessage("服务器说:"+str+"\n");
}
当然在ServerThread服务器线程类中,增加
private ServerThread st = null; // 受到启发的作用!! Client=s
this.st = this;
的确这样修改完有效果,能够群发了,但是就是有些哥们的客户端发的是两条重复信息,有些却是一条。
重要修改,能够保证服务器发的信息不至于发太多给某一个客户端:
String str = tf.getText();
if(!str.isEmpty())
{
tf.setText("");
ta.append("服务器说:"+str+"\n");
//dos.writeUTF(str); //如果不注释掉 其中有一个用户会得到两条重复信息,都把他放在message_list中进行转发,而不是特例的一个利用dos.writeUTF(str)
if(str.equalsIgnoreCase("再见"))
{
close();
System.exit(0);
}
st.pushMessage("服务器说:"+str+"\n");
}
基于此我们的Server端如下:
/**
* 解释:
*/
package TestNetwork;
import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Frame;
import java.awt.Panel;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.*;
/**
* @author 叶昭良
* @time 2015年3月23日上午12:41:10
* @version TestNetworkRewriteTestServer5 V1.0
* 功能:
步骤:
* 注意:
* 掌握:
思考:
* 回顾:
*/
public class RewriteTestServer5
{
/**
* @param args
* 原因:
* 解决:
* 功能:
* 思考:
* 步骤:
*/
public static void main(String[] args) throws Exception
{
// TODO Auto-generated method stub
ServerSocket ss = new ServerSocket(5599);
while(true)
{
new TCPServer2(ss.accept());
}
}
}
class TCPServer2
{
// class variables
// class variables
// connect
private Socket s = null;
// data flow
private DataOutputStream dos = null;
private DataInputStream dis = null;
// UI
private static Frame f = null;
private static TextArea ta = null;
private static TextField tf = null;
private static Button bn = null;
private static boolean isPrint = false;//是否输出消息标志
private static List user_list = new ArrayList();//登陆用户集合
private static List<ServerThread> thread_list = new ArrayList<ServerThread>();//服务器已启用的线程集合
private static LinkedList<String> message_list = new LinkedList<String>();//存放消息队列
static
{
createUI();
}
public TCPServer2(Socket s) throws Exception
{
this.s = s;
new PrintOutThread();//创建向客户端发送消息线程
new ServerThread(this.s);
//本以为可以让其线程安全的,没想到没什么效果 反而更卡
/* Collections.synchronizedList(user_list);
Collections.synchronizedList(thread_list);
Collections.synchronizedList(message_list);*/
// launch();
}
// construct member
/*
public TCPServer()
{
}
*/
public static void createUI()
{
Frame f = new Frame("服务器");
ta = new TextArea();
tf = new TextField();
Panel p = new Panel(new BorderLayout());
bn = new Button("发送");
p.add(tf,BorderLayout.CENTER);
p.add(bn,BorderLayout.EAST);
f.add(ta,BorderLayout.CENTER);
f.add(p,BorderLayout.SOUTH);
f.setSize(400,200);
f.setVisible(true);
//f.setVisable(true);
/* tf.addActionListener(new TCPServerListener());
bn.addActionListener(new TCPServerListener());*/
f.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
}
public void close()
{
try{
s.close();
}catch(Exception e)
{
System.exit(0);
}
}
class TCPServerListener implements ActionListener
{
private ServerThread st = null;
public TCPServerListener(ServerThread s)
{
st =s ;
}
@Override
public void actionPerformed(ActionEvent e)
{
try{
String str = tf.getText();
if(!str.isEmpty())
{
tf.setText("");
ta.append("服务器说:"+str+"\n");
//dos.writeUTF(str);
if(str.equalsIgnoreCase("再见"))
{
close();
System.exit(0);
}
st.pushMessage("服务器说:"+str+"\n");
}
}catch(Exception e1)
{
// e.printStackTrace();
System.exit(0);
}
}
}
/**
* 服务器线程类
*/
class ServerThread extends Thread{
private Socket client;
private String name = null;
private ServerThread st = null; // 受到启发的作用!! Client=s
public ServerThread(Socket s) throws Exception
{
this.client = s;
this.st = this;
dis = new DataInputStream(client.getInputStream());
dos = new DataOutputStream(client.getOutputStream());
dos.writeUTF("成功连上服务器"+client.getInetAddress().getHostAddress());
dos.writeUTF(Thread.currentThread().getName()+"已开启"+"请输入你的姓名");
this.start();
//tf的监听器
tf.addActionListener(new TCPServerListener(this.st));
bn.addActionListener(new TCPServerListener(this.st));
}
@Override
public void run()
{
try
{
int flag = 0 ;
String line = dis.readUTF();
while(!"bye".equals(line))
{
//查看在线用户列表
if ("showuser".equals(line)) {
dos.writeUTF(this.listOnlineUsers());
line = dis.readUTF();
}
//第一次进入,保存名字
if(flag++ ==0){
name = line;
user_list.add(name);
thread_list.add(this);
System.out.println("this="+this);
dos.writeUTF(name +"你好,可以开始聊天了...");
//
this.pushMessage("Client<" + name +">进入聊天室...+\n");
//现在在服务器的大厅中
ta.append("Client<" + name +">进入聊天室...\n");
}else{
//现在在服务器的大厅中
ta.append("Client<" + name +"> say : " + line+"\n");
this.pushMessage("Client<" + name +"> say : " + line+"\n");
}
line = dis.readUTF();
}
}catch(IOException e)
{
}
}
//放入消息队列末尾,准备发送给客户端
public void pushMessage(String msg){
message_list.addLast(msg);
isPrint =true;
}
//向客户端发送一条消息(在PrintOutThread 有使用 于是变成了public修饰)
public void sendMessage(String msg) throws Exception{
dos.writeUTF(msg);
dos.flush();
}
//统计在线用户列表 需要在showuser中使用 改为public
private String listOnlineUsers() {
String s ="--- 在线用户列表 ---";
for (int i =0; i < user_list.size(); i++) {
s +="[" + user_list.get(i) +"]";
}
s +="--------------------";
return s;
}
}
/**
* 监听是否有输出消息请求线程类,向客户端发送消息
*/
class PrintOutThread extends Thread
{
public PrintOutThread()
{
start();
}
@Override
public void run()
{
while(true){
if(isPrint){//将缓存在队列中的消息按顺序发送到各客户端,并从队列中清除。
String message = message_list.getFirst();
for (ServerThread thread : thread_list)
{
try
{
//this.
thread.sendMessage(message);
} catch (Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
message_list.removeFirst();
isPrint = message_list.size() >0 ?true :false;
}
}
}
}
}
Client端如下:
/**
* 解释:
*/
package TestNetwork;
import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Frame;
import java.awt.Panel;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import javax.swing.JOptionPane;
import TestNetwork.TCPClient1.ClientRead;
import TestNetwork.TCPClient1.ClientWrite;
import TestNetwork.TCPClient1.TCPClientListener;
/**
* @author 叶昭良
* @time 2015年3月23日上午1:57:54
* @version TestNetworkRewriteTestClient5 V1.0
* 功能:
步骤:
* 注意:
* 掌握:
思考:
* 回顾:
*/
public class RewriteTestClient5
{
/**
* @param args
* 原因:
* 解决:
* 功能:
* 思考:
* 步骤:
*/
public static void main(String[] args) throws Exception
{
// TODO Auto-generated method stub
Socket s = null;
s = new Socket("127.0.0.1",5599);
new TCPClient5(s);
}
}
class TCPClient5
{
// class variables
// class variables
// connect
private Socket s = null;
// data flow
private DataOutputStream dos = null;
private DataInputStream dis = null;
// UI
private Frame f = null;
private TextArea ta = null;
private TextField tf = null;
private Button bn = null;
/**
* 与服务器连接,并输入发送消息
*/
public TCPClient5(Socket s) throws Exception
{
this.s = s;
dis = new DataInputStream(s.getInputStream());
dos = new DataOutputStream(s.getOutputStream());
createUI();
//new ClientRead(dis);
//new ClientWrite(dos);
new readLineThread();
}
/**
* 用于监听服务器端向客户端发送消息线程类
*/
public void createUI()
{
Frame f = new Frame("客户端");
ta = new TextArea();
tf = new TextField();
Panel p = new Panel(new BorderLayout());
bn = new Button("发送");
p.add(tf,BorderLayout.CENTER);
p.add(bn,BorderLayout.EAST);
f.add(ta,BorderLayout.CENTER);
f.add(p,BorderLayout.SOUTH);
f.setSize(400,200);
f.setVisible(true);
//f.setVisable(true);
tf.addActionListener(new TCPClientListener());
bn.addActionListener(new TCPClientListener());
f.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
}
public void close()
{
try{
dis.close();
dos.close();
s.close();
}catch(Exception e)
{
System.exit(0);
}
}
/**
* 用于监听服务器端向客户端发送消息线程类
*/
class readLineThread extends Thread
{
private BufferedReader buff = null;;
public readLineThread(){
start();
}
@Override
public void run() {
try {
while(true){
String result = dis.readUTF();
if("byeClient".equals(result)){//客户端申请退出,服务端返回确认退出
break;
}else{//输出服务端发送消息
//ta.append(result);
System.out.println(result);
// dos.writeUTF(result);
ta.append("服务器传递说:"+result+"\n");
if(result.equalsIgnoreCase("再见"))
{
close();
System.exit(0);
}
}
}
}catch (Exception e) {
}
}
}
class TCPClientListener implements ActionListener
{
@Override
public void actionPerformed(ActionEvent e)
{
try{
String str = tf.getText();
tf.setText("");
ta.append("客户端说:"+str+"\n");
dos.writeUTF(str);
if(str.equalsIgnoreCase("再见"))
{
close();
System.exit(0);
}
}catch(Exception e1)
{
// e.printStackTrace();
System.exit(0);
}
}
}
}
结果:
1:之前的线程卡的问题,还没有解决,运行起来仍然感觉占用大量内存!! 2:为什么 第一个进入聊天系统的杨中科,会打印出三次呢? 当然后面的消息正常,当然三次的意思是聊天系统进入了三个人,都教授和Rocket. 所以肯定是那部分有问题待解决
http://www.cnblogs.com/linjiqin/archive/2013/05/30/3108188.html 欣赏了这篇佳作
基于上次的问题,打开的时候经常会出现多个打印的情况, 尝试如下修改: “登录用户集合”和“服务器已启用线程集合”的list,如果变化不频繁,读多写少,使用CopyOnWriteArrayList。 “存放消息队列”用Queue接口比List接口更方便(每次直接POP,没消息时候还可以让广播线程自己阻塞在上面),具体的用ArrayBlockingQueue或者LinkedBlockingQueue都可以。http://my.oschina.net/leejun2005/blog/104955 对应的message_list.addLast(msg) 变成了message_list.put(msg) message_list.take()来取代 message_list.getFirst() & message_list.removeFirst() 效果就是居然没问题。居然也不卡了
Server端的修正版本:(Client端不需要修正)
/**
* 解释:
*/
package TestNetwork;
import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Frame;
import java.awt.Panel;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.LinkedBlockingQueue;
/**
* @author 叶昭良
* @time 2015年3月23日上午12:41:10
* @version TestNetworkRewriteTestServer5 V1.0
* 功能:
步骤:
* 注意:
* 掌握:
思考:
* 回顾:
*/
public class RewriteTestServer5
{
/**
* @param args
* 原因:
* 解决:
* 功能:
* 思考:
* 步骤:
*/
public static void main(String[] args) throws Exception
{
// TODO Auto-generated method stub
ServerSocket ss = new ServerSocket(5599);
while(true)
{
new TCPServer2(ss.accept());
}
}
}
class TCPServer2
{
// class variables
// class variables
// connect
private Socket s = null;
// data flow
private DataOutputStream dos = null;
private DataInputStream dis = null;
// UI
private static Frame f = null;
private static TextArea ta = null;
private static TextField tf = null;
private static Button bn = null;
private static boolean isPrint = false;//是否输出消息标志
private static List user_list = new CopyOnWriteArrayList();//登陆用户集合
private static List<ServerThread> thread_list = new CopyOnWriteArrayList<ServerThread>();//服务器已启用的线程集合
private static LinkedBlockingQueue<String> message_list = new LinkedBlockingQueue<String>();//存放消息队列
// private static List<String> message_list = new LinkedList<String>();//存放消息队列
static
{
createUI();
}
public TCPServer2(Socket s) throws Exception
{
this.s = s;
new PrintOutThread();//创建向客户端发送消息线程
new ServerThread(this.s);
//本以为可以让其线程安全的,没想到没什么效果 反而更卡
/* Collections.synchronizedList(user_list);
Collections.synchronizedList(thread_list);
Collections.synchronizedList(message_list);*/
// launch();
}
// construct member
/*
public TCPServer()
{
}
*/
public static void createUI()
{
Frame f = new Frame("服务器");
ta = new TextArea();
tf = new TextField();
Panel p = new Panel(new BorderLayout());
bn = new Button("发送");
p.add(tf,BorderLayout.CENTER);
p.add(bn,BorderLayout.EAST);
f.add(ta,BorderLayout.CENTER);
f.add(p,BorderLayout.SOUTH);
f.setSize(400,200);
f.setVisible(true);
//f.setVisable(true);
/* tf.addActionListener(new TCPServerListener());
bn.addActionListener(new TCPServerListener());*/
f.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
}
public void close()
{
try{
s.close();
}catch(Exception e)
{
System.exit(0);
}
}
class TCPServerListener implements ActionListener
{
private ServerThread st = null;
public TCPServerListener(ServerThread s)
{
st =s ;
}
@Override
public void actionPerformed(ActionEvent e)
{
try{
String str = tf.getText();
if(!str.isEmpty())
{
tf.setText("");
ta.append("服务器说:"+str+"\n");
//dos.writeUTF(str);
if(str.equalsIgnoreCase("再见"))
{
close();
System.exit(0);
}
st.pushMessage("服务器说:"+str+"\n");
}
}catch(Exception e1)
{
// e.printStackTrace();
System.exit(0);
}
}
}
/**
* 服务器线程类
*/
class ServerThread extends Thread{
private Socket client;
private String name = null;
private ServerThread st = null; // 受到启发的作用!! Client=s
public ServerThread(Socket s) throws Exception
{
this.client = s;
this.st = this;
dis = new DataInputStream(client.getInputStream());
dos = new DataOutputStream(client.getOutputStream());
dos.writeUTF("成功连上服务器"+client.getInetAddress().getHostAddress());
dos.writeUTF(Thread.currentThread().getName()+"已开启"+"请输入你的姓名");
this.start();
//tf的监听器
tf.addActionListener(new TCPServerListener(this.st));
bn.addActionListener(new TCPServerListener(this.st));
}
@Override
public void run()
{
try
{
int flag = 0 ;
String line = dis.readUTF();
while(!"bye".equals(line))
{
//查看在线用户列表
if ("showuser".equals(line)) {
dos.writeUTF(this.listOnlineUsers());
line = dis.readUTF();
}
//第一次进入,保存名字
if(flag++ ==0){
name = line;
user_list.add(name);
thread_list.add(this);
System.out.println("this="+this);
this.pushMessage("Client<" + name +">进入聊天室...+\n");
dos.writeUTF(name +"你好,可以开始聊天了...");
//
//现在在服务器的大厅中
ta.append("Client<" + name +">进入聊天室...\n");
}else{
//现在在服务器的大厅中
ta.append("Client<" + name +"> say : " + line+"\n");
this.pushMessage("Client<" + name +"> say : " + line+"\n");
}
line = dis.readUTF();
}
}catch(IOException e)
{
}
}
//放入消息队列末尾,准备发送给客户端
public void pushMessage(String msg){
//msssage_list.
//message_list.addLast(msg); //针对于LinkedList
try
{
message_list.put(msg);
} catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
isPrint =true;
}
//向客户端发送一条消息(在PrintOutThread 有使用 于是变成了public修饰)
public void sendMessage(String msg) throws Exception{
dos.writeUTF(msg);
dos.flush();
}
//统计在线用户列表 需要在showuser中使用 改为public
private String listOnlineUsers() {
String s ="--- 在线用户列表 ---";
for (int i =0; i < user_list.size(); i++) {
s +="[" + user_list.get(i) +"]";
}
s +="--------------------";
return s;
}
}
/**
* 监听是否有输出消息请求线程类,向客户端发送消息
*/
class PrintOutThread extends Thread
{
public PrintOutThread()
{
start();
}
@Override
public void run()
{
while(true){
if(isPrint){//将缓存在队列中的消息按顺序发送到各客户端,并从队列中清除。
String message = null;
try
{
message = message_list.take();
} catch (InterruptedException e1)
{
// TODO Auto-generated catch block
e1.printStackTrace();
}
//message = message_list.getFirst();
for (ServerThread thread : thread_list)
{
try
{
//this.
thread.sendMessage(message);
} catch (Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// message_list.removeFirst();
//isPrint = false;
//这句话很重要!!!不能随便修改
isPrint = message_list.size()>0 ?true :false;
}
}
}
}
}
1 动机是为了熟悉网络编程,于是重写了之前写过的代码。从TCPServer 1 2 3三个进行重新的改写
2 发现TCPServer3有问题?
在客户端连接上后,客户端发送数据可以到服务器端,接着切换到服务器端,但是服务器的输入出现了问题,
无论你输入一个字还是n个字都无法切换回客户端了
3 于是基于这个问题开始进行修改 主要包含一个主要 版本1 Server有一对读写线程类 Client有一对读写线程类 版本2 Server有一对读写线程类 Client没有一对读写线程类 版本3 Server有一个ServerThread线程类 Client有一个readLineThread线程类
真的是从原先的四个类,到最后抛弃了原先的四个类,重写了另外的几个类
RewriteServer1.java RewriteServer2.java RewriteServer3.java RewriteServer4.java RewriteServer5.java 最后的一个版本是RewriteServer5
4 版本3 引入了新的数据结构 CopyWriteArrayList和LinkedBlockingQueue 线程阻塞类
5: 注意了 5.1 线程的使用(从thread到Runnable),线程开启放置的位置,最后发现使用了多线程服务器客户端 5.2 静态代码段static的使用 5.3 ServerThread的静态变量的使用,配合上构造函数 5.4 数据结构的使用
猛然发现一个特点:
GTK.g_timeout_add(100, new IGSourceFunc()
{
@Override
public boolean execute(Object userdata)
{
// TODO Auto-generated method stub
int len = otv.getText().length();
char ch = love.charAt(len);
otv.insertTextAtEnd(Character.toString(ch));
if(otv.getText().length() == love.length()-1)
{
//om.close();
try
(
OOMusic om = new OOMusic("我如此爱你.mp3",true);
)
{
om.playRepeat();
}
return true;
}else if(otv.getText().length() == love.length())
{
return false;
}
else
{
//System.out.println("2");
return true;
}
}
}, null);
2:
vgOpen.addActivateListener(new IGCallBack()
{
@Override
public void execute(int instance, int eventData, Object object)
{
youOpenfile(ootv);
}
});
3:
//添加按钮事件
GTK.g_signal_connect(btnShow, "clicked", new IGCallBack()
{
@Override
public void execute(int instance, int eventData, Object object)
{
// TODO 自动生成的方法存根
String text = GTK.gtk_text_buffer_get_text(textbuffer);
GTK.gtk_label_set_text(label, text);
}
}, null);
4: 前三个都是针对gtk的 awt的事件关闭监听,
f = new JFrame("客户端");
f.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
当然在JFrame可以采用: f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 复制代码 他们共同的特点是都采用匿名类,当做时间的响应函数或者回调函数,于是当前的网络编程的button和 textfield也是采用匿名类来实现:
class TCPClientListener implements ActionListener
{
@Override
public void actionPerformed(ActionEvent e)
{
try{
String str = tf.getText();
tf.setText("");
ta.append("客户端说:"+str+"\n");
dos.writeUTF(str);
if(str.equalsIgnoreCase("再见"))
{
close();
System.exit(0);
}
}catch(Exception e1)
{
// e.printStackTrace();
System.exit(0);
}
}
}
添加监听:
tf.addActionListener(new TCPClientListener());
bn.addActionListener(new TCPClientListener());
注意匿名类的监听函数的匿名类对象实现回调
学习了匿名类的事件回调函数。