什么是多线程并发服务器

什么是多线程并发服务器,第1张

有多个 CPU 可用。单核机器上多线程的优势不明显。

线程间有共享数据。如果没有共享数据,用模型 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


欢迎分享,转载请注明来源:夏雨云

原文地址:https://www.xiayuyun.com/zonghe/576707.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-07-05
下一篇2023-07-05

发表评论

登录后才能评论

评论列表(0条)

    保存