如何用.net Remoting实现一个客户端需要连接多个服务器端

如何用.net Remoting实现一个客户端需要连接多个服务器端,第1张

1、使用socekt通信一般步骤

1)服务器端:socker()建立套接字,绑定(bind)并监听(listen),用accept()等待客户端连接。

2)客户端:socker()建立套接字,连接(connect)服务器,连接上后使用send()和recv(),在套接字上写读数据,直至数据交换完毕,closesocket()关闭套接字。

3)服务器端:accept()发现有客户端连接,建立一个新的套接字,自身重新开始等待连接。该新产生的套接字使用send()和recv()写读数据,直至数据交换完毕,closesocket()关闭套接字。

2、多个客户端同时连接在一个服务器上

这时候服务器端应该使用多线程,每连接上一个客户端就给该客户端开启一个线程。监听端口的时候也要单独开一个线程、不然会阻塞主线程。这样做有一个明显的缺点,就是有N个客户端请求连接时,就会有N个线程,对程序的性能和计算机的性能影响很大,可以使用线程池进行管理。

使用线程池的好处:主要用于减少因频繁创建和销毁线程带来开销,因此那些经常使用且执行时间短的线程需要用线程池来管理。

3、C#版代码如下

服务器端代码:

[html] view plain copy

using System

using System.Collections.Generic

using System.Linq

using System.Text

using System.Net

using System.Net.Sockets

using System.Threading

using System.IO

namespace SockServer

{

class Program

{

static void Main(string[] args)

{

try

{

//把ip地址转换为实例

IPAddress ipa = IPAddress.Parse("127.0.0.1")

//监听端口8001

TcpListener mylsn = new TcpListener(ipa, 8001)

//开启监听

mylsn.Start()

//输出监听成功的信息

Console.WriteLine("在8001启动服务,等待连接")

//等待处理接入连接请求

while (true)

{

Socket mysock = mylsn.AcceptSocket()

Console.WriteLine("有连接,连接来自" + mysock.RemoteEndPoint)

work w = new work()

w.mysock = mysock

w.mylsn = mylsn

Thread t = new Thread(new ThreadStart(w.main))

t.Start()

}

}

catch

{ }

finally

{ }

}

}

class work

{

public Socket mysock

public TcpListener mylsn

public void main()

{

//接收客户端消息

byte[] data = new byte[100]

mysock.Receive(data)

string rt = System.Text.UTF8Encoding.UTF8.GetString(data)

Console.WriteLine(rt)

//给客户端发消息

mysock.Send(UTF8Encoding.UTF8.GetBytes("server callback"))

//释放资源

mysock.Close()

mylsn.Stop()

}

}

}

客户端代码:

[html] view plain copy

using System

using System.Collections.Generic

using System.Linq

using System.Text

using System.Net

using System.Net.Sockets

using System.Threading

using System.IO

namespace SockClient

{

class Program

{

public static void Main()

{

//新建客户端套接字

TcpClient tclient = new TcpClient()

//连接服务器

tclient.Connect("127.0.0.1", 8001)

Console.WriteLine("输入要发送的消息")

//读入要传输的字符

string str = Console.ReadLine()

//得到流

Stream stm = tclient.GetStream()

//发送字符串

ASCIIEncoding asen = new ASCIIEncoding()

byte[] data = asen.GetBytes(str)

stm.Write(data, 0, data.Length)

//接受服务器返回的消息

byte[] back = new byte[100]

int k = stm.Read(back, 0, 100)

//输出服务器返回的消息

for (int i = 0i <ki++)

{

Console.Write(Convert.ToChar(back[i]))

}

//关闭连接

tclient.Close()

}

}

}

服务器在调用listen和accept后,就会阻塞在accept函数上,accpet函数返回后循环调用accept函数等待客户的TCP连接。如果这时候又大量的用户并发发起connect连接,那么在listen有队列上限(最大可接受TCP的连接数)的情况下,有多少个connect会成功了。试验证明,当连接数远远高于listen的可连接数上限时,客户端的大部分TCP请求会被抛弃,只有当listen监听队列空闲或者放弃某个连接时,才可以接收新的连接,那么我们应该如何来避免这种情况出现?

分析:

(一)客户端

客户端运行初期完成所设定的一定量的socket创建和相应的处理线程的创建,然后使用条件变量来完成线程同步,直到最后一个线程创建完成,才向所有线程发出广播通知,让所有线程并发调用connect,连接成功则关闭连接,失败则返回,如下代码所示。

socket创建和线程创建:

int testCount=300//并发用户数

