线程与进程(中)

用vc.net做的一个聊天程序,tcp的

这是SocketServer.h//服务端
using namespace System;

using namespace System::Net;
using namespace System::Net::Sockets;

using namespace System::Threading; // 程序中要创建线程

 

ref class SocketServer
{
private:
 array<Socket^>^ mySocket; // 与客户端会话的socket
 Socket^ listenerSocket; // 用于监听的socket
 array<bool>^ IsRun; // 服务端的运行状态
 array<Thread^>^ myThread; // 监听接收数据的一组线程
 Thread^ listenThread; // 监听客户端连接的线程
 int MAXNUM_CONNECTION; // 定义最大连接数

 String^ localIPStr; // 本地主机的IP地址
 unsigned short localPort;    // 本地主机的端口号
public:
 SocketServer(String^ ServerIP,unsigned short ServerPort,int MaxConnNum)
 {
  localIPStr=ServerIP;
  localPort=ServerPort;
  MAXNUM_CONNECTION=MaxConnNum;

  myThread=gcnew array<Thread^>(MAXNUM_CONNECTION);
  mySocket=gcnew array<Socket^>(MAXNUM_CONNECTION);
  IsRun=gcnew array<bool>(MAXNUM_CONNECTION);

  // create the socket
  listenerSocket = gcnew Socket( AddressFamily::InterNetwork,
          SocketType::Stream,
          ProtocolType::Tcp   );

  // bind the listening socket to the port
  IPAddress^ localIP=IPAddress::Parse(localIPStr);
  EndPoint^ EPhost=gcnew IPEndPoint(localIP,localPort);
  listenerSocket->Bind( EPhost );
  listenerSocket->Listen(0); // 开始监听

  listenThread=gcnew
   Thread(gcnew ThreadStart(this,&SocketServer::Listen));
  listenThread->Start();//启动监听连接的线程
 }

 // 循环监听连接,如果有连接则创建会话的socket和接收数据的线程
 void Listen()
 {
  while(true)
  {
   for(int i=0;i<MAXNUM_CONNECTION;i++)
   {
    if(!IsRun[i])// 某个编号的线程还没有创建或已终止,则可以创建该线程
    {
     mySocket[i]=listenerSocket->Accept(); // 接受tcp连接请求
     IPEndPoint^ remoteEP=(IPEndPoint^)(mySocket[i]->RemoteEndPoint); // 获得远程IP终端,必须强制转换为IPEndPoint^后才能得到IP和Port
     String^ remoteHostInfo=L"接受来自【"+remoteEP->Address->ToString()+":"+remoteEP->Port.ToString()+"】的连接请求!";
     Console::WriteLine(remoteHostInfo);
     IsRun[i]=true;
     ParameterizedThreadStart^ threadStart=gcnew ParameterizedThreadStart(this,&SocketServer::RecvData);
     myThread[i]=gcnew Thread(threadStart);
     myThread[i]->Start(i);//启动线程,并给线程传递一个参数
    }
   }
  }
 }

 // 接收数据,ind表示某个线程号(0——MAXNUM_CONNECTION-1)
    Void RecvData(Object^ ind) // 注意使用委托ParameterizedThreadStart定义线程时,函数RecvData的参数必须是Object^类型的。这里如果参数ind若定义为int则错误。
 { 
  int index=Convert::ToInt32(ind); // 类型转换
   array<Byte>^ bytesRecv = gcnew array<Byte>(1024);
   String^ dataStr=String::Empty; // 接收数据的字符创表示
   int bytes=0; // 接收到的数据的字节数
   array<String^>^ tokens;

   String^ remoteHostInfo;
   while(true)//Socket处于可读状态则一直循环
   {
    try
    {
     bytes=mySocket[index]->Receive(bytesRecv); // 接收数据
     this->DisplayPendingByteCount(mySocket[index]);
    }
    catch(SocketException^ ex)
    {
     Console::WriteLine("----"+ex->Message+"----");
     break;
    }
    dataStr=System::Text::Encoding::UTF8->GetString(bytesRecv,0,bytes);

    //String^ spt="|";
    //tokens=dataStr->Trim()->Split(spt->ToCharArray());

    IPEndPoint^ localEP=(IPEndPoint^)(mySocket[index]->LocalEndPoint); // 获得本地IP终端,必须强制转换为IPEndPoint^后才能得到IP和Port
    IPEndPoint^ remoteEP=(IPEndPoint^)(mySocket[index]->RemoteEndPoint); // 获得远程IP终端,必须强制转换为IPEndPoint^后才能得到IP和Port
    remoteHostInfo=L"本地端口【"+localEP->Port.ToString()+"】获得来自["+remoteEP->Address->ToString()+":"+remoteEP->Port.ToString()+"]的消息:"+ dataStr;
    Console::WriteLine(remoteHostInfo);

    分析接收到的数据(含控制码Chat、Exit等),可自己定义更多一些
    //if(tokens[0]=="Chat")
    //{
    // Console::WriteLine(remoteHostInfo);
    //}
    //else if(tokens[0]=="Exit")
    //{
    // IsRun[index]=false;
    // Console::WriteLine(remoteHostInfo);
    // mySocket[index]->Shutdown(Sockets::SocketShutdown::Both); // 禁止发送和接收
    // mySocket[index]->Close(); // 关闭mySocket连接并释放资源
    //}

    // 转发某个客户端的信息给所有其他客户端
    array<Byte>^ msg=System::Text::Encoding::UTF8->GetBytes(remoteHostInfo);
    for(int i=0;i<MAXNUM_CONNECTION;i++)// 发消息给所有客户端
    {
     if(i!=index)
     {
      if(IsRun[i])
       mySocket[i]->Send(msg);
     }
    }
   }
   // 关闭与客户端的连接
   IPEndPoint^ remoteEP=(IPEndPoint^)(mySocket[index]->RemoteEndPoint); // 获得远程IP终端,必须强制转换为IPEndPoint^后才能得到IP和Port
   remoteHostInfo=L"与远程主机["+remoteEP->Address->ToString()+":"+remoteEP->Port.ToString()+"]断开连接!";
   Console::WriteLine(remoteHostInfo);
   IsRun[index]=false;
   mySocket[index]->Shutdown(Sockets::SocketShutdown::Both); // 禁止发送和接收
   mySocket[index]->Close(); // 关闭mySocket连接并释放资源

   // 结束对应的线程
   this->myThread[index]->Abort();
   this->IsRun[index]=false; // 修改对应标记
  }

 void DisplayPendingByteCount( Socket^ s )
 {
  array<Byte>^ outValue = BitConverter::GetBytes( 0 );

  // Check how many bytes have been received.
  s->IOControl( IOControlCode::DataToRead, nullptr, outValue );

  UInt32 bytesAvailable = BitConverter::ToUInt32( outValue, 0 );
  Console::Write( "server has {0} bytes pending,",
   bytesAvailable );
  Console::WriteLine( "Available property says {0}.",
   s->Available );
  return;
 }


};

