在网络编程中最常用的方案便是Client/Server (客户机/服务器)模型。在这种方案中客户应用程序向服务器程序请求服务。一个服务程序通常在一个众所周知的地址监听对服务的请求,也就是说,服务进程一直处于休眠状态,直到一个客户向这个服务的地址提出了连接请求。在这个时刻,服务程序被"惊醒"并且为客户提供服务-对客户的请求作出适当的反应。为了方便这种Client/Server模型的网络编程,90年代初,由Microsoft联合了其他几家公司共同制定了一套WINDOWS下的网络编程接口,即Windows Sockets规范,它不是一种网络协议,而是一套开放的、支持多种协议的Windows下的网络编程接口。现在的Winsock已经基本上实现了与协议无关,你可以使用Winsock来调用多种协议的功能,但较常使用的是TCP/IP协议。Socket实际在计算机中提供了一个通信端口,可以通过这个端 口与任何一个具有Socket接口的计算机通信。应用程序在网络上传输,接收的信息都通过这个Socket接口来实现。
简单的来说,socket非常类似于电话插座。以一个电话网为例。电话的通话双方相当于相互通信的2个程序,电话号码就是IP地址。任何用户在通话之前,首先要占有一部电话机,相当于申请一个socket;同时要知道对方的号码,相当于对方有一个固定的socket。然后向对方拨号呼叫,相当于发出连接请求。对方假如在场并空闲,拿起电话话筒,双方就可以正式通话,相当于连接成功。双方通话的过程,是一方向电话机发出信号和对方从电话机接收信号的过程,相当于向socket发送数据和从socket接收数据。通话结束后,一方挂起电话机相当于关闭socket,撤消连接。
客户端Client Socket连接服务端指定的端口(负责接收和发送服务端消息)
服务端Welcoming Socket监听到客户端连接,创建Connection Socket(负责和客户端通讯)
Socket的通讯过程:
客户端:
服务器端:
socket通信的基本流程图:
Thread threadWatchPort = null;//监听端口线程
Socket socketWatchPort = null;
//存储客户端连接的信息
Dictionary<string, ClientConnection> dictConnections = new Dictionary<string, ClientConnection>();
//IP地址
IPAddress address = null;
//IP节点
IPEndPoint endpoint = null;
/// <summary>
/// 开始监听用户连接
/// </summary>
/// 函数原型是int PASCAL listen(SOCKET,int);
///其中第二参数的含义楼主理解错误,并非最大可连接数,而是最多可缓存的监听个数。
///这里listen()维护一个队列,每一个请求监听,但尚未被accept()的请求都放在队列里,而一旦监听被accept()之后,该监听就从队列移走了。
private void WatchConnection()
{
try
{
//创建IP地址
address = IPAddress.Parse(txtIP.Text);
//创建IP节点(包含IP和端口)
endpoint = new IPEndPoint(address, int.Parse(txtPort.Text));
//创建一个监听套接字(基于TCP的流式套接字)
socketWatchPort = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//将套接字绑定到主机上某个端口
socketWatchPort.Bind(endpoint);
//能同时处理的连接数
socketWatchPort.Listen(10);
threadWatchPort = new Thread(WatchPort);
threadWatchPort.Name = "threadWatchPort";
//设为后台线程,当所有前台线程停止后会自动关闭
threadWatchPort.IsBackground = true;
threadWatchPort.Start();
ShowMsg("服务器启动完毕,等待客户端连接");
}
catch (Exception ex)
{
ShowErrorMsg("",ex);
}
}
private void WatchPort()
{
while (true)
{
try
{
//cSok和客户端通信套接字
Socket cSok = socketWatchPort.Accept();
ClientConnection conn = new ClientConnection(this, cSok);
ShowMsg("客户端" + cSok.RemoteEndPoint.ToString() + "连接成功");
dictConnections.Add(cSok.RemoteEndPoint.ToString(), conn);
AddClientToList(cSok.RemoteEndPoint.ToString());
}
catch (Exception ex)
{
ShowErrorMsg("",ex);
//break;
}
}
}
public class ClientConnection
{
Thread threadClient = null;
Socket socket = null;
FrmMain frmMain = null;
bool doesClose = false;
public ClientConnection(FrmMain frmMain,Socket socket)
{
this.frmMain = frmMain;
this.socket = socket;
threadClient = new Thread(WatchClientMsg);
threadClient.IsBackground = true;
threadClient.Start();
}
#region 监听客户端消息 -WatchClientMsg();
/// <summary>
/// 监听客户端消息
/// </summary>
private void WatchClientMsg()
{
while (!doesClose)
{
try
{
byte[] byteMsgRec = new byte[1024 * 1024 * 4];
int length = socket.Receive(byteMsgRec, byteMsgRec.Length, SocketFlags.None);
if (length > 0)
{
string strMsgRec = Encoding.UTF8.GetString(byteMsgRec, 1, length - 1);
ShowMsg(socket.RemoteEndPoint.ToString() + "说:" + strMsgRec);
}
}
catch (Exception ex)
{
if (socket!=null)
{
ShowErr("客户端"+socket.RemoteEndPoint.ToString()+"断开连接:",ex);
frmMain.RemoveListItem(socket.RemoteEndPoint.ToString());
break;
}
//
}
}
}
#endregion
#region 发送窗口抖动 SendShake()
public void SendShake()
{
try
{
byte[] finalByte = new byte[1];
finalByte[0] = 2;
socket.Send(finalByte);
}
catch (Exception ex)
{
ShowErr("SendShake()",ex);
}
}
#endregion
#region 发送消息 -SendMsg(string msg)
/// <summary>
/// 发送消息 标志:第一位是:0
/// </summary>
/// <param name="msg"></param>
public void SendMsg(string msg)
{
try
{
byte[] msgSendByte = Encoding.UTF8.GetBytes(msg);
byte[] finalByte = new byte[msgSendByte.Length + 1];
finalByte[0] = 0;
Buffer.BlockCopy(msgSendByte, 0, finalByte, 1, msgSendByte.Length);
socket.Send(finalByte);
}
catch (Exception ex)
{
ShowErr("SendMsg(string msg)",ex);
throw;
}
}
#endregion
#region 发送文件 -SendFile(string fileName)
/// <summary>
/// 发送文件 标记:第一位为1
/// </summary>
/// <param name="fileName">文件路径</param>
public void SendFile(string fileName)
{
FileStream fs = null;
try
{
fs = new FileStream(fileName, FileMode.Open);
byte[] byteFile = new byte[1024 * 1024 * 5];
int length = fs.Read(byteFile, 0, byteFile.Length);
if (length > 0)
{
byte[] byteFinalFile = new byte[length + 1];
byteFinalFile[0] = 1;
Buffer.BlockCopy(byteFile, 0, byteFinalFile, 1, length);
socket.Send(byteFinalFile);
}
}
catch (Exception ex)
{
ShowErr("SendFile(string fileName)", ex);
}
finally
{
fs.Close();
}
}
#endregion
#region 关闭与客户端连接-Close()
public void Close()
{
doesClose = true;
threadClient.Abort();
socket.Shutdown(SocketShutdown.Both);
socket.Close();
socket = null;
}
#endregion
#region 在面板上显示消息 -ShowMsg()
private void ShowMsg(string msg)
{
this.frmMain.ShowMsg(msg);
}
private void ShowErr(string errMsg,Exception ex)
{
this.frmMain.ShowErrorMsg(errMsg, ex);
}
#endregion
}
//初始化客户端Socket用于连接服务器端
private void InitSocketAndConnect()
{
try
{
dgShowMsg = new DGShowMsg(DoShowMsg);
//创建一个客户端Socket
clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
address = IPAddress.Parse(txtIp.Text.Trim());
endP = new IPEndPoint(address, int.Parse(txtPort.Text.Trim()));
//连接到指定服务器的指定端口
clientSocket.Connect(endP);
ShowMsg("连接成功!");
}
catch (Exception ex)
{
ShowErr("InitSocketAndConnect()",ex);
}
}
private void WatchMsg()
{
while (true)
{
byte[] msgByte = new byte[1024 * 1024 * 2];
int length = 0;
try
{
length = clientSocket.Receive(msgByte,msgByte.Length,SocketFlags.None);
if (length>0)
{
if (msgByte[0]==0)//接受文字
{
ShowMsg("对方说:"+Encoding.UTF8.GetString(msgByte,1,length-1));
}
else if (msgByte[0]==1)//接受文件
{
SaveFileDialog sfd = new SaveFileDialog();
if (sfd.ShowDialog()==DialogResult.OK)
{
string savePath = sfd.FileName;
using (FileStream fs=new FileStream (savePath,FileMode.Create) )
{
fs.Write(msgByte,1,length-1);
}
ShowMsg("文件保存成功:"+savePath);
}
}
else//抖动窗体
{
ShakeWindow();
}
}
}
/// <summary>
/// 发送消息
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSendMsg_Click(object sender, EventArgs e)
{
if (clientSocket!=null)
{
try
{
string msgSend = txtInput.Text.Trim();
byte[] orgByte = Encoding.UTF8.GetBytes(msgSend);
byte[] finalByte=new byte[orgByte.Length+1];
finalByte[0] = 0;
Buffer.BlockCopy(orgByte,0,finalByte,1,orgByte.Length);
clientSocket.Send(finalByte);
ShowMsg("我说:"+msgSend);
}
catch (SocketException ex)
{
ShowErr("发送消息时",ex);
}
}
}
/// <summary>
/// 抖动窗体
/// </summary>
private void doShakeWin()
{
Random ran = new Random();
System.Drawing.Point point = this.Location;
for (int i = 0; i < 30; i++)
{
this.Location = new System.Drawing.Point(point.X + ran.Next(8), point.Y + ran.Next(8));
System.Threading.Thread.Sleep(15);
this.Location = point;
System.Threading.Thread.Sleep(15);
}
}
源代码下载:
http://files.cnblogs.com/OceanEyes/Ocean.Eyes.SocketWork.Solution.rar