线程内部的socket处理方法,希望大家有什么想法都可以回帖,都有分的!

goldenfinger413 2003-08-03 12:56:02
目标:是这样的,目前我正在负责一个信息系统的服务器端程序开发,开发工具是BORLAND的C++ BUILDER6.0,系统平台是win2000 。服务器要完成与多个客户端的包文交互,同时可能有大文件传输存在[目前限制5m],服务器通过BDE与数据库交互。
方法:现在服务器端程序框架主要是:从TThread派生一个处理客户端请求的线程类,在线程类里面主要完成与客户端包文交互,操作数据库,接受发送文件等任务;采用TServerSocket侦听网络连接,一旦接收到连接请求,即开启一个线程,并将socket 的句柄传递进入线程,在线程内部使用socket的基础函数recv,send等来与客户端进行交互。send调用的时候没有做什么限制,是将整个流全部发送send出去,而recv循环调用,每次接受2048bytes,直到接受到自定义的包文结束标志为止,如果中间recv返回值为0,-1,则在循环内部sleep(可配置参数)一段时间后再调用recv,如果连续10次这样sleep()后还是没有接收到数据,则端开与客户端的连接结束线程。
问题:服务器同时对教小包文[1m以下]传输、处理好象看不出来有什么毛病,感觉还是比较流畅(有10台左右机器一起测试,在任务管理器中最多能看到过3个增加的线程)。但是服务器与客户端要接收或则发送的包文比教大的时候超过2m的时候,发现服务器cpu时间被这个客户端独占掉[有时候达100%],其他客户端的请求根本进入不了线程,只有等他们的处理完毕以后才能看到服务器处理其他客户端的请求。
目前线程就跟假线程一样,没有起到同时处理多个客户端请求的需求。 希望大家对上述处理方式提提改良的办法,随便发表发表意见都行。可重分想送!
...全文
40 19 打赏 收藏 转发到动态 举报
写回复
用AI写文章
19 条回复
切换为时间正序
请发表友善的回复…
发表回复
goldenfinger413 2003-08-06
  • 打赏
  • 举报
回复
to[googoler(古乐)]:在线程内部用Application->ProcessMessages(),我也试过了的。

谢谢大家关注。虽然目前我的问题还没有彻底解决,但是大家给了我很多建议,感谢大家!
googoler 2003-08-05
  • 打赏
  • 举报
回复
2 goldenfinger413

愧不敢当。

偶真是糊涂。
如真是因为recv循环调用造成CPU占用太高,在循环内部加入多个
Application->ProcessMessages();应该就可以了。

这样你的代码就不用太多改动。

googoler 2003-08-05
  • 打赏
  • 举报
回复
2 goldenfinger413

mail今天早上收到。下午有时间帮你看看!

老实说,BCB3时我用过TThread(BCB3的TThread好象是在访问VCL安全性有问题)。
以后的版本从没用过。

另想请教jishiping:
关于我写的例子:

Unit2.h
//------------------------------------------------------------
class TMyThread : public TThread
{
private:
int iOldHandle,iOldThreadID;
protected:
void __fastcall Execute();
void __fastcall SetCaption();
public:
int tt;
int j;
__fastcall TMyThread(bool CreateSuspended);
};
//-----------------------------------------------------
Unit2.cpp
//-----------------------------------------------------

__fastcall TMyThread::TMyThread(bool CreateSuspended)
: TThread(CreateSuspended)
{
}
//---------------------------------------------------------------------------
void __fastcall TMyThread::SetCaption()
{
Form1->Label1->Caption="Now In Thread "+IntToStr(j);
Form1->Invalidate();
}
//---------------------------------------------------------------------------
void __fastcall TMyThread::Execute()
{
iOldHandle=Handle;
iOldThreadID=ThreadID;
for(;j<tt;j++)
{
Synchronize(SetCaption);
Sleep(50);
}
}
//---------------------------------------------------------------------------

Unit1.cpp
//-----------------------------------------------------
TMyThread *ThreadA;
TMyThread *ThreadB;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------

