基于C++ Builder 5的Socket组件实现的聊天程序(三)
2.创建客户端程序
(1)创建主窗体
选择“File | New Application”菜单命令创建一个新工程,该工程包括一个空白窗体,将窗体的“Name”属性改为“MainForm”,选择“File | Save Project As…”菜单命令,把Unit1.cpp保存为 ChatClientMain.cpp ,Project1.bpr 保存为 ChatClient.bpr。
剩下的和服务器的窗体创建方法差不多,我就不啰嗦了,我再把客户端的界面贴一下,大家应该都能理解。
别忘了,这次是TClientSocket组件而不是TServerSocket ,其他的组件都差不多。
窗体和组件的属性设置如下表:
组件 |
属性 |
属性值 |
Form1 |
FormStyle |
fsNormal |
Name |
MainForm | |
Caption |
客户端 | |
Label1 |
Caption |
聊天内容 |
Label2 |
Caption |
发送信息 |
Label3 |
Caption |
在线用户 |
Button1 |
Caption |
发送 |
Name |
SendButton 不是BtnToPer了,其实没有影响,名字无所谓 | |
Button2 |
Caption |
所有人 |
Name |
BtnToAll | |
Edit1 |
Name |
EdtSend |
ListBox1 |
Name |
ListUserBox |
Memo1 |
Name |
ChatMemo |
ClientSocket1 |
Port |
8999 |
ServerType |
stNonBlocking | |
MenuItem1 |
Name |
MenuConnect |
Caption |
登陆 | |
MenuItem2 |
Name |
MenuDisConnect |
Caption |
离开 | |
MenuItem3 |
Name |
MenuHelp |
Caption |
帮助 | |
MenuItem4 |
Name |
MenuAbout |
Caption |
关于 |
也许大家会问我的客户端的界面图片里只有“登陆”、“帮助”和“关于”三个菜单项啊,没有“离开”啊,等你登陆成功后就有“离开”了。
(2)设计代码
ChatClientMain.h头文件的代码如下:
//---------------------------------------------------------------------------
#ifndef ChatClientMainH
#define ChatClientMainH
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <Menus.hpp>
#include <ScktComp.hpp>
#include <ComCtrls.hpp>
//---------------------------------------------------------------------------
class TMainForm : public TForm
{
__published: // IDE-managed Components
TMemo *ChatMemo;
TListBox *ListUserBox;
TLabel *Label1;
TLabel *Label2;
TLabel *Label3;
TEdit *EdtSend;
TButton *SendButton;
TMainMenu *MainMenu1;
TClientSocket *ClientSocket1;
TMenuItem *MenuConnect;
TMenuItem *MenuDisConnect;
TStatusBar *StatusBar1;
TButton *BtnToAll;
TMenuItem *MenuHelp;
TMenuItem *MenuAbout;
// 同样,上面的代码是设计好窗体后自动生成的
// 下面的这些函数也应该不难理解
void __fastcall MenuConnectClick(TObject *Sender);
void __fastcall ClientSocket1Connect(TObject *Sender,
TCustomWinSocket *Socket);
void __fastcall ClientSocket1Read(TObject *Sender,
TCustomWinSocket *Socket);
void __fastcall ClientSocket1Error(TObject *Sender,
TCustomWinSocket *Socket, TErrorEvent ErrorEvent,
int &ErrorCode);
void __fastcall ListUserBoxClick(TObject *Sender);
void __fastcall BtnToAllClick(TObject *Sender);
void __fastcall SendButtonClick(TObject *Sender);
void __fastcall MenuSetClick(TObject *Sender);
void __fastcall MenuDisConnectClick(TObject *Sender);
void __fastcall ClientSocket1Disconnect(TObject *Sender,
TCustomWinSocket *Socket);
void __fastcall MenuHelpClick(TObject *Sender);
void __fastcall MenuAboutClick(TObject *Sender);
private: // User declarations
BOOL m_HasConnected;
AnsiString m_sHostAddress;
AnsiString m_sName;
AnsiString m_sIsChat;
AnsiString m_sSpeakTo;
AnsiString m_sPartTo;
AnsiString m_sPartFr;
AnsiString m_sSendInfo;
AnsiString m_sIsName;
public: // User declarations
__fastcall TMainForm(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TMainForm *MainForm;
//---------------------------------------------------------------------------
#endif
源文件ChatClientMain.cpp的代码如下:
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "ChatClientMain.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TMainForm *MainForm;
//---------------------------------------------------------------------------
__fastcall TMainForm::TMainForm(TComponent* Owner)
: TForm(Owner)
{
m_sHostAddress = "192.168.0.187"; // 服务端程序所在的机器的IP地址,这是我
// 的IP地址,其实IP地址设在这里不太人性
// 化,应该在客户端程序的界面层对服务器的// IP地址进行自由设置,这样当服务端程序放// 在别的机器上的时候就不用来这里改代码
// 了,大家可以自己实现一下,很简单的,弄// 一个文本框,让m_sHostAddress = 文本框
// ->Text 就行了
ClientSocket1->Active = false;
ClientSocket1->Address = m_sHostAddress;
ClientSocket1->Port = 8999; // 端口好也最好弄成上面IP地址那样的实现方法
m_sSpeakTo = "ALL";
m_sIsName = "NA";
m_sIsChat = "CT";
m_sPartTo = "#";
m_sPartFr = "$";
m_HasConnected = false;
TDateTime NowTime;
NowTime = Now();
StatusBar1->SimpleText = NowTime.DateTimeString();
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::MenuConnectClick(TObject *Sender)
{
InputQuery("请输入用户名","(不能包含#和$字符) ",m_sName);
while(m_sName.IsEmpty())
{
MessageBox(NULL,"用户名不能为空","",MB_OK);
InputQuery("请输入用户名","(不能包含#和$字符)",m_sName);
}
for (int i=1;i<m_sName.Length()+1;i++)
{
if (m_sName.SubString(i,1)=="#")
{
MessageBox(NULL,"用户名不合法,请重新输入","",MB_OK);
InputQuery("请输入用户名","(不能包含#和$字符)",m_sName);
}
else if(m_sName.SubString(i,1)=="$")
{
MessageBox(NULL,"用户名不合法,请重新输入","",MB_OK);
InputQuery("请输入用户名","用户名:",m_sName);
}
}
ClientSocket1->Open();
MenuConnect->Visible = false;
MenuDisConnect->Visible = true;
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::ClientSocket1Connect(TObject *Sender,
TCustomWinSocket *Socket)
{
Socket->SendText(m_sIsName+m_sName);
AnsiString sTemp;
sTemp.sprintf("%s登陆成功",m_sName);
m_HasConnected = true;
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::ClientSocket1Read(TObject *Sender,
TCustomWinSocket *Socket)
{
AnsiString sFr;
AnsiString sInfo;
AnsiString sType;
AnsiString sChat;
AnsiString sAddr;
int iPosFr,iPosWel;
int iIndex;
sInfo = Socket->ReceiveText();
sType = sInfo.SubString(1,2);
iPosFr = sInfo.Pos(m_sPartFr);
sFr = sInfo.SubString(3,iPosFr-3);
if (sInfo=="CLEAR")
{
ListUserBox->Items->Clear();
}
else
{
if (sType==m_sIsName)
{
if (sFr!=m_sName)
{
StatusBar1->SimpleText = sFr+"刚加入";
ChatMemo->Lines->Add(sFr+"刚加入");
}
ListUserBox->Items->Add(sFr);
}
else if (sType==m_sIsChat)
{
sChat = sFr+sInfo.SubString(iPosFr+1,
sInfo.Length()-iPosFr+1);
ChatMemo->Lines->Add(sChat);
}
else
{
ChatMemo->Lines->Add(sInfo);
}
}
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::ClientSocket1Error(TObject *Sender,
TCustomWinSocket *Socket, TErrorEvent ErrorEvent, int &ErrorCode)
{
ErrorCode = 0;
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::ListUserBoxClick(TObject *Sender)
{
if (m_HasConnected)
{
AnsiString sTemp;
if (m_sName == ListUserBox->Items->Strings[ListUserBox->ItemIndex])
{
MessageBox(NULL,"不能与自己交谈","",MB_OK);
}
else
{
MainForm->Caption = "与"+
ListUserBox->Items->Strings[ListUserBox->ItemIndex]+"交谈中";
m_sSpeakTo = ListUserBox->Items->Strings[ListUserBox->ItemIndex];
}
}
else
{
MessageBox(NULL,"对不起,请先登录","",MB_OK);
}
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::BtnToAllClick(TObject *Sender)
{
AnsiString sTemp;
m_sSpeakTo = "ALL";
sTemp = m_sIsChat+m_sName+m_sPartFr+m_sSpeakTo+m_sPartTo+EdtSend->Text;
if(!(ClientSocket1->Active))
{
MessageBox(NULL,"请先登陆","",MB_OK);
}
else
{
if (EdtSend->Text=="")
{
MessageBox(NULL,"发送内容不能为空","",MB_OK);
}
else
{
ClientSocket1->Socket->SendText(sTemp);
EdtSend->Clear();
StatusBar1->SimpleText = "发送完毕";
}
}
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::SendButtonClick(TObject *Sender)
{
AnsiString sTemp;
sTemp = m_sIsChat+m_sName+m_sPartFr+m_sSpeakTo+m_sPartTo+EdtSend->Text;
if (!(ClientSocket1->Active))
{
MessageBox(NULL,"请先登陆","",MB_OK);
}
else
{
if (EdtSend->Text=="")
{
MessageBox(NULL,"发送内容不能为空","",MB_OK);
}
else
{
if (m_sSpeakTo!="ALL")
{
ClientSocket1->Socket->SendText(sTemp);
ChatMemo->Lines->Add(m_sName+" 说:");
ChatMemo->Lines->Add(" "+EdtSend->Text);
EdtSend->Clear();
}
StatusBar1->SimpleText = "发送完毕";
}
}
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::MenuDisConnectClick(TObject *Sender)
{
if(ClientSocket1->Socket->Connected)
{
ClientSocket1->Socket->Close();
if (!(ClientSocket1->Active))
{
MenuConnect->Visible = true;
MenuDisConnect->Visible = false;
}
}
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::ClientSocket1Disconnect(TObject *Sender,
TCustomWinSocket *Socket)
{
ListUserBox->Items->Clear();
m_HasConnected = false;
ChatMemo->Clear();
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::MenuHelpClick(TObject *Sender)
{
MessageBox(NULL,"点“登陆”,输入用户名/r/n点“离开”,断开与服务器的连接",
"",MB_OK);
}
//---------------------------------------------------------------------------
void __fastcall TMainForm::MenuAboutClick(TObject *Sender)
{
MessageBox(NULL,
"作者:赵永康/r/n完成日期:2009年7月29日/r/n版本:5.0/r/nQQ:516996841",
"",MB_OK);
// MessageBox 中 ,在需要换行的后面加上 "/r/n" 即可
}
//---------------------------------------------------------------------------
参考着服务端程序,客户端程序很好理解的。