网络书屋(Web Reading Room)

A blogging framework for hackers.

多线程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张的票,但是实际却是销售了?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
/**
* 解释:
*/
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) 都是可以的, 相当于在一扇门中 塞入了口香糖,得拿走了才能打开!一个坑的作用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@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中执行,因为少)

1
2
3
4
5
6
7
8
9
10
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方法也是可以的! 相当于锁定函数代码块,霸占着 直到用完! 但是这个方法仅仅针对一个线程,执行完!一个对象霸占一个方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
/**
* 解释:
*/
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;
}
}
}

}

结果:

1
2
3
4
5
6
7
8
9
10
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的时候通知生产)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
/**
* 解释:
*/
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());
}
}
}

结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
/**
* 解释:
*/
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
/**
* 解释:
*/
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)
{

}
}

}

结果:

1
2
先开启Server,再运行Client
1000

TCP编程: 知识点: 1.通过ServerSocket产生一个运行的所需的端口号 2.利用Socket进行编程,Socket源自于unix操作系统

案例1: 模拟UDP的类似程序,即服务器端等待数据 进行读取 客户端发送数据

Server:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/**
* 解释:
*/
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
/**
* 解释:
*/
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
<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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
/**
* 解释:
*/
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中止

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
/**
* 解释:
*/
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的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
/**
* 解释:
*/
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();
}
}
}
}

结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
客户端的控制台:

--------------------------------------------------------
客户端说:fs
fsdf
客户端说:heihei
吃了吗
客户端说:我吃了啊,下午去哪里玩?
----------------------------------------------------------


服务器端的控制台:
----------------------------------------------------------

fs

服务器说:fsdf

heihei
服务器说:吃了吗
我吃了啊,下午去哪里玩?
服务器说:不然咱们去闯红灯吧

----------------------------------------------------------

问题:这里面的线程切换,没有生产和消费的明显,还需要进一步理解线程的切换,为什么就能够从Server的读线程,然后写入信息给client就可以切换到Client的读线程?

      仅仅加入了extends thread?

案例4: 在原先的基础上 加入了窗体,并进一步抽象到TCPClient & TCPServer两个类下,通过TCPClient.launch TCPServer.launch进行启动 美化效果操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
/**
* 解释:
*/
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的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
/**
* 解释:
*/
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%E3%80%82

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
/**
* 解释:
*/
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
/**
* 解释:
*/
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 光临” 紧接着客户端做了动作,接收到了服务器来的欢迎词,然后开始写入 一些话提交给服务器 反复执行这个过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
/**
* 解释:
*/
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
/**
* 解释:
*/
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();
                }
        }

}

我遇到了一个问题:

1
2
3
4
5
6
7
8
9
10
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有关系,没关系,加进去也没什么效果)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
/**
* 解释:
*/
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
/**
* 解释:
*/
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
/**
* 解释:
*/
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
/**
* 解释:
*/
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 并没有新的客户端连接服务器)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
/**
* 解释:
*/
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);
               } 
        }
    }

}

1
2
3
4
5
6
7
8
9
10
11
12
13
结果:
![有问题的ServerClient](/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()+"已开启");
                //}
1
2
3
4
5
6
7
8
2: 使用静态代码段的方法,只创建一个UI界面,不再把createUI放在launch当中,否则 只有在连接上客户端会创建客户端的同时也创建了服务器
   服务器一直在增加,这不是我们所想的,只要一个服务器,于是采用static方法

至少在这边已经开始注意一个问题: 到底Clientread & write要不要加入线程? 这边是没有,仅仅是服务器的read & write加入了线程?



具体的RewreiteTestServer4:

