------- Windows Phone 7手机开发、.Net培训、期待与您交流! -------
Socket的英文原意是"孔"或"插座".作为进程通信机制,取后一种意思,通常也成为"套接字".用于描述IP地址和端口,是一个通信链的句柄(其实就是两个程序通信用的).
有两种类型
①.流式Socket(STREAM)
是一种面向连接的Socket,针对与面向连接的TCP服务应用,安全,但效率低.
②.数据报式Socket(DATAGRAM)
是一种无连接的Socket,对应于无连接的UDP服务应用,不安全,但效率高
Socket一般应用模式(服务器端和客户端)
①.服务器端的Socket(至少需要两个)
1.一个负责接收客户端的连接请求(但不负责与客户通信)
2.每成功接收到一个客户端的连接便在服务端产生一个对应的Socket.
②.客户端Socket
1.必须指定要连接的服务端地址和端口.
2.通过创建一个Socket对象来初始化一个到服务端的TCP连接
Socket的通讯过程
①.服务器端
1.申请一个Socket
2.绑定到一个IP地址和一个端口上
3.开启侦听,等待接受连接
②.客户端
1.申请一个Socket
2.连接服务器(指明IP地址和端口号)
服务器端接到连接请求后,产生一个新的Socket(端口大于1024)与客户端建立连接并进行通讯,原监听Socket继续监听
Socket方法
相关类
IPAddress类:包含了一个IP地址
IPEndPoint类:包含了一对IP地址和端口号
方法
Socket():创建一个Socket
Bind():绑定一个本地的IP和端口号(IPEndPoint)
Listen():让Socket侦听传入的连接尝试,并指定侦听队列容量
Connect():初始化与另一个Socket的连接
Accpet():接收连接并返回一个新的Socket
Send():输出数据到Socket
Receive():从Socket中读取数据
Close():关闭Socket(销毁连接)
一.首先,进行聊天室服务端和客户端的WINFORM框架设计
二.服务器端的代码
1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 using System.Net.Sockets; 10 using System.Net; 11 using System.Threading; 12 using System.IO; 13 14 namespace MyChatRoomServer 15 { 16 public partial class FChatServer : Form 17 { 18 public FChatServer() 19 { 20 InitializeComponent(); 21 TextBox.CheckForIllegalCrossThreadCalls = false;//关闭对 文本框的跨线程操作检查 22 } 23 Thread threadwatch = null;//负责监听 客户端 连接请求的线程 24 Socket socketwatch = null;//创建 服务端 负责监听的套接字 25 26 private void btnbeginlisten_Click(object sender, EventArgs e) 27 { 28 //创建 服务端 负责监听的套接字,参数(使用IP4寻址协议,使用流式连接,使用TCP协议传输数据) 29 socketwatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 30 //获得文本框中的IP地址 31 IPAddress address = IPAddress.Parse(txtip.Text.Trim()); 32 //创建包含IP和PORT的网络节点对象 33 IPEndPoint endpoint = new IPEndPoint(address, int.Parse(txtport.Text.Trim())); 34 //将负责监听的套接字绑定到唯一的IP和PORT上 35 socketwatch.Bind(endpoint); 36 //设置监听队列的长度 37 socketwatch.Listen(10); 38 threadwatch = new Thread(WatchConnecting); 39 threadwatch.IsBackground = true;//设置为后台线程 40 threadwatch.Start();//开启线程 41 ShowMsg("服务器启动监听成功"); 42 43 } 44 //保存了服务器端所有负责和客户端通信的套接字 45 Dictionary<string, Socket> dict = new Dictionary<string, Socket>(); 46 //保存了服务器端所有负责调用 通信套接字.recive方法的线程 47 Dictionary<string, Thread> dictThread = new Dictionary<string, Thread>(); 48 /// <summary> 49 /// 监听客户端请求的方法 50 /// </summary> 51 //Socket sokConnection = null;//创建监听套接字 52 void WatchConnecting() 53 { 54 while (true)//持续不断的监听新的客户端请求 55 { 56 //开始监听 客户端 连接请求,注意: Accept方法会阻断当前线程! 57 Socket sokConnection = socketwatch.Accept();//一旦监听到客户端的请求,就返回一个负责和该客户端通信的套接字 sokconnection 58 //向列表控件中添加一个客户端的IP端口字符串,作为客户端的唯一标识 59 lbOnline.Items.Add(sokConnection.RemoteEndPoint.ToString()); 60 //将与客户端通信的套接字对象sokConnection添加到键值对集合中,并以客户端ip端口作为键 61 dict.Add(sokConnection.RemoteEndPoint.ToString(),sokConnection); 62 //创建通信线程 63 ParameterizedThreadStart pts = new ParameterizedThreadStart(RecMsg); 64 Thread thr = new Thread(pts); 65 thr.SetApartmentState(ApartmentState.STA); 66 thr.IsBackground = true; 67 thr.Start(sokConnection); 68 dictThread.Add(sokConnection.RemoteEndPoint.ToString(), thr); 69 70 ShowMsg("客户端连接成功!"+sokConnection.RemoteEndPoint.ToString()); 71 } 72 } 73 /// <summary> 74 /// 服务器端负责监听客户端发送来的数据的方法 75 /// </summary> 76 void RecMsg(object socketclientpara) 77 { 78 Socket socketclient = socketclientpara as Socket; 79 while (true) 80 { 81 //定义一个接收用的缓存区(2M字节数组) 82 byte[] arrmsgrec = new byte[1024 * 1024 * 2]; 83 //将接收到的数据存入arrmsgrec数组,并返回真正接收到的数据的长度 84 int length = -1; 85 try 86 { 87 length = socketclient.Receive(arrmsgrec); 88 } 89 catch (SocketException ex) 90 { 91 ShowMsg("异常:" + ex.Message+",RemoteEndPoint="+socketclient.RemoteEndPoint.ToString()); 92 //从通信套接字集合中删除被中断连接的通信 套接字对象 93 dict.Remove(socketclient.RemoteEndPoint.ToString()); 94 //从 通信线程 集合中 删除 被 中断连接的 通信线程 对象 95 dictThread.Remove(socketclient.RemoteEndPoint.ToString()); 96 //从 列表 中 移除 被中断连接的对象的 IP POINT 97 lbOnline.Items.Remove(socketclient.RemoteEndPoint.ToString()); 98 break; 99 } 100 catch(Exception ex) 101 { 102 ShowMsg("异常:" + ex.Message); 103 } 104 if (arrmsgrec[0] == 0)//判断发送过来的数据第一个元素是0,则发送过来的是文字数据 105 { 106 107 string strmsgrec = System.Text.Encoding.UTF8.GetString(arrmsgrec, 1, length-1); 108 ShowMsg(strmsgrec); 109 } 110 else if (arrmsgrec[0] == 1)//如果是1,则代表发送过来的是文件数据(图片/视频/文件...) 111 { 112 SaveFileDialog sfd = new SaveFileDialog();//保存文件选择框对象 113 if(sfd.ShowDialog()==System.Windows.Forms.DialogResult.OK)//用户选择文件路径后 114 { 115 string fileSavePath = sfd.FileName;//获得要保存的文件路径 116 //创建文件流,然后让文件流来 根据路径 创建一个文件 117 using (FileStream fs = new FileStream(fileSavePath, FileMode.Create)) 118 { 119 fs.Write(arrmsgrec,1, length-1); 120 ShowMsg("文件保存成功:" + fileSavePath); 121 } 122 } 123 } 124 } 125 } 126 127 //发送消息到客户端 128 private void btnsend_Click(object sender, EventArgs e) 129 { 130 if (string.IsNullOrEmpty(lbOnline.Text)) 131 { 132 MessageBox.Show("请选择好友"); 133 } 134 else 135 { 136 string strmsg = txtmsgsend.Text.Trim(); 137 //将要发送的字符串 转成 UTF8对应的字节数组 138 byte[] arrmsg = System.Text.Encoding.UTF8.GetBytes(strmsg); 139 //获得列表中选定的KEY 140 string strClientKey = lbOnline.Text; 141 //通过KEY,使用字典集合中对应的与某个客户端通信的套接字的send方法,发送数据给对方 142 dict[strClientKey].Send(arrmsg); 143 //sokConnection.Send(arrmsg); 144 ShowMsg("发送了数据出去" + strmsg); 145 } 146 147 148 } 149 void ShowMsg(string msg) 150 { 151 txtmsg.AppendText(msg + "\r\n"); 152 } 153 154 private void txtmsg_TextChanged(object sender, EventArgs e) 155 { 156 157 } 158 //服务端群发消息 159 private void btnSendToAll_Click(object sender, EventArgs e) 160 { 161 string strMsg = txtmsgsend.Text.Trim(); 162 byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg); 163 foreach (Socket s in dict.Values) 164 { 165 s.Send(arrMsg); 166 } 167 ShowMsg("群发完毕:)"); 168 } 169 } 170 }
三.客户端的代码
1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 using System.Net.Sockets; 10 using System.Net; 11 using System.Threading; 12 using System.IO; 13 14 namespace MyChatRoomClient 15 { 16 public partial class Form1 : Form 17 { 18 public Form1() 19 { 20 InitializeComponent(); 21 TextBox.CheckForIllegalCrossThreadCalls = false; 22 } 23 Thread ThreadClient = null;//客户端用来接收服务端发来的数据消息的线程 24 Socket socketclient = null; 25 private void btnbeginlisten_Click(object sender, EventArgs e) 26 { 27 IPAddress address = IPAddress.Parse(txtip.Text.Trim()); 28 IPEndPoint endpoint = new IPEndPoint(address, int.Parse(txtport.Text.Trim())); 29 socketclient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 30 socketclient.Connect(endpoint); 31 //开启线程,监听服务端发来的消息 32 ThreadClient = new Thread(RecMsg); 33 ThreadClient.IsBackground = true; 34 ThreadClient.Start(); 35 } 36 void RecMsg() 37 { 38 while(true) 39 { 40 //定义一个接收用的缓存区(2M字节数组) 41 byte[] arrmsgrec = new byte[1024 * 1024 * 2]; 42 //将接收到的数据存入arrmsgrec数组,并返回真正接收到的数据的长度 43 int length=socketclient.Receive(arrmsgrec); 44 //此时是将数组所有的元素都转成字符串,而真正接收到的只有服务端发来的几个字符 45 string strmsgrec = System.Text.Encoding.UTF8.GetString(arrmsgrec,0,length); 46 ShowMsg(strmsgrec); 47 } 48 } 49 void ShowMsg(string msg) 50 { 51 txtmsg.AppendText(msg + "\r\n"); 52 } 53 54 private void Form1_Load(object sender, EventArgs e) 55 { 56 57 } 58 //向服务器发送文本消息 59 private void btnSendMsg_Click(object sender, EventArgs e) 60 { 61 string strMsg = textMsgSend.Text.Trim(); 62 //将字符串 转成 方便网络传送的 二进制数据 63 byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg); 64 byte[] arrMsgSend = new byte[arrMsg.Length + 1]; 65 arrMsgSend[0] = 0;//设置标识位,0代表的是文字 66 Buffer.BlockCopy(arrMsg, 0, arrMsgSend, 1, arrMsg.Length); 67 socketclient.Send(arrMsgSend); 68 ShowMsg("我说" + strMsg); 69 70 } 71 72 private void textMsgSend_TextChanged(object sender, EventArgs e) 73 { 74 75 } 76 //选择要发送的文件 77 private void btnChooseFile_Click(object sender, EventArgs e) 78 { 79 OpenFileDialog ofd = new OpenFileDialog(); 80 if(ofd.ShowDialog()==System.Windows.Forms.DialogResult.OK) 81 { 82 txtFilePath.Text = ofd.FileName; 83 } 84 } 85 //向服务端发送文件 86 private void btnSendFile_Click(object sender, EventArgs e) 87 { 88 ////用文件流打开 用户选择的 文件 89 //using(FileStream fs=new FileStream(txtFilePath.Text,FileMode.Open)) 90 //{ 91 // byte[] arrFile=new byte[1024*1024*2];//定义一个2M的数组(缓存区) 92 // //将文件数据读到数组arrFlie中,并获得读取的真实数据长度length 93 // int length=fs.Read(arrFile, 0, arrFile.Length); 94 // byte[] arrFileSend = new byte[length + 1]; 95 // arrFileSend[0] = 1;//1代表发送的是文件数据 96 // //for (int i = 0; i < length; i++) 97 // //{ 98 // // arrFileSend[i + 1] = arrFile[i]; 99 // //} 100 // //将arrFile数组里的元素 从第0个拷贝,拷贝到arrFileSend数组里,从第1个位置开始存放,一共存放 101 // //length个数据 102 // Buffer.BlockCopy(arrFile, 0, arrFileSend, 1, length); 103 // //发送包含了标识位的新数据数组到服务端 104 // socketclient.Send(arrFileSend); 105 //} 106 ThreadClient = new Thread(SendFile); 107 ThreadClient.IsBackground = true; 108 ThreadClient.Start(); 109 } 110 void SendFile() 111 { 112 //用文件流打开 用户选择的 文件 113 using (FileStream fs = new FileStream(txtFilePath.Text, FileMode.Open)) 114 { 115 byte[] arrFile = new byte[1024 * 1024 * 2];//定义一个2M的数组(缓存区) 116 //将文件数据读到数组arrFlie中,并获得读取的真实数据长度length 117 int length = fs.Read(arrFile, 0, arrFile.Length); 118 byte[] arrFileSend = new byte[length + 1]; 119 arrFileSend[0] = 1;//1代表发送的是文件数据 120 //for (int i = 0; i < length; i++) 121 //{ 122 // arrFileSend[i + 1] = arrFile[i]; 123 //} 124 //将arrFile数组里的元素 从第0个拷贝,拷贝到arrFileSend数组里,从第1个位置开始存放,一共存放 125 //length个数据 126 Buffer.BlockCopy(arrFile, 0, arrFileSend, 1, length); 127 //发送包含了标识位的新数据数组到服务端 128 socketclient.Send(arrFileSend); 129 } 130 } 131 132 private void txtFilePath_TextChanged(object sender, EventArgs e) 133 { 134 135 } 136 } 137 }
------- Windows Phone 7手机开发、.Net培训、期待与您交流! -------