// SocketServer.cpp: 主项目文件。

// CreatesSocket.cpp: 主项目文件。
//下面的代码示例会创建套接字,连接到服务器并使用 Poll 检查套接字的状态。
#include "stdafx.h"
#include "SocketServer.h"
using namespace System;

int main(array<System::String ^> ^args)
{
 String^ localIPStr=Dns::GetHostByName(Dns::GetHostName())->AddressList[0]->ToString();//"127.0.0.1"; // 本地主机的IP地址
 unsigned short localPort=1002;    // 本地主机的端口号
 const int maxConnectNum=10; //最大连接数
 
 SocketServer^ Server;

 Server=gcnew SocketServer(localIPStr,localPort,maxConnectNum);
 
 Console::WriteLine("服务器已就绪,正在监听客户端的连接请求...");
 
 return 0;
}

 

以下是客户端,在自己机子上实验的,所以ip是一个

// CreatesSocket.cpp: 主项目文件。
//下面的代码示例会创建套接字,连接到服务器并使用 Poll 检查套接字的状态。
#include "stdafx.h"

using namespace System;

using namespace System::Net;
using namespace System::Net::Sockets;

int main(array<System::String ^> ^args)
{
 //Creates the Socket for sending data over TCP.
 Socket^ s = gcnew Socket( AddressFamily::InterNetwork,
        SocketType::Stream,
        ProtocolType::Tcp );

 String^ remoteIPStr="127.0.0.1"; // 远程主机的IP地址
 unsigned short remotePort=1002;    // 远程主机的端口号
 //IPAddress^ remoteIP=IPAddress::Parse(remoteIPStr);
 IPAddress^ remoteIP=Dns::GetHostByName(Dns::GetHostName())->AddressList[0]; // 远程主机的IP地址
 EndPoint^ EPhost=gcnew IPEndPoint(remoteIP,remotePort);
 
 // Connects to host using IPEndPoint.
 s->Connect( EPhost );
 if ( !s->Connected )
 {
  Console::WriteLine("Unable to connect to host");
 }
 // Use the SelectWrite enumeration to obtain Socket status.
 if ( s->Poll( -1, SelectMode::SelectWrite ) )
 {
  Console::WriteLine( "This Socket is writable." );
  
  String^ msgStr="大家好才是真的好!";
  while(true)
  {
   if(s->Connected==false)
    {
     Console::WriteLine( "与服务器的连接已经关闭!" );
     break;
    }
   Console::Write("请输入要发送的消息:");
   msgStr=Console::ReadLine();
   if(msgStr=="Quit")
    break;
   array<Byte>^ msg=System::Text::Encoding::UTF8->GetBytes(msgStr);
   try
   {
    s->Send(msg);
   }
   catch(Exception^ ex)
   {
    Console::WriteLine( "发送失败!" );
    if(s->Connected==false)
    {
     Console::WriteLine( "与服务器的连接已经关闭!" );
     break;
    }
   }
  }
 }
 else if ( s->Poll(  -1, SelectMode::SelectRead ) )
 {
  Console::WriteLine( "This Socket is readable." );
 }
 else if ( s->Poll(  -1, SelectMode::SelectError ) )
 {
  Console::WriteLine("This Socket has an error.");
 }


 s->Close();// 关闭socket连接并释放资源

 Console::ReadLine();
 return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值