(完整)使用socket进行通信程序设计
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
(完整)使用socket进行通信程序设计
编辑整理:
尊敬的读者朋友们:
这里是精品文档编辑中心,本文档内容是由我和我的同事精心编辑整理后发布的,发布之前我们对文中内容进行仔细校对,但是难免会有疏漏的地方,但是任然希望((完整)使用socket进行通信程序设计)的内容能够给您的工作和学习带来便利。
同时也真诚的希望收到您的建议和反馈,这将是我们进步的源泉,前进的动力。
本文可编辑可修改,如果觉得对您有帮助请收藏以便随时查阅,最后祝您生活愉快业绩进步,以下为(完整)使用socket进行通信程序设计的全部内容。
使用socket进行通信程序设计姓名:
学号:
专业:
2015年10月30日
引言:
“一切皆Socket!”.这话虽些许夸张,但是事实也是,现在的网络编程几乎都是用的socket。
当前是信息时代,网络的快速普及势不可挡,各种新兴应用也如雨后春笋般,层出不穷。
利用socket通信拥有即时通信功能的网络应用——聊天室,也因其为用户提供了实时性对话的渠道,深受青睐。
在本课程设计中,我个人选择C#语言实现了一个界面友好的网络聊天室,包括服务器端和客户端两个程序,可以支持多人进行文字聊天.
基本原理:
1、客户机/服务器模式
在TCP/IP网络中两个进程间相互作用的主机模式是客户机/服务器模式(Client/Server model)。
该模式的建立基于以下两点:1、非对等作用;2、通信完全是异步的。
客户机/服务器模式在操作过程中采取的是主动请示方式:首先服务器方要先启动,并根据请示提供相应服务:(过程如下)
① 打开一个通信通道(端口)并告知本地主机,并在某一个公认地址上接收客户请求;
② 等待客户请求到达该端口;
③ 接收到重复服务请求,处理该请求并发送应答信号;
④ 返回第二步,等待另一客户请求;
⑤关闭服务器。
客户方:
①打开一个通信通道,并连接到服务器所在主机的特定端口;
② 向服务器发送服务请求报文,等待并接收应答;继续提出请求……
③ 请求结束后关闭通信通道并终止.
2、套接字
套接字是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元。
可以将套接字看作不同主机间的进程进行双向通信的端点,它构成了单个主机内及整个网络间的编程界面。
套接字存在于通信域中,通信域是为了处理一般的线程通过套接字通信而引进的一种抽象概念。
套接字通常和同一个域中的套接字交换数据(数据交换也可能穿越域的界限,但这时一定要执行某种解释程序)。
各种进程使用这个相同的域互相之间用Internet协议簇来进行通信.
套接字可以根据通信性质分类,这种性质对于用户是可见的。
应用程序一般仅在同一类的套接字间进行通信。
不过只要底层的通信协议允许,不同类型的套接字间也照样可以通信。
套接字有两种不同的类型:流套接字和数据报套接字。
套接字工作原理:
要通过互联网进行通信,你至少需要一对套接字,其中一个运行于客户机端,我们称之为ClientSocket,另一个运行于服务器端,我们称之为ServerSocket。
根据连接启动的方式以及本地套接字要连接的目标,套接字之间的连接过程可以分为三个步骤:服务器监听,客户端请求,连接确认。
所谓服务器监听,是服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态.
所谓客户端请求,是指由客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。
为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求。
所谓连接确认,是指当服务器端套接字监听到或者说接收到客户端套接字的连接请求,它就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发给客户端,一旦客户端确认了此描述,连接就建立好了。
而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求。
3、C# Socket
在C#中提供了两种网络服务,一种是Socket类,另一种是TcpListener(服务器),TcpClient(客户端);
Socket 类为网络通信提供了一套丰富的方法和属性.Socket 类允许您使用ProtocolType枚举中所列出的任何一种协议执行异步和同步数据传输.
Framework为应用程序访问Internet提供了分层的、可扩展的以及受管辖的网络服务,其名字空间和System。
Net.Sockets包含丰富的类可以开发多种网络应用程序。
.Net类采用的分层结构允许应用程序在不同的控制级别上访问网络,开发人员可以根据需要选择针对不同的级别编制程序,这些级别几乎囊括了Internet的所有需要--从socket套接字到普通的请求/响应,更重要的是,这种分层是可以扩展的,能够适应Internet不断扩展的需要。
抛开ISO/OSI模型的7层构架,单从TCP/IP模型上的逻辑层面上看,.Net类可以视为包含3个层次:请求/响应层、应用协议层、传输层。
WebReqeust和WebResponse 代表了请求/响应层,支持Http、Tcp和Udp的类组成了应用协议层,而Socket类处于传输层。
传输层位于这个结构的最底层,当其上面的应用协议层和请求/响应层不能满足应用程序的特殊需要时,就需要使用这一层进行Socket套接字编程。
而在。
Net中,System。
Net.Sockets 命名空间为需要严密控制网络访问的开发人员提供了 Windows Sockets (Winsock)接口的托管实现。
System。
Net 命名空间中的所有其他网络访问类都建立在该套接字Socket实现之上,如TCPClient、TCPListener 和 UDPClient 类封装有关创建到 Internet 的 TCP 和 UDP 连接的详细信息;NetworkStream类则提供用于网络访问的基础数据流等,常见的许多Internet服务都可以见到Socket的踪影,如Telnet、Http、Email、Echo等,这些服务尽管通讯协议Protocol的定义不同,但是其基础的传输都是采用的
Socket。
其实,Socket可以象流Stream一样被视为一个数据通道,这个通道架设在应用程序端(客户端)和远程服务器端之间,而后,数据的读取(接收)和写入(发送)均针对这个通道来进行。
可见,在应用程序端或者服务器端创建了Socket对象之后,就可以使用
Send/SentTo方法将数据发送到连接的Socket,或者使用Receive/ReceiveFrom方法接收来自连接Socket的数据;
针对Socket编程,.NET 框架的 Socket 类是 Winsock32 API 提供的套接字服务的托管代码版本。
其中为实现网络编程提供了大量的方法,大多数情况下,
Socket 类方法只是将数据封送到它们的本机 Win32 副本中并处理任何必要的安全检查。
使用socket其间用到的方法/函数有:
Socket.Connect方法:建立到远程设备的连接
public void Connect(EndPoint remoteEP)(有重载方法)
Socket.Send 方法:从数据中的指示位置开始将数据发送到连接的 Socket。
public int Send(byte[], int, SocketFlags);(有重载方法)
Socket.SendTo 方法将数据发送到特定终结点。
public int SendTo(byte[], EndPoint);(有重载方法)
Socket。
Receive方法:将数据从连接的 Socket 接收到接收缓冲区的特定位置.
public int Receive(byte[],int,SocketFlags);
Socket.ReceiveFrom方法:接收数据缓冲区中特定位置的数据并存储终结点.
public int ReceiveFrom(byte[], int, SocketFlags, ref EndPoint);
Socket.Bind 方法:使 Socket 与一个本地终结点相关联:
public void Bind( EndPoint localEP );
Socket.Listen方法:将 Socket 置于侦听状态。
public void Listen( int backlog );
Socket。
Accept方法:创建新的 Socket 以处理传入的连接请求。
public Socket Accept();
Socket.Shutdown方法:禁用某 Socket 上的发送和接收
public void Shutdown( SocketShutdown how );
Socket。
Close方法:强制 Socket 连接关闭
public void Close();
TcpClient 类基于 Socket 类构建,这是它能够以更高的抽象程度提供 TCP 服务的基础。
因此许多应用层上的通讯协议,比如FTP(File Transfers Protocol)文件传输协议、HTTP(Hypertext Transfers Protocol)超文本传输协议等都直接创建在TcpClient等类之上.
TcpClient类,TcpListener类提供了一些简单的方法,用于在各模式下通过网络来连接、发送和接收流数据。
为使TcpClient连接并交换数据,使用TCP ProtocolType创建的TcpListener或Socket必须侦听是否有传入的连接请求。
可以使用下面两种方法之一连接到该侦听器:
(1)创建一个TcpClient,并调用三个可用的Connect方法之一.
(2)使用远程主机的主机名和端口号创建TcpClient。
此构造函数将自动尝试一个连接。
TcpClient和TcpListener使用NetworkStream类表示网络。
使用GetStream方法返回网络流,然后调用该流的Read和Write方法.NetworkStream不拥有协议类的基础套接字,因此关闭它并不影响套接字.
TCPClient 类使用 TCP 从 Internet 资源请求数据.TCP 协议建立与远程终结点的连接,然后使用此连接发送和接收数据包。
TCP 负责确保将数据包发送到终结点并在数据包到达时以正确的顺序对其进行组合.
从名字上就可以看出,TcpClient类专为客户端设计,它为 TCP 网络服务提供客户端连接。
TcpClient提供了通过网络连接、发送和接收数据的简单方法。
基于以上,本次实验中我使用了TcpClient类和TcpListener。
主要数据结构:
主程序分成两部分,在服务器端由于要实现多用户通信,所以建立一个Client 类用于存放各个连接服务器端客户的信息.该类的主要结构如下:
class Client
{
public TcpClient client { get; private set; }
public BinaryReader br { get; private set; }
public BinaryWriter bw { get; private set;}
public string clientName { get; set;}
public Client(TcpClient client){}
public void Close(){}
}
其余程序部分没有特别需要说明的数据结构,都是有关于socket通信的,在上面部分已经详述,在这里不作赘述。
主要程序流程:
服务器和客户端之间面向连接的基于套接字的系统调用时序图如下图所示
图1 Socket通信流程图
程序设计:
(一)服务端设计
1、利用VS2012应用程序向导生成C#程序框架.
2、设计界面如下:
设置属性如下:
namespace chatserver
{
partial class FServer
{
///<summary〉
/// Required designer variable.
///〈/summary〉
private ponentModel。
IContainer components = null;
///<summary>
/// Clean up any resources being used。
///〈/summary〉
///〈param name="disposing”>true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if(disposing &&(components != null))
{
components.Dispose();
}
base。
Dispose(disposing);
}
#region Windows Form Designer generated code
///〈summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor。
///</summary>
private void InitializeComponent()
{
bel1 = new System。
Windows。
bel();
this。
txtIPInfo = new System。
Windows.Forms。
TextBox();
this。
label2 = new bel();
this。
txtPortInfo = new System.Windows.Forms.TextBox();
this.btnServerStart = new System.Windows.Forms.Button();
bel3 = new System。
Windows。
Forms。
Label();
this。
richTxtMessage = new System.Windows。
Forms.RichTextBox ();
this。
btnStopServer = new System。
Windows。
Forms.Button();
this。
label4 = new System。
bel();
this.richTxtOnlineUser = new System.Windows。
Forms.RichTextBox();
this。
SuspendLayout();
//
// label1
//
this。
label1.AutoSize = true;
this。
label1.Location = new System。
Drawing.Point(33, 24);
= ”label1";
bel1。
Size = new System。
Drawing.Size(41, 12);
this。
label1.TabIndex = 0;
bel1。
Text = "IP地址";
//
// txtIPInfo
//
this。
txtIPInfo.Location = new System.Drawing.Point(80, 21);
this。
txtIPInfo。
Name = ”txtIPInfo";
this.txtIPInfo。
Size = new System。
Drawing.Size(94, 21);
this。
txtIPInfo。
TabIndex = 1;
this。
txtIPInfo。
Text = ”127。
0。
0.1”;
//
// label2
//
this。
label2。
AutoSize = true;
this。
label2.Location = new System.Drawing.Point(219, 24);
this。
= ”label2";
this。
label2.Size = new System.Drawing.Size(95, 12);
this。
label2.TabIndex = 2;
bel2.Text = "端口号(0—65535)";
//
// txtPortInfo
//
this。
txtPortInfo。
Location = new System。
Drawing.Point(320,20);
= "txtPortInfo";
this.txtPortInfo。
Size = new System.Drawing.Size(79, 21);
this。
txtPortInfo.TabIndex = 3;
this.txtPortInfo。
Text = "1”;
//
// btnServerStart
//
this.btnServerStart。
Location = new System。
Drawing。
Point (433, 17);
= ”btnServerStart";
this.btnServerStart。
RightToLeft = System。
Windows.Forms。
RightToLeft。
No;
this。
btnServerStart.Size = new System。
Drawing。
Size(80,25);
this.btnServerStart。
TabIndex = 4;
this。
btnServerStart.Text = "启动服务器";
eVisualStyleBackColor = true;
this。
btnServerStart.Click += new System。
EventHandler(this.btnServerStart_Click);
//
// label3
//
bel3.AutoSize = true;
this。
label3。
Location = new System。
Drawing。
Point(219, 64);
this。
= "label3";
this。
label3。
Size = new System.Drawing。
Size(53, 12);
bel3.TabIndex = 5;
this。
label3。
Text = ”聊天内容”;
//
// richTxtMessage
//
this。
richTxtMessage.Location = new System.Drawing.Point(221,89);
= "richTxtMessage";
this。
richTxtMessage。
RightToLeft = System。
Windows.Forms。
RightToLeft。
No;
this。
richTxtMessage。
Size = new System.Drawing。
Size(397,303);
this.richTxtMessage。
TabIndex = 6;
this。
richTxtMessage。
Text = ”";
//
// btnStopServer
//
this。
btnStopServer.FlatStyle = System.Windows.Forms。
FlatStyle。
System;
this。
btnStopServer.Location = new System.Drawing。
Point(543, 17);
this.btnStopServer。
Name = "btnStopServer";
this。
btnStopServer.Size = new System。
Drawing。
Size(80, 25);
this。
btnStopServer。
TabIndex = 10;
this。
btnStopServer.Text = "停止服务器";
this.btnStopServer。
UseVisualStyleBackColor = true;
this.btnStopServer.Click += new System。
EventHandler(this。
btnStopServer_Click);
//
// label4
//
bel4.AutoSize = true;
bel4。
Location = new System.Drawing.Point(26, 64);
this。
= ”label4";
bel4.Size = new System。
Drawing.Size(77, 12);
bel4.TabIndex = 11;
this。
label4.Text = "当前在线用户”;
//
// richTxtOnlineUser
//
this。
richTxtOnlineUser。
Location = new System。
Drawing。
Point(28, 89);
this。
= "richTxtOnlineUser";
this.richTxtOnlineUser。
Size = new System。
Drawing。
Size(146, 303);
this。
richTxtOnlineUser。
TabIndex = 12;
this.richTxtOnlineUser.Text = "”;
//
// FServer
//
this。
AutoScaleDimensions = new System.Drawing.SizeF(6F,12F);
this。
AutoScaleMode = System。
Windows.Forms.AutoScaleMode。
Font;
this.ClientSize = new System.Drawing.Size(648, 418);
this。
Controls.Add(this。
richTxtOnlineUser);
this.Controls.Add(this。
label4);
this。
Controls.Add(this。
btnStopServer);
this。
Controls。
Add(this。
richTxtMessage);
this。
Controls.Add(this。
label3);
this.Controls.Add(this.btnServerStart);
this.Controls.Add(this.txtPortInfo);
this.Controls。
Add(bel2);
this.Controls。
Add(this.txtIPInfo);
this.Controls。
Add(this。
label1);
this。
Name = "FServer”;
this。
Text = "服务端";
this。
ResumeLayout(false);
this。
PerformLayout();
}
#endregion
private System.Windows。
bel label1;
private System。
Windows.Forms。
TextBox txtIPInfo;
private System.Windows。
bel label2;
private System。
Windows。
Forms。
TextBox txtPortInfo;
private System。
Windows。
Forms。
Button btnServerStart;
private System.Windows.Forms。
Label label3;
private System。
Windows。
Forms。
RichTextBox richTxtMessage;
private System.Windows.Forms.Button btnStopServer;
private System。
Windows。
Forms。
Label label4;
private System。
Windows。
Forms.RichTextBox richTxtOnlineUser;
}
}
3、设计Client类并编写
4、按照通信流程进行相应代码编写
5、进行相应测试并作相关修改
(二)客户端设计
1、依照服务端同样的方法创建客户端界面,并设置工程属性.
设置属性如下:
namespace AsyncTcpClient
{
partial class FClient
{
///<summary>
///必需的设计器变量。
///</summary〉
private ponentModel.IContainer components = null;
///〈summary〉
///清理所有正在使用的资源。
///</summary>
///<param name=”disposing”〉如果应释放托管资源,为 true;否则为 false。
</param〉
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows 窗体设计器生成的代码
///<summary〉
///设计器支持所需的方法—不要
///使用代码编辑器修改此方法的内容。
///〈/summary〉
private void InitializeComponent()
{
bel1 = new System.Windows.Forms。
Label();
this。
txt_UserName = new System。
Windows。
Forms.TextBox();
this.btn_Login = new System.Windows.Forms。
Button();
this。
groupBoxOnline = new System。
Windows.Forms.GroupBox();
this.FirstOnlineUser = new System。
Windows。
Forms.ListBox();
this.groupBoxDialog = new System。
Windows。
Forms。
GroupBox();
this。
rtb_MessageInfo = new System.Windows。
Forms.RichTextBox();
this。
groupBox3 = new System.Windows.Forms.GroupBox();
this。
btn_SendMessage = new System.Windows。
Forms。
Button();
this.rtb_SendMessage = new System。
Windows。
Forms.RichTextBox ();
belIPInfo = new System。
bel();
bel3 = new System。
Windows。
Forms。
Label();
this。
txt_IPInfo = new System.Windows.Forms.TextBox();
this.txt_PortInfo = new System.Windows.Forms。
TextBox();
this.rtb_StatusInfo = new System.Windows。
Forms。
RichTextBox ();
this.groupBoxOnline。
SuspendLayout();
this.groupBoxDialog。
SuspendLayout();
this.groupBox3。
SuspendLayout();
this。
SuspendLayout();
//
// label1
//
bel1.AutoSize = true;
bel1.Location = new System.Drawing。
Point(9, 81);
= ”label1";
this。
label1.Size = new System。
Drawing.Size(53, 12);
bel1。
TabIndex = 0;
bel1.Text = "用户名:”;
//
// txt_UserName
//
this.txt_UserName.Location = new System.Drawing。
Point(68, 78);
this。
txt_UserName。
Name = "txt_UserName";
this.txt_UserName.Size = new System。
Drawing.Size(171, 21);
this.txt_UserName.TabIndex = 1;
//
// btn_Login
//
this。
btn_Login.Location = new System。
Drawing。
Point(11, 105);
this。
btn_ = ”btn_Login”;
this。
btn_Login。
Size = new System。
Drawing。
Size(228, 33);
this。
btn_Login。
TabIndex = 2;
this。
btn_Login.Text = ”登陆”;
this.btn_eVisualStyleBackColor = true;
this.btn_Login.Click += new
System.EventHandler(this.btn_Login_Click);
//
// groupBoxOnline
//
this.groupBoxOnline。
Controls。
Add(this.FirstOnlineUser);
this。
groupBoxOnline.Location = new System。
Drawing.Point(11, 144);
this。
= ”groupBoxOnline";
this.groupBoxOnline.Size = new System。
Drawing.Size(228, 200);
this。
groupBoxOnline.TabIndex = 3;
this。
groupBoxOnline.TabStop = false;
this。
groupBoxOnline.Text = ”当前在线”;
//
// FirstOnlineUser
//
this.FirstOnlineUser。
FormattingEnabled = true;
this。
FirstOnlineUser.ItemHeight = 12;
this.FirstOnlineUser.Location = new System。
Drawing.Point(1,17);
this。
FirstOnlineUser。
Name = "FirstOnlineUser”;
this。
FirstOnlineUser.Size = new System。
Drawing.Size(216, 172);
this.FirstOnlineUser。
TabIndex = 0;
//
// groupBoxDialog
//
this.groupBoxDialog。
Controls。
Add(this.rtb_MessageInfo);
this。
groupBoxDialog.Location = new System。
Drawing。
Point (255, 12);
= ”groupBoxDialog”;
this.groupBoxDialog。
Size = new System.Drawing.Size(385, 221);
this。
groupBoxDialog.TabIndex = 4;
this。
groupBoxDialog.TabStop = false;
this.groupBoxDialog。
Text = "对话信息";
//
// rtb_MessageInfo
//
this。
rtb_MessageInfo。
Location = new System.Drawing。
Point(15, 20);
this.rtb_ = "rtb_MessageInfo”;
this。
rtb_MessageInfo。
Size = new System.Drawing。
Size(351, 195);
this。
rtb_MessageInfo.TabIndex = 0;
this.rtb_MessageInfo。
Text = ””;
//
// groupBox3
//
this.groupBox3.Controls。
Add(this。
btn_SendMessage);
this.groupBox3.Controls。
Add(this.rtb_SendMessage);
this.groupBox3。
Location = new System.Drawing。
Point(255,239);
= "groupBox3";
this。
groupBox3。
Size = new System.Drawing。
Size(385, 98);
this.groupBox3.TabIndex = 5;
this。
groupBox3。
TabStop = false;
this。
groupBox3.Text = "发送信息”;
//
// btn_SendMessage
//
this。
btn_SendMessage.Location = new System。
Drawing。
Point (323, 41);
this。
btn_SendMessage。
Name = "btn_SendMessage";
this。
btn_SendMessage。
Size = new System.Drawing。
Size(56,23);
this.btn_SendMessage.TabIndex = 3;
this。
btn_SendMessage。
Text = "发送";
this.btn_eVisualStyleBackColor = true;
this.btn_SendMessage。
Click += new System。
EventHandler(this。
btn_SendeMessage_Click);
//
// rtb_SendMessage
//
this.rtb_SendMessage。
Location = new System。
Drawing.Point (15, 20);
this.rtb_ = "rtb_SendMessage";
this.rtb_SendMessage。
Size = new System。
Drawing.Size(302, 71);
this.rtb_SendMessage。
TabIndex = 1;
this.rtb_SendMessage.Text = ”";
//
// labelIPInfo
//
belIPInfo。
AutoSize = true;
belIPInfo。
Location = new System。
Drawing.Point(13, 13);
this。
labelIPInfo。
Name = ”labelIPInfo”;
belIPInfo.Size = new System.Drawing.Size(41, 12);
belIPInfo。
TabIndex = 7;
this。
labelIPInfo.Text = "IP地址”;
//
// label3
//
bel3。
AutoSize = true;
bel3.Location = new System。
Drawing.Point(13, 45);
= "label3";
this。
label3.Size = new System.Drawing。
Size(107, 12);
bel3.TabIndex = 8;
this。
label3.Text = "端口号(0-65535)”;
//
// txt_IPInfo
//
this.txt_IPInfo.Location = new System。
Drawing.Point(68, 10);
this.txt_IPInfo。
Name = "txt_IPInfo";
this。
txt_IPInfo。
Size = new System.Drawing.Size(171, 21);
this。
txt_IPInfo.TabIndex = 9;
this。
txt_IPInfo。
Text = ”127.0.0。
1";
//
// txt_PortInfo
//
this。
txt_PortInfo.Location = new System.Drawing。
Point(122, 42);
this。
txt_PortInfo。
Name = ”txt_PortInfo";
this。
txt_PortInfo.Size = new System。
Drawing。
Size(117, 21);
this。
txt_PortInfo。
TabIndex = 10;
this。
txt_PortInfo。
Text = "1”;
//
// rtb_StatusInfo
//
this。
rtb_StatusInfo。
Location = new System。
Drawing.Point (11, 350);
this.rtb_ = ”rtb_StatusInfo”;
this。
rtb_StatusInfo。
Size = new System。
Drawing。
Size(629,56);
this。
rtb_StatusInfo.TabIndex = 6;
this。
rtb_StatusInfo.Text = ”";
//
// FClient
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
this。
AutoScaleMode = System.Windows.Forms。
AutoScaleMode。
Font;
this.ClientSize = new System。
Drawing.Size(657, 412);
this。
Controls.Add(this.txt_PortInfo);
this。
Controls。
Add(this。
txt_IPInfo);
this.Controls.Add(bel3);
this.Controls.Add(belIPInfo);
this.Controls。
Add(this.rtb_StatusInfo);
this.Controls.Add(this.groupBox3);
this.Controls。
Add(this.groupBoxDialog);
this.Controls.Add(this.groupBoxOnline);
this.Controls。
Add(this。
btn_Login);
this.Controls.Add(this。
txt_UserName);
this.Controls。
Add(this。
label1);
this。
Name = ”FClient”;
this。
Text = "客户端”;
this.FormClosing += new System。
Windows。
Forms.FormClosingEventHandler(this.FormClient_FormClosing);
this。
groupBoxOnline。
ResumeLayout(false);
this.groupBoxDialog.ResumeLayout(false);
this。
groupBox3.ResumeLayout(false);
this.ResumeLayout(false);
this。
PerformLayout();
}
#endregion
private System。
Windows。
bel label1;
private System。
Windows。
Forms。
TextBox txt_UserName;
private System。
Windows。
Forms。
Button btn_Login;
private System.Windows.Forms.GroupBox groupBoxOnline;
private System.Windows。
Forms。
GroupBox groupBoxDialog;
private System。
Windows。
Forms。
RichTextBox rtb_MessageInfo;
private System。
Windows。
Forms。
GroupBox groupBox3;
private System。
Windows。
Forms.RichTextBox rtb_SendMessage;
private System。
Windows.Forms。
Button btn_SendMessage;
private System.Windows。
Forms.ListBox FirstOnlineUser;
private System。
Windows。
Forms。
Label labelIPInfo;
private System.Windows。
bel label3;
private System。
Windows.Forms。
TextBox txt_IPInfo;
private System。
Windows.Forms。
TextBox txt_PortInfo;
private System。
Windows.Forms。
RichTextBox rtb_StatusInfo;
}
}
2、按照流程编写相应socket通信代码
3、与服务器端共同进行测试并作相应修改、
封装方法:
编写好并测试完成全部需求确保无误后需要对代码进行封装打包成软件。
由于从VS2012开始VS2012不再直接包含封装工具,所以需要下载安装installShield。
相应步骤我参照了网络上的相应教程与视频,由于installshield需要访问的国外网站无法完成注册,所以我下了CSDN上网友分享的,没有激活,属于适用,所以最
后封装过程中的general information 中的ID,NAME我选择了No。
具体的部署安装与本project的主题关系不大,不作赘述。
安装方法:
安装步骤如下:
点击setup.exe,然后一路next指导finish就可以。
服务器端的安装与上面相同.
安装完成后,在“开始”中搜索“chat”结果如下:
可以打开相应程序。
使用方法与相关通信测试:
1、本机测试
1、打开服务器端程序(chatserver图标),按下“启动服务器”按钮,服务端开始侦听,等待服务请求。
2、打开客户端程序(chatclient图标),输入服务器IP和端口号以及用户名(有随机生成的用户名,可修改)。
如下图
3、以相同方法可以启动多个客户端,相互间可以发送数据。
4、在客户端按直接关闭可退出聊天室,最后可关闭服务器。
2、不通计算机(同一Wifi)下测试
1、在室友打开服务器端程序(chatserver图标),ipconfig查询本机无线地址为192.168.1。
100,输入后设置端口号并按下“启动服务器”按钮,服务端开始侦听,等待服务请求。
2、在我和室友的计算机分别打开客户端程序(chatclient图标),输入服务器IP和端口号以及用户名,然后相互间发送消息.如下图
3、在客户端按直接关闭可退出聊天室,最后可关闭服务器。
附录
主要程序代码(基本都包含较详细的注释):
(1)服务端主要源程序Fserver.cs:
using System;
using System。
Collections。
Generic;
using System。
ComponentModel;
using System。
Data;
using System.Drawing;
using System.Linq;
using System。
Text;
using System.Threading。
Tasks;
using System.Windows.Forms;
using System。
Threading;
using .Sockets;
using ;
namespace chatserver
{
public partial class FServer : Form
{
///<summary〉
///保存连接的所有客户端用户
///〈/summary>
private List<Client〉 clientList = new List<Client>();
//定义TcpListener
private TcpListener myListener;
///〈summary>
///是否正常退出所有接收线程
///〈/summary〉
bool isExit = false;
public FServer()
{
InitializeComponent();
//关闭对文本框的非法线程操作检查
TextBox.CheckForIllegalCrossThreadCalls = false;
//停止服务器按钮初始状态不可用
btnStopServer。
Enabled = false;
}
private void btnServerStart_Click(object sender, EventArgs e) {
//获取文本框输入的IP地址
IPAddress ipaddress = IPAddress。
Parse(txtIPInfo。
Text。
Trim ());
//获取文本框上输入的端口号
int port=Int16.Parse(txtPortInfo。
Text.Trim());
//建立listener
myListener=new TcpListener(ipaddress,port);
//启动监听
myListener。
Start();
//文本框显示提示监听开启信息
richTxtMessage。
AppendText(string.Format(string。
Format (GetCurrentTime() + ”开始在{0}:{1}监听客户端消息\r\n”, ipaddress,port)));
//为每一个监听建立相应进程
Thread myThread = new Thread(ListenClientConnect);
//相应进程开启
myThread.Start();
//开启服务器按钮封闭
btnServerStart。
Enabled = false;
//停止服务器按钮开启
btnStopServer.Enabled = true;
}
///〈summary>
///监听客户端发来的请求
///</summary〉
private void ListenClientConnect()
{
TcpClient newClient = null;
while (true)
{
ListenClientDelegate d = new
ListenClientDelegate(ListenClient);
IAsyncResult result = d。
BeginInvoke(out newClient,null, null);
//使用轮询方式来判断异步操作是否完成
while(result。
IsCompleted == false)
{
if(isExit)
break;
Thread。
Sleep(250);
}
//获取Begin 方法的返回值和所有输入/输出参数
d。
EndInvoke(out newClient, result);
if(newClient != null)
{
//每接受一个客户端连接,就创建一个对应的线程循环接收该
客户端发来的信息
Client client = new Client(newClient);
Thread threadReceive = new Thread(ReceiveData);
threadReceive。
Start(client);
clientList。
Add(client);
richTxtMessage.AppendText(string.Format (GetCurrentTime() + ”[{0}]进入\r\n",
newClient.Client.RemoteEndPoint));
richTxtMessage。
AppendText
(string.Format(GetCurrentTime() + "当前连接用户数:{0}\r\n", clientList。
Count));
richTxtOnlineUser.AppendText
(string.Format(GetCurrentTime() + ”[{0}]\r\n”,
newClient.Client.RemoteEndPoint));
}
else
{
break;
}
}
}
private delegate void ListenClientDelegate(out TcpClient client);
///〈summary〉
///接受挂起的客户端连接请求
///〈/summary〉
///〈param name="newClient"></param〉
private void ListenClient(out TcpClient newClient)
{
try
{
newClient = myListener.AcceptTcpClient();
}
catch
{
newClient = null;
}
}
///〈summary〉
///
///</summary>
private void ReceiveData(object userState)
{
Client user = (Client)userState;
TcpClient client = user.client;
while(!isExit)
{
string receiveString = null;
ReceiveMessageDelegate d = new ReceiveMessageDelegate (ReceiveMessage);
IAsyncResult result = d。
BeginInvoke(user, out receiveString, null,null);
//使用轮询方式来判断异步操作是否完成
while (!result.IsCompleted)
{
if(isExit)
break;
Thread。
Sleep(250);
}
//获取Begin方法的返回值和所有输入/输出参数
d。
EndInvoke(out receiveString, result);
if (receiveString == null)
{
if (!isExit)
{
richTxtMessage.AppendText(string。
Format (GetCurrentTime() + "与{0}失去联系,已终止接收该用户信息\r\n”, client.Client.RemoteEndPoint));
RemoveUser(user);
}
break;
}
richTxtMessage.AppendText(string.Format(GetCurrentTime() + "来自[{0}]:{1}\r\n", user.client.Client.RemoteEndPoint, receiveString));
string[] splitString = receiveString.Split(',’);
switch (splitString[0])
{
case”Login”:
user.clientName = splitString[1];
AsyncSendToAllClient(user, receiveString);
break;
case"Logout":
AsyncSendToAllClient(user, receiveString);
RemoveUser(user);
return;
case”Talk":
string talkString = receiveString。
Substring (splitString[0]。
Length + splitString[1].Length + 2);
richTxtMessage.AppendText(string。
Format (GetCurrentTime() + "{0}对{1}说:{2}\r\n", user.clientName, splitString[1], talkString));
foreach(Client target in clientList)
{
if(target。
clientName == splitString[1])
{
AsyncSendToClient(target,”talk,” + user。
clientName + ”,” + talkString);
break;
}
}
break;
default:
richTxtMessage.AppendText(GetCurrentTime() + "
说的什么鬼:\r\n” + receiveString);
break;
}
}
}
delegate void ReceiveMessageDelegate(Client user,out string receiveMessage);
///〈summary〉
///接收客户端发来的信息
///</summary>
///<param name=”user”〉〈/param〉
///〈param name="receiveMessage"〉〈/param>
private void ReceiveMessage(Client user, out string receiveMessage)
{
try
{
receiveMessage = user.br。
ReadString();
}
catch (Exception ex)
{
richTxtMessage。
AppendText(GetCurrentTime() + ex。
Message + "\r\n”);
receiveMessage = null;
}
}
///<summary〉
///异步发送信息给所有客户
///〈/summary〉
///〈param name="user"></param〉
///〈param name="message”></param>
private void AsyncSendToAllClient(Client user,string message)
{
string command = message.Split(’,')[0].ToLower();
if (command == ”login”)
{
for (int i = 0; i 〈 clientList。
Count; i++)
{
AsyncSendToClient(clientList[i], message);
if(clientList[i]。
clientName != user.clientName) AsyncSendToClient(user, "login," + clientList [i]。
clientName);
}
}
else if(command == ”logout”)
{
for(int i = 0; i 〈 clientList。
Count; i++)
{
if(clientList[i]。
clientName != user.clientName)
AsyncSendToClient(clientList[i], message);
}
}
}
///<summary>
///异步发送message给user
///</summary〉
///<param name="user"></param>
///<param name=”message”〉〈/param〉
private void AsyncSendToClient(Client user,string message)
{
SendToClientDelegate d = new SendToClientDelegate (SendToClient);
IAsyncResult result = d.BeginInvoke(user, message,null, null);
while(result。
IsCompleted == false)
{
if(isExit)
break;
Thread.Sleep(250);
}
d.EndInvoke(result);
}
private delegate void SendToClientDelegate(Client user, string message);
///〈summary>
///发送message给user
///</summary>
///<param name=”user"〉</param>
///〈param name=”message”〉〈/param〉
private void SendToClient(Client user,string message)
{
try
{
//将字符串写入网络流,此方法会自动附加字符串长度前缀
user.bw。
Write(message);
user。
bw。
Flush();
richTxtMessage.AppendText(string.Format(GetCurrentTime()
+ ”向[{0}]发送:{1}\r\n”, user.clientName, message));
}
catch
{
richTxtMessage。
AppendText(string。
Format (GetCurrentTime() + ”向[{0}]发送信息失败\r\n", user。
clientName));
}
}
///〈summary〉
///移除用户
///〈/summary>
///<param name=”user”></param〉
private void RemoveUser(Client user)
{
clientList。
Remove(user);
user。
Close();
richTxtMessage。
AppendText(string.Format(GetCurrentTime()
+ ”当前连接用户数:{0}\r\n", clientList。
Count));
}
///〈summary>
///获取当前系统时间的方法
///</summary〉
///<returns>当前时间</returns>
private DateTime GetCurrentTime()
{
DateTime currentTime = new DateTime();
currentTime = DateTime。
Now;
return currentTime;
}
private void btnStopServer_Click(object sender, EventArgs e)
{
richTxtMessage。
AppendText(GetCurrentTime() + "开始停止服务,并依次使用户退出!\r\n”);
isExit = true;
for(int i = clientList。
Count - 1; i 〉= 0; i--)
{
RemoveUser(clientList[i]);
}
//通过停止监听让myListener.AcceptTcpClient()产生异常退出监听线程
myListener.Stop();
btnServerStart。
Enabled = true;//开启服务器按钮封闭
btnStopServer.Enabled = false; //停止服务器按钮开启
}
}
}
(2)服务端主要源程序类Client.cs:
using System;
using System。
Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading。
Tasks;
using System.IO;
using 。
Sockets;
namespace chatserver
{
class Client
{
public TcpClient client {get; private set; }
public BinaryReader br { get; private set;}
public BinaryWriter bw { get; private set; }
public string clientName { get;set; }
public Client(TcpClient client)
{
this。
client = client;
NetworkStream networkStream = client。
GetStream();
br = new BinaryReader(networkStream);
bw = new BinaryWriter(networkStream);
}
public void Close()
{。