---------------------- Windows Phone 7手机开发、Net培训、期待与您交流! ----------------------
socket的英文原义是“孔”或“插座”。作为进程通信机制,取后一种意思。通常也称作“套接字”,用于描述IP地址和端口,是一个通信链的句柄.(其实就是两个程序通信用的)
http使用80端口 ftp使用21端口 smtp使用23端口
socket有两种类型:
流式socket(stream)
是一种面向连接的socket,针对于面向连接的TCP服务应用,安全,但是效率低。
数据报式socket(datagram)
是一种无连接的socket,对应于无连接的UDP服务应用,不安全(丢失,顺序混乱,在接收端要分析重排及要求重发),但效率高。
Socket一般应用模式(服务器端和客服端)
1.服务端welcoming socket 开始监听端口(负责监听客服端连接信息)
2.客服端client socket连接服务端指定端口(负责接收和发送服务端消息)
3.服务端welcoming socket监听到客户端连接,创建connection socket(负责和客服端通信)
Socketde的通讯过程
服务器端:
申请一个socket
绑定到一个IP地址和一个端口上
开启侦听,等待接受连接
客户端:
申请一个socket
连接服务器(指明IP地址和端口号)
服务器端接到连接请求后,产生一个新的socket(端口大于1024)与客户端建立连接并 进行通讯,原侦听socket继续侦听。
简单聊天室
服务端
public partial class FChatServer : Form
{
public FChatServer()
{
InitializeComponent();
TextBox.CheckForIllegalCrossThreadCalls = false;
}
Thread threadWatch=null;
Socket socketWatch = null;
private void button1_Click(object sender, EventArgs e)
{
//创建 服务端负责监听的套接字,参数(使用IP4地址协议,使用流式连接,使用TCP协议传输数据);
socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//获取 文本框IP地址对象
IPAddress address = IPAddress.Parse(txtIP.Text.Trim());
//创建 包含ip和port的网络节点对象
IPEndPoint endpoint = new IPEndPoint(address,int.Parse(txtPort.Text.Trim()));
//将负责监听的套接字绑定到唯一的IP和端口上
socketWatch.Bind(endpoint);
//设置监听队列长度
socketWatch.Listen(10);
//创建负责监听的线程,传入监听方法
threadWatch = new Thread(WatchConnecting);
threadWatch.IsBackground = true;
threadWatch.Start();
ShowMsg("服务器启动监听成功!");
}
//保存了服务器端所有负责和客服端通信的套接字
Dictionary dict = new Dictionary();
//保存了服务器端所有负责调用通信套接字Recive方法的线程
Dictionary dictThread = new Dictionary();
///
/// 监听 客服端请求的方法
///
///
void WatchConnecting()
{
//持续不断的监听新客服端的连接请求
while (true)
{
//开始监听客服端连接请求,accept方法,会阻断当前的线程
Socket sokConnection = socketWatch.Accept();//一旦监听到客服端的请求 ,就返回一个负责和该客服端通信的套接字sokConnection
//向列表控件中添加一个客服端的IP端口字符串,作为 客服端的唯一标识
lbOnline.Items.Add(sokConnection.RemoteEndPoint.ToString());
//将与客服端通信的套接字对象sokConnection添加到键值对集合中,并以客服端IP端口作为键
dict.Add(sokConnection.RemoteEndPoint.ToString(),sokConnection);
//创建通信线程
Thread thr = new Thread(RecMsg);
thr.IsBackground = true;
thr.Start(sokConnection);
dictThread.Add(sokConnection.RemoteEndPoint.ToString(),thr);
ShowMsg("客服端连接成功!");
}
}
///
/// 服务端负责监听客服端发送来的数据的方法
///
void RecMsg(object socketClientPara)
{
Socket socketClient = socketClientPara as Socket;
while (true)
{
//定义一个接收用的缓存区(2M字节数组)
byte[] arrMsgRec = new byte[1024 * 1024 * 2];
//将接收到的数据存入arrMsgRec数组,返回真正就收到的数据长度
int length = -1;
try
{
length = socketClient.Receive(arrMsgRec);
}
catch (SocketException ex)
{
ShowMsg("异常:" + ex.Message);
//从通信套接字集合中删除被中断连接的通信套接字对象
dict.Remove(socketClient.RemoteEndPoint.ToString());
//从通信线程集合中删除被终端连接的通信线程对象
dictThread.Remove(socketClient.RemoteEndPoint.ToString());
//从列表中移除被中断的ip:port
lbOnline.Items.Remove(socketClient.RemoteEndPoint.ToString());
break;
}
catch (Exception ex)
{
ShowMsg("异常:" + ex.Message);
break;
}
if (arrMsgRec[0] == 0)//判断发送过来的数据的第一个元素是0,则代表发送来的是文字数据
{
//此时是将数组的所有元素都转成字符串,而真正接收到的只有服务器发来的几个字符
string strMsgRec = System.Text.Encoding.UTF8.GetString(arrMsgRec, 1, length-1);
ShowMsg(strMsgRec);
}
else if (arrMsgRec[0] == 1)//如果是1,代表发送来的是文件数据(图片/视频/文件...)
{
SaveFileDialog sfd = new SaveFileDialog();
if (sfd.ShowDialog() == DialogResult.OK)
{
string fileSavePath = sfd.FileName;//获得要保存的文件路径
using (FileStream fs = new FileStream(fileSavePath, FileMode.Create))
{
fs.Write(arrMsgRec,1,length-1);
ShowMsg("文件保存成功");
}
}
}
}
}
void ShowMsg(string msg)
{
txtMessage.AppendText(msg+"\r\n");
}
//发送消息到客服端
private void btnSend_Click(object sender, EventArgs e)
{
if (string.IsNullOrEmpty(lbOnline.Text))
{
MessageBox.Show("请选择要发送的好友");
return;
}
string strMsg = txtMsgSend.Text.Trim();
//将要发送的字符串转成utf-8对应的字节数组
byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg);
//获得列表中选中的KEY
string strClientKey = lbOnline.Text;
//通过KEY,找到字典集合中对应的与某个 客服端通信的套接字的send方法,发送数据给对方
try
{
dict[strClientKey].Send(arrMsg);
ShowMsg("发送了数据:" + strMsg);
}
catch (SocketException ex)
{
ShowMsg("发送时异常:" + ex.Message);
return;
}
catch (Exception ex)
{
ShowMsg("发送时异常:" + ex.Message);
return;
}
}
private void btnSendAll_Click(object sender, EventArgs e)
{
string strMsg = txtMsgSend.Text.Trim();
byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg);
try
{
foreach (Socket s in dict.Values)
{
s.Send(arrMsg);
}
ShowMsg("群发完毕!");
}
catch (SocketException ex)
{
ShowMsg("发送时异常:" + ex.Message);
return;
}
catch (Exception ex)
{
ShowMsg("发送时异常:" + ex.Message);
return;
}
}
}
客服端
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
TextBox.CheckForIllegalCrossThreadCalls = false;
}
Thread threadClient = null;//客服端负责接收服务端发来数据消息的线程
Socket socketClient=null;
private void btnConnect_Click(object sender, EventArgs e)
{
IPAddress address = IPAddress.Parse(txtIP.Text.Trim());
IPEndPoint endpoint = new IPEndPoint(address, int.Parse(txtPort.Text.Trim()));
socketClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socketClient.Connect(endpoint);
threadClient = new Thread(RecMsg);
threadClient.IsBackground = true;
threadClient.Start();
}
void RecMsg()
{
while (true)
{
//定义一个接收用的缓存区(2M字节数组)
byte[] arrMsgRec = new byte[1024 * 1024 * 2];
//将接收到的数据存入arrMsgRec数组,返回真正就收到的数据长度
int length=socketClient.Receive(arrMsgRec);
//此时是将数组的所有元素都转成字符串,而真正接收到的只有服务器发来的几个字符
string strMsgRec = System.Text.Encoding.UTF8.GetString(arrMsgRec,0,length);
ShowMsg(strMsgRec);
}
}
void ShowMsg(string msg)
{
txtMessage.AppendText(msg + "\r\n");
}
//向服务器发送文本消息
private void btnSend_Click(object sender, EventArgs e)
{
string strMsg = txtSendMsg.Text.Trim();
//将字符串转成方便网络传送的二进制数据
byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg);
byte[] arrMsgSend=new byte[arrMsg.Length+1];
arrMsgSend[0] = 0;//设置标识位
Buffer.BlockCopy(arrMsg, 0, arrMsgSend, 1, arrMsg.Length);
try
{
socketClient.Send(arrMsgSend);
ShowMsg("我说:"+strMsg);
}
catch (SocketException ex)
{
ShowMsg("发送时异常:" + ex.Message);
return;
}
catch (Exception ex)
{
ShowMsg("发送时异常:" + ex.Message);
return;
}
}
//选择要发送的文件
private void btnChooseFile_Click(object sender, EventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog();
if (ofd.ShowDialog() == DialogResult.OK)
{
txtFilePath.Text = ofd.FileName;
}
}
//发送文件
private void btnSendFile_Click(object sender, EventArgs e)
{
//用文件流打开用户选择的文件
using (FileStream fs = new FileStream(txtFilePath.Text, FileMode.Open))
{
byte[] arrFile = new byte[1024 * 1024 * 2];//定义一个2M的数组(缓存区)
//将文件数据读到数组arrFile中,并获得读取的真实数据长度Length
int length=fs.Read(arrFile,0,arrFile.Length);
byte[] arrFileSend=new byte[length+1];
arrFileSend[0] = 1;//代表发送的是文件数据
//将arrFile数组里的元素从第0个开始拷贝,拷贝到arrFileSend数组里,从第1个开始存放,一共拷贝length个数据
Buffer.BlockCopy(arrFile,0,arrFileSend,1,length);
try
{
//发送包含了标识位的新数据数组到服务端
socketClient.Send(arrFileSend);
}
catch (SocketException ex)
{
ShowMsg("发送时异常:" + ex.Message);
return;
}
catch (Exception ex)
{
ShowMsg("发送时异常:" + ex.Message);
return;
}
}
}
}
上面的代码没实现客服端之间的消息传送
---------------------- Windows Phone 7手机开发、Net培训、期待与您交流! ----------------------
详细请查看: http://net.itheima.com