void __fastcall TForm1::Button1Click(TObject *Sender)
{
ThreadA=new TMyThread(true);
ThreadA->Priority=tpNormal;
ThreadA->OnTerminate=ATerminate;
ThreadA->FreeOnTerminate=true;
ThreadA->j=0;
ThreadA->tt=100;
int iHA=ThreadA->Handle;
int iHC=ThreadA->ThreadID;
ThreadA->Resume();

ThreadB=new TMyThread(true);
ThreadB->Priority=tpNormal;
ThreadB->OnTerminate=BTerminate;
ThreadB->FreeOnTerminate=true;
ThreadB->Priority=tpLower;
int iHB=ThreadB->Handle;
int iHD=ThreadB->ThreadID;
ThreadB->j=0;
ThreadB->tt=200;
ThreadB->Resume();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ATerminate(TObject *Sender)
{
Caption="ThreadA Terminate";
}
//---------------------------------------------------------------------------
void __fastcall TForm1::BTerminate(TObject *Sender)
{
Caption="ThreadB Terminate";
}
//---------------------------------------------------------------------------

假设threada先启动,threadb启动时ThreadB->j等于100;
不知道是不是和tiddata有关,但是OnTerminate执行正确。

烦请看一看。
jishiping 2003-08-05
  • 打赏
  • 举报
回复
估计是你写的不对。下面是我给别人的一个例子,TServerSocket的属性ServerType的值为
stThreadBlocking。在它的事件OnGetThread里,建立线程。线程由TServerClientThread
派生。

class TForm1 : public TForm
{
__published: // IDE-managed Components
TServerSocket *ServerSocket1;
TOpenDialog *OpenDialog1;
TGroupBox *GroupBox1;
TLabel *Label1;
TEdit *SrvPort;
TUpDown *UpDown1;
TButton *ListenBtn;
TGroupBox *GroupBox2;
TLabel *Label2;
TEdit *IPAddr;
TLabel *Label3;
TEdit *ClientPort;
TButton *SendBtn;
void __fastcall ServerSocket1GetThread(TObject *Sender,
TServerClientWinSocket *ClientSocket,
TServerClientThread *&SocketThread);
void __fastcall FormDestroy(TObject *Sender);
void __fastcall SendBtnClick(TObject *Sender);
void __fastcall ListenBtnClick(TObject *Sender);
private: // User declarations
public: // User declarations
__fastcall TForm1(TComponent* Owner);
};

class SrvThread : public TServerClientThread
{
private:
UINT FTimeOut;
TWinSocketStream* WskStream;
protected:
void __fastcall ClientExecute();
public:
__fastcall SrvThread(TServerClientWinSocket*);

__property UINT TimeOut = { read=FTimeOut, write=FTimeOut };
};

class ClientThread : public TThread
{
private:
AnsiString File;
TClientSocket* ClientSocket;
TWinSocketStream* WskStream;
protected:
void __fastcall Execute();
public:
__fastcall ClientThread(AnsiString IPAddr,
WORD Port, AnsiString file);
};

__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormDestroy(TObject *Sender)
{
ServerSocket1->Active = false;
}
//---------------------------------------------------------------------------
//ServerSocket1 的 OnGetThread 事件的函数代码
void __fastcall TForm1::ServerSocket1GetThread(TObject *Sender,
TServerClientWinSocket *ClientSocket,
TServerClientThread *&SocketThread)
{
SocketThread = new SrvThread(ClientSocket);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ListenBtnClick(TObject *Sender)
{
ServerSocket1->Port = SrvPort->Text.ToInt();
ServerSocket1->Active = true;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::SendBtnClick(TObject *Sender)
{
int Port;
AnsiString Addr;

Addr = IPAddr->Text.Trim();
if (Addr.IsEmpty()) {
IPAddr->SetFocus(); return;
}
try {
Port = ClientPort->Text.ToInt();
}
catch(Exception& e) {
ShowMessage(e.Message);
ClientPort->SetFocus(); return;
}
if (OpenDialog1->Execute())
new ClientThread(Addr, Port,
OpenDialog1->FileName);
}
//-----------------------------------------------------------
__fastcall SrvThread::SrvThread(TServerClientWinSocket* socket)
: TServerClientThread(false,socket)
{
}
//---------------------------------------------------------
//示范程序里,收到的数据写到文件"test*.dat"
void __fastcall SrvThread::ClientExecute()
{
int nLen;
int nSize;
int hFile;
char buf[4096];
static int num=0;

TimeOut = 60000; //60秒
WskStream = new TWinSocketStream(
ClientSocket, TimeOut);

while(1) {
wsprintf(buf, "test%04x.dat",
num++);
if (!FileExists(buf)) {
hFile = FileCreate(buf);
if (hFile!=-1) break;
}
}
try {
DWORD dwTick = GetTickCount();
//先读取发送的文件长度
if (WskStream->WaitForData(
TimeOut)) {
nLen = WskStream->Read(
&nSize, 4);
if (nLen!=4) nSize = 0;
}
else
nSize = 0;
//然后读取文件数据
for(; nSize>0 && !Terminated;
nSize-=nLen) {
if (!WskStream->WaitForData(
5000)) {
if (GetTickCount()-dwTick
<TimeOut)
continue;
//在规定时间内没有收到数据,
//结束线程
break;
}
nLen = WskStream->Read(buf,
sizeof(buf));
dwTick = GetTickCount();
if (nLen <= 0) {
//读数据出错,这儿做一些
//处理
break;
}
//添加收到数据的处理,这儿写到
//文件 test*.dat 中 ...
FileWrite(hFile, buf, nLen);
}
if (ClientSocket->Connected)
ClientSocket->Close();
}
catch(Exception& e) {
MessageBox(0, e.Message.c_str(),
"Error", MB_ICONERROR);
}
FileClose(hFile); delete WskStream;
}
//---------------------------------------------------------------------------
__fastcall ClientThread::ClientThread(AnsiString IPAddr,
WORD Port, AnsiString file)
: TThread(true) //先将线程挂起,给线程初始化后再运行
{
File = file; WskStream = NULL;
ClientSocket = new TClientSocket(NULL);
ClientSocket->ClientType = ctBlocking;
ClientSocket->Host = IPAddr;
ClientSocket->Port = Port;
Resume(); //初始化后,让线程继续执行
}

void __fastcall ClientThread::Execute()
{
int nLen;
int hFile;
int nSize;
char buf[4096];

try {
hFile = -1;
ClientSocket->Active = true;
WskStream = new TWinSocketStream(
ClientSocket->Socket, 60000);
hFile = FileOpen(File, fmOpenRead);
if (hFile != -1) {
nSize = GetFileSize((HANDLE)
hFile, NULL);
//先发送文件长度
WskStream->Write(&nSize, 4);
//然后发送文件数据
for(; nSize>0; nSize-=nLen) {
nLen = min((int)sizeof(
buf), nSize);
nLen = FileRead(hFile, buf,
nLen);
if (nLen<=0) break;
WskStream->Write(buf, nLen);
}
}
ClientSocket->Active = false;
}
catch(Exception& e) {
MessageBox(0, e.Message.c_str(),
"Error", MB_OK|MB_ICONERROR);
}
delete WskStream; delete ClientSocket;
if (hFile!=-1) FileClose(hFile);
}
wjlsmail 2003-08-05
  • 打赏
  • 举报
回复
Study
goldenfinger413 2003-08-05
  • 打赏
  • 举报
回复
to[googoler] 谢谢你!
googoler 2003-08-05
  • 打赏
  • 举报
回复
2 goldenfinger413

刚看了你另外的贴子的回复,有两个网友说得应该很有道理。再加上jishiping大侠
也给你写了例子。我就不看你的代码了,不好意思可能也帮不到你。

没想到没帮上你,俺对TThread倒有一些认识。:(
googoler 2003-08-05
  • 打赏
  • 举报
回复
刚刚把上面代码改了一下:
二个线程同时启动了,运行也很正确。
unit1
void __fastcall TMyThread::SetCaption()
{
lblTemp->Caption="Now In Thread "+IntToStr(j);
Form1->Invalidate();
}
//---------------------------------------------------------------------------
public:
int tt;
int j;
TLabel *lblTemp;
unit2
ThreadA=new TMyThread(true);
ThreadA->Priority=tpNormal;
ThreadA->OnTerminate=ATerminate;
ThreadA->FreeOnTerminate=true;
ThreadA->lblTemp=Form1->Label1;
ThreadA->j=0;
ThreadA->tt=100;
int iHA=ThreadA->Handle;
int iHC=ThreadA->ThreadID;
ThreadA->Resume();

ThreadB=new TMyThread(true);
ThreadB->Priority=tpNormal;
ThreadB->OnTerminate=BTerminate;
ThreadB->FreeOnTerminate=true;
ThreadB->Priority=tpLower;
ThreadB->lblTemp=Form1->Label2;
int iHB=ThreadB->Handle;
int iHD=ThreadB->ThreadID;
ThreadB->j=0;
ThreadB->tt=200;
ThreadB->Resume();

---------------------------
用两个label OK,

用一个Label 时,两个线程也同时启动了,我上了视觉的当。
再将代码改成:
void __fastcall TMyThread::SetCaption()
{
lblTemp->Caption="Now In Thread "+IntToStr(j)+"Handle:"+IntToStr(iOldHandle);
Form1->Invalidate();
}
//---------------------------------------------------------------------------

昨天晚上就没想到,哎!



supwjhuLoveCjj 2003-08-04
  • 打赏
  • 举报
回复
大家踊跃回答呀,,我是不大了解,但是我也想学学。。。。高手进来。
goldenfinger413 2003-08-04
  • 打赏
  • 举报
回复
to[googoler]:有接收到我得EMAIL吗??
googoler 2003-08-04
  • 打赏
  • 举报
回复
我也糊涂了,从8点多一直调到现在。

MyThread的两个实例A和B,
A->i和B->i不在同一个地址空间中。

但就我写的代码而言,假设A线程先启动,B线程后启动。
当B线程启动时,B->i的值确实为A线程结束时的A->i的值。

水平所限,盼高手作答!
redrd 2003-08-04
  • 打赏
  • 举报
回复
怎么会独占CPU?
goldenfinger413 2003-08-04
  • 打赏
  • 举报
回复
希望大家继续关注!
googoler 2003-08-04
  • 打赏
  • 举报
回复
刚做了一个例子。

也就是说A,B两个线程其实是同一个线程类的实例。

如你所说的情况,在A线程执行完之前,B线程是不可能得到CPU的。
至于为什么:我想可能是这样:
假设MyThread的两个实例A和B。MyThread有一个成员 int i;还有一个
成员函数是对i进行读写的。如果A,B线程同时启动,由于A,B都可能对
i进行读写,那在MyThread内部又怎样来对i进行同步?所以A,B线程是绝
对不可以同时启动的。在我的例子中,在B线程启动时,变量i的值已是A
线程结束时i的值。这也说明TThread的不安全,在这方面,_beginthreadex
是最安全的。你可以看看线程的局部存储(TLS)。
也可能还有一些其它的原因,也可能不是上述原因,纯属个人猜测。

建议你用_beginthreadex或CreateThread对你原来的线程类进行改写。

googoler 2003-08-04
  • 打赏
  • 举报
回复
老实说我也怀疑你的线程是否有问题。线程是分时间片的,好象是以20ms为单位。

又看了一下,也就是说A,B两个线程其实是同一个线程类的实例。

晕,彻底晕了,BCB的线程类也可以这样用吗?

老实说早期的BCB我还用过TThread(早期的BCB的TThread好象是有问题的),这几年都没用过。我更习惯用CreateThread。

如果一定要封装在类里面的话,可不可以也考虑用_beginthreadex。

goldenfinger413 2003-08-03
  • 打赏
  • 举报
回复
http://expert.csdn.net/Expert/topic/2103/2103323.xml?temp=.8589136
goldenfinger413 2003-08-03
  • 打赏
  • 举报
回复
其实独占CPU,主要是在循环接受大文件和发送大文件的时候。这里能否在循环内部,释放CPU,以分配CPU响应其他客户端?要怎样实现呢??希望大家赐教!
googoler 2003-08-03
  • 打赏
  • 举报
回复
试试 CreateEvent,WaitForSingleObject(WaitForSingleObjectEx),
WaitForMultipleObjects(WaitForMultipleObjectsEx);
goldenfinger413 2003-08-03
  • 打赏
  • 举报
回复
这里的人气为什么这么低????

1,317

社区成员

发帖
与我相关
我的任务
社区描述
C++ Builder 网络及通讯开发
社区管理员
  • 网络及通讯开发社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