Socket编程
socket的英文原义是“孔”或“插座”。作为进程通信机制,取后一种意思。通常也称作“套接字”,用于描述IP地址和端口,是一个通信链的句柄。(其实就是两个程序通信用的。)
socket非常类似于电话插座。以一个电话网为例。电话的通话双方相当于相互通信的2个程序,电话号码就是IP地址。任何用户在通话之前,首先要占有一部电话机,相当于申请一个socket;同时要知道对方的号码,相当于对方有一个固定的socket。然后向对方拨号呼叫,相当于发出连接请求。对方假如在场并空闲,拿起电话话筒,双方就可以正式通话,相当于连接成功。双方通话的过程,是一方向电话机发出信号和对方从电话机接收信号的过程,相当于向socket发送数据和从socket接收数据。通话结束后,一方挂起电话机相当于关闭socket,撤消连接。
在Internet上有很多这样的主机,这些主机一般运行了多个服务软件,同时提供几种服务。每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务(应用程序)。
端口分为以下三类:
(1)公认端口(Well Known Ports):从0到1023,它们紧密绑定(binding)于一些服务。通常这些端口的通讯 明确表明了某种服务的协议。例如:80端口实际上总是HTTP通讯。
(2)注册端口(Registered Ports):从1024到49151。它们松散地绑定于一些服务。也就是说有许多服务绑定于这些端口,这些端口同样用于许多其它目的。例如:许多系统处理动态端口从1024左右开始。
(3)动态和/或私有端口(Dynamic and/or Private Ports):从49152到65535。理论上,不应为服务分配这些端 口。实际上,机器通常从1024起分配动态端口。但也有例外:SUN的RPC端口从32768开始。
Socket一般应用模式(服务器端和客户端)
客户端与服务器端进行通信要进行三次握手:
第一次:客户端发送请求到服务器端请求链接
第二次:服务器端收到请求后,发送给客户端进行确认链接
第三次:服务器端要确认客户端收没收到回信,所以客户端要发送给服务器端进行确认
经过三次握手之后就可以进行通信了, 其实还有第四次握手,就是断开连接。
服务器端的Socket(至少需要两个)
一个负责接收客户端连接请求(但不负责与客户端通信)
每成功接收到一个客户端的连接便在服务端产生一个对应的负责通信的Socket
在接收到客户端连接时创建.
为每个连接成功的客户端请求在服务端都创建一个对应的Socket(负责和客户端通信).
客户端的Socket
客户端Socket
必须指定要连接的服务端地址和端口。
通过创建一个Socket对象来初始化一个到服务器端的TCP连接。
Socket的通讯过程
服务器端:
• 申请一个socket
• 绑定到一个IP地址和一个端口上
• 开启侦听,等待接授连接
客户端:
• 申请一个socket
• 连接服务器(指明IP地址和端口号)
服务器端接到连接请求后,产生一个新的socket(端口大于1024)与客户端建立连接并进行通讯,原监听socket继续监听。
Socket的构造函数
连接通过构造函数完成。
public Socket(AddressFamilyaddressFamily, SocketType socketType, ProtocolType protocolType)
• AddressFamily 成员指定 Socket 用来解析地址的寻址方案。例如,InterNetwork 指示当 Socket 使用一个 IP 版本 4 地址连接。
• SocketType 定义要打开的 Socket 的类型
• Socket 类使用 ProtocolType 枚举向 Windows Sockets API 通知所请求的协议
如:mySocket = new Socket(AddressFamily.InterNetwork,
SocketType.Stream,ProtocolType.Tcp);
SocketDemo
服务器端:
private void btnStart_Click(object sender, EventArgs e)
{
//创建Socket对象
//AddressFamily.InterNetwork:寻址的协议用ipv4
//SocketType.Stream:数据传输的方式
//ProtocolType.Tcp:Socket传输的协议。
SocketserverSocket =new Socket(AddressFamily.InterNetwork,SocketType.Stream, ProtocolType.Tcp);
IPAddressip = IPAddress.Parse(this.txtIP.Text);
IPEndPointipEndPoint =new IPEndPoint(ip,int.Parse(this.txtPort.Text));
//绑定IP和端口
serverSocket.Bind(ipEndPoint);
//开启侦听
serverSocket.Listen(10);//如果同一时间来了很多链接那么给一个等待的链接队列。
//this.txtLog.Text = "开始侦听服务\r\n"+this.txtLog.Text;
WriteLog("开始侦听服务\r\n");
Threadthread = new Thread(this.AcceptClientConnected);
thread.IsBackground = true;
thread.Start(serverSocket);//启动一个新的线程不停的接受客户端的链接
}
public voidAcceptClientConnected(object obj)
{
SocketserverSocket = obj as Socket;
while(true)
{
//不停的开始接受客户端的链接 阻塞当前的主线程。
SocketproxSocket = serverSocket.Accept();
WriteLog("客户端:"+proxSocket.RemoteEndPoint.ToString()+"链接上了\r\n");
//将代理ProxSocketList添加到list里面去
ProxSocketList.Add(proxSocket);
//启动新线程,不停接收客户端发送来的消息
Threadthread =new Thread(this.RecieveData);
thread.IsBackground = true;
thread.Start(proxSocket);
}
}
public voidRecieveData(object obj)
{
SocketproxSocket = obj as Socket;
while(true)
{
try
{
//接收消息
byte[] recieveData = new byte[1024 * 1024];
//Receive接收数据的返回值是:真正的接受到数据的长度。
//Receive:会阻塞当前线程。
int realLength = proxSocket.Receive(recieveData, 0, recieveData.Length,SocketFlags.None);
if(realLength == 0)
{
string str = "客户端:" + proxSocket.RemoteEndPoint.ToString() +"正常退出\r\n";
WriteLog(str);
//轻轻的他走了,
proxSocket.Shutdown(SocketShutdown.Both);
proxSocket.Close();
ProxSocketList.Remove(proxSocket);
return;
}
//把接收到数据转成字符串
string msg = Encoding.Default.GetString(recieveData, 0, realLength);
string str2 = msg + " from"+proxSocket.RemoteEndPoint.ToString()+"\r\n" ;
WriteLog(str2);
}
catch(Exception ex)
{
string str = "客户端:" + proxSocket.RemoteEndPoint.ToString() +"异常退出\r\n";
WriteLog(str);
//轻轻的他走了,
if(proxSocket.Connected)
{
proxSocket.Shutdown(SocketShutdown.Both);
proxSocket.Close();
}
ProxSocketList.Remove(proxSocket);
return;
}
}
}
private voidbtnSend_Click(object sender, EventArgs e)
{
foreach(var socket in ProxSocketList)
{
if (socket.Connected)
{
string str = this.txtMsg.Text;
byte[] data = Encoding.Default.GetBytes(str);
//发送消息
socket.Send(data);
}
}
}
public voidWriteLog(string text)
{
//把消息写到 窗体的文本框里面去
if(this.txtLog.InvokeRequired)
{
this.txtLog.Invoke(new Action<string>(s =>
{
lock (this.txtLog)
{
string str = this.txtLog.Text;
this.txtLog.Text = s + str;
}
}),text);
}
else
{
this.txtLog.Text = text + this.txtLog.Text;
}
}
客户端:
private voidbtnConn_Click(object sender, EventArgs e)
{
ClientSocket = newSocket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
ClientSocket.Connect(IPAddress.Parse(this.txtIP.Text),int.Parse(this.txtPort.Text));
Thread thread =new Thread(newParameterizedThreadStart(RecieveData));
thread.IsBackground = true;
thread.Start(ClientSocket);
RecievedataThread = thread;
}
private void RecieveData(object obj)
{
while (ClientSocket !=null&& ClientSocket.Connected)
{
try
{
byte[] data = new byte[1024* 1024];
int realLen =ClientSocket.Receive(data, 0, data.Length, SocketFlags.None);
if (realLen == 0)//服务器端退出给发送消息
{
ClientSocket.Shutdown(SocketShutdown.Both);
ClientSocket.Close();
this.txtLog.Text ="服务器端退出 \r\n" + this.txtMsg.Text;
}
string strMsg =Encoding.Default.GetString(data, 0, realLen);
this.txtLog.Text = strMsg +" \r\n" + this.txtMsg.Text;
}
catch (Exception ex)
{
if (ClientSocket != null&& ClientSocket.Connected)
{
ClientSocket.Shutdown(SocketShutdown.Both);
ClientSocket.Close();
}
this.txtLog.Text = "服务器端异常退出 \r\n" + this.txtMsg.Text;
}
}
}
private void btnSend_Click(objectsender, EventArgs e)
{
if (ClientSocket.Connected)
{
ClientSocket.Send(Encoding.Default.GetBytes(this.txtMsg.Text));
}
}
private void Form1_FormClosing(objectsender, FormClosingEventArgs e)
{
if (RecievedataThread != null)
{
RecievedataThread.Abort();
}
//窗体关闭之前执行此方法。
if (ClientSocket != null &&ClientSocket.Connected)
{
ClientSocket.Shutdown(SocketShutdown.Both);
ClientSocket.Close();
}
//lock (this)
//{
//}
//Monitor.Enter(this);
//try
//{
//}
//finally
//{
// Monitor.Exit(this);
//}
}
----------------------------------------------------------------以上是Socket的一个Demo简单的聊天室----------------------------------------------------------------------