用Delphi编写局域网中C/S型聊天程序中原工学院计算机教研室夏敏捷Internet盛行的今天,网上聊天QQ已成为一种时尚。
QQ是深圳腾讯公司的一个网络实时通讯软件,在国内拥有大量的用户群。
但QQ必须连接上互联网登陆到腾讯的服务器才能使用。
所以我们可以自己写一个在局部网里面使用的自己的QQ。
QQ使用的是UDP协议,这是一种无连接协议,即通信双方不用建立连接就可以发送信息,所以效率比较高。
Delphi本身自带的FastNEt公司的NMUDP控件就是一个UDP协议的用户数据报控件。
这里我们编写的局域网上实现聊天的程序采用的是TCP协议,TCP 是一种面向连接协议,具有较高的可靠性。
在Delphi中利用ClientSocket、ServerSocket这两种控件(Internet面板上),可以轻松的编写出C/S聊天程序,以下的程序在Delphi 6+winXP 中通过。
一、原理首先建立客户与主机的连接,把要发送的文字信息写进字符串中,然后ClientSocket组件的Socket..sendtext方法把文本发送出去。
当主机有数据接收时,触发它的ServerSocket 组件onClientRead事件,我们在这里用socket.ReceiveText接收字符信息,然后显示出来。
主机可以通过Socket..sendtext方法再将此用户信息转发给所有用户或单独的用户,在数据到达客户端后触发客户端OnRead事件,接受主机的数据。
为了区分不同的信息(新用户信息、昵称、交谈、用户离开)设计了以下协议格式:1.发送添加的新用户信息的格式:“添加用户”@用户的昵称^它的IP@用户的昵称^它的IP@……@此信息中将所有的用户的昵称和它的IP均包含在其中,新用户是最后一个。
2.发送昵称信息的格式:“昵称”@用户的昵称此信息用于在客户和服务器建立连接后,发送用户的昵称给服务器。
而用户的IP 地址可以通过(socket.RemoteAddress)得出。
3.发送文本对话信息的格式:“交谈”@to用户的昵称@交谈的内容4.发送离开信息的格式:“离开”@离开的用户的昵称二、客户端:新建一个工程,在Internet面板拖一个ClientSocket控件到窗口,然后依次放上一个EDIT, 一个ListBox和一个RichEdit,两个按钮,一个状态条StatusBar1。
当用户点击“连接主机”按钮时,用户输入服务器主机的IP及紧接着自己的昵称。
procedure TForm1.Edit1KeyDown(Sender: TObject; var Key: Word;Shift: TShiftState); beginif key=13 then //最好加本地IPbeginif listbox1.itemindex>=0 thenbeginclientsocket1.Socket.SendText('交谈@'+'to'+listBox1.Items.strings[listbox1.itemindex]+'@ '+edit1.Text );edit1.text:='';endelseshowmessage('请选择交谈对象');end;end;procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);beginclientsocket1.Active :=false;clientsocket1.close;end;procedure TForm1.Button1Click(Sender: TObject);var str1:string;beginstr1:=inputbox('建立连接','请输入IP','127.0.0.1');if trim(str1)<>'' thenbeginclientsocket1.Port:=5555;//server portclientsocket1.host:=str1;tryclientsocket1.active:=true;str1:=inputbox('建立连接','请输入昵称','泡泡');clientsocket1.Socket.SendText('昵称@'+str1);exceptshowmessage('连接失败');end;end;end;procedure TForm1.Button2Click(Sender: TObject);beginclose;end;procedure TForm1.ClientSocket1Connect(Sender: TObject;Socket: TCustomWinSocket); beginStatusBar1.SimpleText:='连接成功';end;procedure TForm1.ClientSocket1Read(Sender: TObject; Socket: TCustomWinSocket);var str1,name,str2,nei_rong:string;n,i:integer;beginstr1:=socket.ReceiveText;str2:=copy(str1,1,pos('@',str1)-1);if str2='添加用户' thenbeginlistbox1.Clear;//清除已有名单str1:=copy(str1,pos('@',str1)+1,length(str1)-pos('@',str1));while str1<>'' do //建立新名单beginname:=copy(str1,1,pos('@',str1)-1);//昵称^IPlistbox1.Items.Add(copy(name,1,pos('^',name)-1));str1:=copy(str1,pos('@',str1)+1,length(str1)-pos('@',str1));end;RichEdit1.Lines.Add(name+'进入了');end;if str2='交谈' then //交谈@xmj@hellobeginstr2:=copy(str1,pos('@',str1)+1,length(str1)-pos('@',str1));name:=copy(str2,1,pos('@',str2)-1);nei_rong:=copy(str2,pos('@',str2)+1,length(str2)-pos('@',str2));RichEdit1.Lines.Add(name+'对你说'+nei_rong);end;if str2='离开' thenbeginn:= pos('@',str1);name:=copy(str1,n+1,length(str1)-n);for i:=0 to listbox1.items.count-1 dobeginif listbox1.items.strings[i]=name thenlistbox1.items.delete(i);end;end;end;end.三、服务器端:新建一个工程,在Internet面板拖一个ServerSocket控件到窗口,然后依次放上一个EDIT, 一个ListView(ViewStyle属性为vsReport,表格型)和一个RichEdit,一个状态条StatusBar1。
procedure TForm1.FormCreate(Sender: TObject);beginserversocket1.Active :=true;RichEdit1.Lines.add(datetostr(date)+' '+timetostr(time)+'==>'+'聊天室服务器启动了');StatusBar1.SimpleText:='聊天室服务器启动了';end;procedure TForm1.ServerSocket1ClientRead(Sender: TObject;Socket: TCustomWinSocket);var s,str,name,nei_rong:string;i,n:integer;all_name,ni_cheng:string;newitem:TListItem;begins:=Socket.ReceiveText; //接收字符串n:=pos('@',s);if n>0 thenbeginstr:=copy(s,1,n-1);if str='交谈' then //格式:"交谈"@"to昵称"@"内容"beginstr:=copy(s,n+1,length(s)-n);name:=copy(str,3,pos('@',str)-3); //交谈的对象昵称nei_rong:=copy(str,pos('@',str)+1,length(str)-pos('@',str));//交谈的内容for i:=0 to ListView1.Items.count-1 do //根据发言者IP找发言者昵称beginif socket.RemoteAddress=ListView1.Items[i].subitems[0] thenbeginni_cheng:= ListView1.Items[i].caption;RichEdit1.Lines.add(ni_cheng+'对'+name+'说'+nei_rong); end;end;for i:=0 to ListView1.Items.count-1 do //根据交谈的对象昵称找交谈的对象IPbeginif name=ListView1.Items[i].caption thenname:=ListView1.Items[i].subitems[0]; //subitems[0]存放IPend;for i:=0 to ListView1.Items.count-1 do //实现私聊beginif ServerSocket1.Socket.Connections[i].RemoteAddress=name thenServerSocket1.Socket.Connections[i].SendText('交谈@'+ni_cheng+'@'+nei_rong);end;end;if str='昵称' then //昵称@xmjbeginall_name:='';newitem:=ListView1.Items.insert(ListView1.Items.count);newitem.caption:=copy(s,pos('@',s)+1,length(s)-pos('@',s));newitem.SubItems.Add(socket.RemoteAddress);newitem.SubItems.Add(socket.RemoteHost);RichEdit1.Lines.add(newitem.caption+'进来了');for i:=0 to ListView1.Items.count-1 doall_name:=all_name+ListView1.Items[i].Caption+'^'+ListView1.Items[i].subitems[0]+'@';for i:=0 to ListView1.Items.count-1 do //广播新进入用户beginServerSocket1.Socket.Connections[i].SendText('添加用户@'+all_name);end;end;end;end;procedure TForm1.ServerSocket1ClientDisconnect(Sender: TObject; Socket: TCustomWinSocket); //用户离开触发var i,j,k:integer;ni_cheng:string;beginfor i:=0 to ListView1.Items.count-1 do //ListView1中查找离开用户beginif socket.RemoteAddress=ListView1.Items[i].subitems[0] thenbeginni_cheng:= ListView1.Items[i].caption; //根据发言者IP找发言者昵称RichEdit1.Lines.add(ni_cheng+'离开了');Listview1.Items.Item[i].Delete(); break;end;end;for j:=0 to ListView1.items.count-1 do //广播离开用户ServerSocket1.Socket.Connections[j].SendText('离开@'+ ni_cheng);end;procedure TForm1.Edit1KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); var i:integer;beginif key=13 thenbeginfor i:=0 to ListView1.items.count-1 do //广播用户beginServerSocket1.Socket.Connections[i].SendText('交谈@'+'all'+'@'+edit1.Text );edit1.text:='';end;end;end;end.如果读者对此感兴趣,可以到“Delphi盒子”此网站下载“C/S型聊天室3.0版”源程序网址为/article.asp?articleid=699 。