java /* * 解释: / 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);
               } 
        }
    }

}

1
RewriteClient4:

java /* * 解释: / 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);
               } 
        }
    }

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
结果:

通过分析发现,的确是需要把socket.accept放在main中,而不是connection中! 为什么? 也许是connection会跳出? 具体的不太清楚,如果有人知道求证明之。。。。

另外还存在一个问题,为什么服务器发出的数据只是放在第一个连接的地方? 这是不是跟Client没有线程有关系,如果我也建个clientRead & clientWrite线程?
![现在的切换和传递都不成问题](/images/java/切换不成问题.png)






1:增加了两个ClientRead & ClientWrite两个线程,尝试看一下是否可以群发,
2 并把socket的创建 提到main当中

Client的重写:

java /* * 解释: / 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);
               } 
        }
    }

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
结果:
结果分析发现,并没有丝毫的改进。what a pity.

![结果还是跟上例的client没有线程一样](/images/java/yiranmeiyou.png)


参考此文进行修改:

1:加入了三个主要集合
  用户   user_list
  线程集
  消息集
2:服务器端的主要修正:
    1.在线程类的构造函数都添加 start() 开启线程
    2.删掉了ServerWrite &ServerRead线程  都整合到ThreadServer类中
    3.增加了一个PrintOutThread用于向客户端发送消息
3:客户端的主要修正:
   新建了一个readLineThread() 读取服务器数据,并传递客户端数据
   删除了ClientReader  & ClientWriter  & launch等函数


基本能够实现转发的功能,但是
存在的问题:开启多个则计算机变得特别卡


RewriteServer5的实现:

java /* * 解释: / 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;
                }
            }
        }
    }

}

1
RewriteClient5的实现:

java /* * 解释: / 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
3
4
5
6
7
8
结果: 还有一问题待解决,运行多个界面,程序占用内存过大!!!!!! 如何解决??

![服务器转发客户端实现局部聊天](/images/java/jiben.png)

还有一个问题:服务器说的话只能发给一个人。

解决办法:
修改TCPServerListener的实现:

java private ServerThread st = null;

            public TCPServerListener(ServerThread s)
            {
                    st =s ;
            }
1
并在actionPerformed中增加 st.pushMessage();

java

               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");
                   }
1
当然在ServerThread服务器线程类中,增加

java 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");
                   }
1
基于此我们的Server端如下:

java /* * 解释: / 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;
                }
            }
        }
    }

}

1
Client端如下:

java /* * 解释: / 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
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
结果:

1:之前的线程卡的问题,还没有解决,运行起来仍然感觉占用大量内存!!
2:为什么 第一个进入聊天系统的杨中科,会打印出三次呢? 当然后面的消息正常,当然三次的意思是聊天系统进入了三个人,都教授和Rocket. 所以肯定是那部分有问题待解决


![在这边有一个小问题,出现了三行打印](/images/java/服务器2.png)

![群发效果OK,服务器也没问题了](/images/java/基本满足题意.png)



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端不需要修正)

java

/* * 解释: / 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
![有问题的版本(但是基本满足题意)](/images/java/服务器3.png)

![不卡版本,且解决了登陆时候的多条打印问题](/images/java/居然不卡了.png)


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  引入了新的数据结构 CopyWriteArrayListLinkedBlockingQueue 线程阻塞类

5: 注意了
   5.1 线程的使用(threadRunnable),线程开启放置的位置,最后发现使用了多线程服务器客户端
   5.2 静态代码段static的使用
   5.3 ServerThread的静态变量的使用,配合上构造函数
   5.4 数据结构的使用




猛然发现一个特点:

java 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);
1
2

java vgOpen.addActivateListener(new IGCallBack()

            {

                    @Override
                    public void execute(int instance, int eventData, Object object)
                    {
                            youOpenfile(ootv);

                    }
            });
1
3

java //添加按钮事件

            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);
1
2
3
4
前三个都是针对gtk
awt的事件关闭监听,

java f = new JFrame(“客户端”); f.addWindowListener(new WindowAdapter()

                {
                    public void windowClosing(WindowEvent e)
                    {
                        System.exit(0);
                    }
                });
1
2
3
4
当然在JFrame可以采用:
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
复制代码
他们共同的特点是都采用匿名类,当做时间的响应函数或者回调函数,于是当前的网络编程的button  textfield也是采用匿名类来实现:

java 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
添加监听:

java

     tf.addActionListener(new TCPClientListener());
            bn.addActionListener(new TCPClientListener());

“`

注意匿名类的监听函数的匿名类对象实现回调

学习了匿名类的事件回调函数。