线程间有共享数据。如果没有共享数据,用模型 3b 就行。虽然我们应该把线程间的共享数据降到最低,但不代表没有;
共享的数据是可以修改的,而不是静态的常量表。如果数据不能修改,那么可以在进程间用 shared memory,模式 3 就能胜任;
提供非均质的服务。即,事件的响应有优先级差异,我们可以用专门的线程来处理优先级高的事件。防止优先级反转;
latency 和 throughput 同样重要,不是逻辑简单的 IO bound 或 CPU bound 程序;
利用异步操作。比如 logging。无论往磁盘写 log file,还是往 log server 发送消息都不应该阻塞 critical path;
能 scale up。一个好的多线程程序应该能享受增加 CPU 数目带来的好处,目前主流是 8 核,很快就会用到 16 核的机器了。
具有可预测的性能。随着负载增加,性能缓慢下降,超过某个临界点之后急速下降。线程数目一般不随负载变化。
多线程能有效地划分责任与功能,让每个线程的逻辑比较简单,任务单一,便于编码。而不是把所有逻辑都塞到一个 event loop 里,就像 Win32 SDK 程序那样。
用C#实现多线程TCP协议的服务器端程序:// <summary>
/// Tcp客户线程类(服务端),ThreadServerProcessor 线程产生的客户连接,用该线程读写
/// </summary>
public class ThreadClientProcessor
{
//Tcp连接实例
private TcpClient tcpClient
//消息框,本来想写日志用
private System.Windows.Forms.ListBox MessageList
private string Password//该连接登陆密码
private string Cmd1Echo
private string Cmd2Echo
private bool ClientLogOn//客户是否登陆
private bool TcpClose
public ThreadClientProcessor(){}
//构造函数,参数解释:Tcp客户,消息框,该服务密码(password命令后的参数) ,命令回应串 1,2 ******************
public ThreadClientProcessor(TcpClient client , ListBox listBox,string LogonText ,string cmd1echo,string cmd2echo)
{
ClientList.Add(this) //把当前实例加入一个列表中,方便以后控制
this.tcpClient=client
this.MessageList=listBox
this.Password=LogonText
this.Cmd1Echo=cmd1echo
this.Cmd2Echo=cmd2echo
this.ClientLogOn=false
this.TcpClose=false
}
public static char[] CmdSplit={' '}//读来的串由' ' 进行分离,命名+' '+参数
//public const string[] Cmd=new string[] { "password","cmd1","cmd2","echo","bye"}
//该函数由你自己写,这个只是给一个例子,
//功能:命令处理器,给个命令串,返回该命令处理结果,把命令和处理结果放在一个文本文件里,便于系统升级
public string TcpCmd(string s)
{
string result
try
{
string cmdarg=s.Trim()
string[] args=cmdarg.Split(CmdSplit)
string cmd=args[0].ToLower()
switch (cmd )
{
case "password" :
if (args.Length>1)
{
ClientLogOn= Password.Equals(args[1].Trim())
result=ClientLogOn? "登陆成功":"密码不正确,未登陆"
}
else result= "登陆时候,没有输入密码"
break
case "cmd1":
result=ClientLogOn?this.Cmd1Echo:"该命令无权执行,请先登陆"
break
case "cmd2":
result=ClientLogOn?this.Cmd2Echo:"该命令无权执行,请先登陆"
break
case "echo":
result=string.Format("服务器回应:\n {0}",s)
break
case "bye":
this.TcpClose=true
result="DisConnected"
break
default:
result="不可识别的命令"
break
}
}
catch
{
result="解析命令发生错误,你输入的是狗屁命令,TMD *^* "
}
return result
} //end cmd
//定义一个线程,该线程对应的函数是 void start()(不是Start())********************************
//一下程序主要是操作该线程
public System.Threading.Thread tcpClientThread
//启动客户连接线程 *************************************************************
public void Start()
{
tcpClientThread=new Thread(new ThreadStart(start))
tcpClientThread.Priority=ThreadPriority.BelowNormal
tcpClientThread.Start()
}
//断开该当前实例连接,终止线程 **************************************************************
public void Abort()
{
if (this.tcpClientThread!=null)
{
//tcpClientThread.Interrupt()
tcpClientThread.Abort()
//一定要等一会儿,以为后边tcpClient.Close()时候,会影响NetWorkStream的操作
Thread.Sleep(TimeSpan.FromMilliseconds(100))
tcpClient.Close()
}
}
//静态列表,包含了每个连接实例(在构造实例时候使用了 ArrayList.Add( object))
private static System.Collections.ArrayList ClientList=new ArrayList()
//断开所有的Tcp客户连接,静态方法*************************************************************
public static void AbortAllClient()
{
for(int j=0 j<ClientList.Countj++)
{
//从实例列表中取一个对象,转化为ThreadClientProcessor对象
ThreadClientProcessor o=(ThreadClientProcessor ) ClientList[j]
//调用ThreadClientProcessor 对象的停止方法
o.Abort()
}
//清除连接列表
ClientList.Clear()
}
//读写连接的函数,用于线程//*******************************************************************
private void start()
{
byte[] buf=new byte[1024*1024]//预先定义1MB的缓冲
int Len=0//流的实际长度
NetworkStream networkStream=tcpClient.GetStream()//建立读写Tcp的流
try
{
byte[] p=Encoding.UTF8.GetBytes(" 欢迎光临,请输入密码" )
//向Tcp连接写 欢迎消息
if (!this.ClientLogOn )
networkStream.Write(p,0,p.Length)
//开始循环读写tcp流
while (!TcpClose)
{
//如果当前线程是在其它状态,(等待挂起,等待终止.....)就结束该循环
if (Thread.CurrentThread.ThreadState!=ThreadState.Running)
break
//判断Tcp流是否有可读的东西
if ( networkStream.DataAvailable)
{
//从流中读取缓冲字节数组
Len=networkStream.Read(buf,0,buf.Length)
//转化缓冲数组为串
string cmd=Encoding.UTF8.GetString(buf,0,Len)
this.MessageList.Items.Add("客户机:"+cmd)
//处理该缓冲的串(分析命令),分析结果为res串
string res=TcpCmd(cmd)
//把命令的返回结果res 转化为字节数组
byte[] result=Encoding.UTF8.GetBytes(res)
//发送结果缓冲数组给客户端
networkStream.Write(result,0,result.Length)
this.MessageList.Items.Add("服务器回应:"+res)
}
else
{
//Thread.Sleep(TimeSpan.FromMilliseconds(200d))
//this.MessageList.Items.Add("客户机无命令")
//如果当前Tcp连接空闲,客户端没有写入,则当前线程停止200毫秒
Thread.Sleep(TimeSpan.FromMilliseconds(200d))
}
}
程序分Server和Client服务器端打开侦听的端口,一有客户端连接就创建两个新的线程来负责这个连接
一个负责客户端发送的信息(ClientMsgCollectThread 类),
另一个负责通过该Socket发送数据(ServerMsgSendThread )
Server.java代码如下:
/*
* 创建日期 2009-3-7
*
* TODO 要更改此生成的文件的模板,请转至
* 窗口 - 首选项 - Java - 代码样式 - 代码模板
*/
package faue.MutiUser
import java.io.BufferedReader
import java.io.IOException
import java.io.InputStreamReader
import java.io.PrintWriter
import java.net.ServerSocket
import java.net.Socket
/**
* 服务器端
*
* @author Faue
*/
public class Server extends ServerSocket {
private static final int SERVER_PORT = 10000
/**
* 构造方法,用于实现连接的监听
*
* @throws IOException
*/
public Server() throws IOException {
super(SERVER_PORT)
try {
while (true) {
Socket socket = super.accept()
new Thread(new ClientMsgCollectThread(socket), "getAndShow"
+ socket.getPort()).start()
new Thread(new ServerMsgSendThread(socket), "send"
+ socket.getPort()).start()
}
} catch (IOException e) {
e.printStackTrace()
}
}
public static void main(String[] args) throws IOException {
new Server()
}
/**
* 该类用于创建接收客户端发来的信息并显示的线程
*
* @author Faue
* @version 1.0.0
*/
class ClientMsgCollectThread implements Runnable {
private Socket client
private BufferedReader in
private StringBuffer inputStringBuffer = new StringBuffer("Hello")
/**
* 得到Socket的输入流
*
* @param s
* @throws IOException
*/
public ClientMsgCollectThread(Socket s) throws IOException {
client = s
in = new BufferedReader(new InputStreamReader(client
.getInputStream(), "GBK"))
}
public void run() {
try {
while (!client.isClosed()) {
inputStringBuffer.delete(0, inputStringBuffer.length())
inputStringBuffer.append(in.readLine())
System.out.println(getMsg(inputStringBuffer.toString()))
}
} catch (IOException e) {
//e.printStackTrace()
System.out.println(client.toString() + " is closed!")
}
}
/**
* 构造显示的字符串
*
* @param line
* @return
*/
private String getMsg(String line) {
return client.toString() + " says:" + line
}
}
/**
* 该类用于创建发送数据的线程
*
* @author Faue
* @version 1.0.0
*/
class ServerMsgSendThread implements Runnable {
private Socket client
private PrintWriter out
private BufferedReader keyboardInput
private StringBuffer outputStringBuffer = new StringBuffer("Hello")
/**
* 得到键盘的输入流
*
* @param s
* @throws IOException
*/
public ServerMsgSendThread(Socket s) throws IOException {
client = s
out = new PrintWriter(client.getOutputStream(), true)
keyboardInput = new BufferedReader(new InputStreamReader(System.in))
}
public void run() {
try {
while (!client.isClosed()) {
outputStringBuffer.delete(0, outputStringBuffer.length())
outputStringBuffer.append(keyboardInput.readLine())
out.println(outputStringBuffer.toString())
}
} catch (IOException e) {
//e.printStackTrace()
System.out.println(client.toString() + " is closed!")
}
}
}
}
客户端:
实现基于IP地址的连接,连接后也创建两个线程来实现信息的发送和接收
/*
* 创建日期 2009-3-7
*
*/
package faue.MutiUser
import java.io.BufferedReader
import java.io.IOException
import java.io.InputStreamReader
import java.io.PrintWriter
import java.net.Socket
/**
* 客户端
*
* @author Faue
*/
public class Client {
private Socket mySocket
/**
* 创建线程的构造方法
*
* @param IP
* @throws IOException
*/
public Client(String IP) throws IOException {
try {
mySocket = new Socket(IP, 10000)
new Thread(new ServerMsgCollectThread(mySocket), "getAndShow"
+ mySocket.getPort()).start()
new Thread(new ClientMsgSendThread(mySocket), "send"
+ mySocket.getPort()).start()
} catch (IOException e) {
//e.printStackTrace()
System.out.println("Server.IP:" + IP
+ " port:10000 can not be Connected")
}
}
public static void main(String[] args) throws IOException {
try {
new Client(args[0])
} catch (Exception e) {
System.out.println("输入的IP地址错误")
}
}
/**
* 该类用于创建接收服务端发来的信息并显示的线程
*
* @author Faue
* @version 1.0.0
*/
class ServerMsgCollectThread implements Runnable {
private Socket client
private BufferedReader in
private StringBuffer inputStringBuffer = new StringBuffer("Hello")
/**
* 得到Socket的输入流
*
* @param s
* @throws IOException
*/
public ServerMsgCollectThread(Socket s) throws IOException {
client = s
in = new BufferedReader(new InputStreamReader(client
.getInputStream(), "GBK"))
}
public void run() {
try {
while (!client.isClosed()) {
inputStringBuffer.delete(0, inputStringBuffer.length())
inputStringBuffer.append(in.readLine())
System.out.println(getMsg(inputStringBuffer.toString()))
}
} catch (IOException e) {
//e.printStackTrace()
System.out.println(client.toString() + " is closed!")
System.exit(0)
}
}
/**
* 构造输入字符串
*
* @param line
* @return
*/
private String getMsg(String line) {
return client.toString() + " says:" + line
}
}
/**
* 该类用于创建发送数据的线程
*
* @author Faue
* @version 1.0.0
*/
class ClientMsgSendThread implements Runnable {
private Socket client
private PrintWriter out
private BufferedReader keyboardInput
private StringBuffer outputStringBuffer = new StringBuffer("Hello")
/**
* 得到键盘的输入流
*
* @param s
* @throws IOException
*/
public ClientMsgSendThread(Socket s) throws IOException {
client = s
out = new PrintWriter(client.getOutputStream(), true)
keyboardInput = new BufferedReader(new InputStreamReader(System.in))
}
public void run() {
try {
while (!client.isClosed()) {
outputStringBuffer.delete(0, outputStringBuffer.length())
outputStringBuffer.append(keyboardInput.readLine())
out.println(outputStringBuffer.toString())
}
out.println("--- See you, bye! ---")
} catch (IOException e) {
//e.printStackTrace()
System.out.println(client.toString() + " is closed!")
System.exit(0)
}
}
}
}
如果对您有帮助,请记得采纳为满意答案,谢谢!祝您生活愉快!
vaela
欢迎分享,转载请注明来源:夏雨云
评论列表(0条)