/*

每个进程需要自己独立的栈空间,linux下默认栈大小是10M,在32位的机子上一个进程需要4G的内存空间,去掉自己的栈空间全局程序段空间,一般只有3G内存可以用,创建线程时就需要从这3G的空间中分配10M出来,所以最多可以分配300个线程。当然这里还可以使用多个进程,每个进程300个线程的方式来进一步扩大并发量。

*/

int sockfd[testCount]

pthread_t ntid[testCount]

bzero(&servaddr,sizeof(servaddr))

servaddr.sin_family=AF_INET

servaddr.sin_port=htons(SERVER_PORT)

inet_pton(AF_INET,argv[1],&servaddr.sin_addr)

int testCaseIndex=0

for(testCaseIndex=0testCaseIndex<testCounttestCaseIndex++)

{

sockfd[testCaseIndex]=socket(AF_INET,SOCK_STREAM,0)

//为每个并发客户端创建一个socket

if(sockfd[testCaseIndex]==-1)

{

printf("socket established error: %s\n",(char*)strerror(errno))

return -1

}

if( pthread_create(&ntid[testCaseIndex],NULL,handleFun,&sockfd[testCaseIndex])!=0)

{

printf("create thread error :%s\n",strerror(errno))

return -1

}

//为每个并发客户端创建一个线程来执行connect

}

printf("%d client has initiated\n",testCaseIndex)

并发客户端的线程实现:线程阻塞在条件变量上(只有条件满足了并且发起唤醒动作,线程才开始执行)。

int sockfd=*((int*)arg)

{

pthread_cond_wait(&cond,&mut)

//在条件变量上等待条件满足!

//阻塞返回后立即解锁,防止互斥量加锁带来的阻塞

pthread_mutex_unlock(&mut)

int conRes=0

conRes=connect(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr))

//线程执行connect连接,每个线程在接到唤醒信号后,才可以执行该语句,来模拟多个线程的并发调用。

if(conRes==-1)

{

printf("connect error: %s\n",strerror(errno))

return 0

}

}

当条件满足时,唤醒阻塞在条件变量上的线程:

while(1)

{

sleep(2)

pthread_cond_broadcast(&cond)//在所有线程创建完成后才进行唤醒。

服务器端可以采用多线程处理客户请求,例如:

package threadPool

import java.io.BufferedReader

import java.io.IOException

import java.io.InputStream

import java.io.InputStreamReader

import java.io.OutputStream

import java.io.PrintWriter

import java.net.ServerSocket

import java.net.Socket

import java.util.concurrent.ExecutorService

import java.util.concurrent.Executors

public class ThreadPoolServer {

private int port = 8000

private ServerSocket serverSocket

private ExecutorService executorService //线程池

private final int POOL_SIZE = 4 //单个CPU时线程池中的工作线程数目

public ThreadPoolServer() throws IOException{

serverSocket = new ServerSocket(port)

//创建线程池

//Runtime 的availableProcessors()方法返回当前系统CPU的数目

//系统CPU越多,线程池中的工作线程数目越多

executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()*POOL_SIZE)

System.out.println("服务器已启动!!")

}

public void service(){

while(true){

Socket socket = null

try{

socket = serverSocket.accept()

executorService.execute(new Handler(socket))

}catch(IOException e){

e.printStackTrace()

}

}

}

public static void main(String[] args) {

try {

new ThreadPoolServer().service()

} catch (IOException e) {

e.printStackTrace()

}

}

}

class Handler implements Runnable {

private Socket socket

public Handler(Socket socket) {

this.socket = socket

}

private PrintWriter getWriter(Socket socket) throws IOException {

OutputStream socketOut = socket.getOutputStream()

return new PrintWriter(socketOut, true)

}

private BufferedReader getReader(Socket socket) throws IOException {

InputStream socketIn = socket.getInputStream()

return new BufferedReader(new InputStreamReader(socketIn))

}

public String echo(String msg) {

return "echo:" + msg

}

@Override

public void run() {

try {

System.out.println("New connection accepted:" + socket.getInetAddress() + ":" + socket.getPort())

BufferedReader br = getReader(socket)

PrintWriter pw = getWriter(socket)

String msg = null

while ((msg = br.readLine()) != null) {

System.out.println(msg)

pw.println(echo(msg))

if (msg.equals("bye")) {

break

}

}

} catch (IOException e) {

e.printStackTrace()

} finally {

try {

if (socket != null)

socket.close()

} catch (IOException e) {

e.printStackTrace()

}

}

}

}


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

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

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

发表评论

登录后才能评论

评论列表(0条)

    保存