基本概念
lIP地址
每台联网的电脑都有一个唯一的IP地址。
长度32位,分为四段,每段8位,用十进制数字表示,每段范围 0 ~ 255
特殊IP:127.0.0.1 用户本地网卡测试
版本:V4(32位) 和 V6(128位,分为8段,每段16位)
l端口
在网络上有很多电脑,这些电脑一般运行了多个网络程序。每种网络程序都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的网络程序。
常用端口:21 FTP ,25 SMTP ,110 POP3 ,80 HTTP , 443 HTTPS
有两种常用Socket类型:
流式Socket(STREAM):
是一种面向连接的Socket,针对于面向连接的TCP服务应用,安全,但是效率低
是一种面向连接的Socket,针对于面向连接的TCP服务应用,安全,但是效率低
数据报式Socket(DATAGRAM):
是一种无连接的Socket,对应于无连接的UDP服务应用.不安全(丢失,顺序混乱,在接收端要分析重排及要求重发),但效率高.
是一种无连接的Socket,对应于无连接的UDP服务应用.不安全(丢失,顺序混乱,在接收端要分析重排及要求重发),但效率高.
Socket流式
1.服务端welcoming socket 开始监听端口(负责监听客户端连接信息)
2.客户端client socket连接服务端指定端口(负责接收和发送服务端消息)
3.服务端welcoming socket 监听到客户端连接,创建connection socket。(负责和客户端通信)
l服务器端的Socket(至少需要两个)
一个负责接收客户端连接请求(但不负责与客户端通信)
每成功接收到一个客户端的连接便在服务端产生一个对应的负责通信的Socket
在接收到客户端连接时创建.
为每个连接成功的客户端请求在服务端都创建一个对应的Socket(负责和客户端通信).
客户端的Socket
客户端Socket
必须指定要连接的服务端IP地址和端口。
通过创建一个Socket对象来初始化一个到服务器端的TCP连接
Socket的通讯过程:
服务器端:
申请一个socket
绑定到一个IP地址和一个端口上
开启侦听,等待接授连接
客户端:
申请一个socket
连接服务器(指明IP地址和端口号)
服务器端接到连接请求后,产生一个新的socket(端口大于1024)与客户端建立连接并进行通讯,原监听socket继续监听。
Socket的方法:
相关类
IPAddress类:包含了一个IP地址
IPEndPoint类:包含了一对IP地址和端口号
方法
Socket (): 创建一个Socket
Bind(): 绑定一个本地的IP和端口号(IPEndPoint)
Listen(): 让Socket侦听传入的连接尝试,并指定侦听队列容量
Connect(): 初始化与另一个Socket的连接
Accept(): 接收连接并返回一个新的socket
Send(): 输出数据到Socket
Receive(): 从Socket中读取数据
Close(): 关闭Socket (销毁连接)
Socket简单代码示例《聊天室》:
服务端:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Net.Sockets; using System.Net; using System.Threading; namespace C03聊天室服务端 { public partial class Form1 : Form { public Form1() { InitializeComponent(); //在多线程程序中,新创建的线程不能访问UI线程创建的窗口控件,如果需要访问窗口中的控件,可以在窗口构造函数中将CheckForIllegalCrossThreadCalls设置为 false TextBox.CheckForIllegalCrossThreadCalls = false; } //服务端 监听套接字 Socket sokWatch = null; //服务端 监听线程 Thread thrWatch = null; //字典集合:保存 通信套接字 Dictionary<string, Socket> dictCon = new Dictionary<string, Socket>(); private void btnStartListen_Click(object sender, EventArgs e) { //1.创建监听套接字 使用 ip4协议,流式传输,TCP连接 sokWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //2.绑定端口 //2.1获取网络节点对象 IPAddress address = IPAddress.Parse(txtIP.Text); IPEndPoint endPoint = new IPEndPoint(address, int.Parse(txtPort.Text)); //2.2绑定端口(其实内部 就向系统的 端口表中 注册 了一个端口,并指定了当前程序句柄) sokWatch.Bind(endPoint); //2.3设置监听队列 sokWatch.Listen(10); //2.4开始监听,调用监听线程 执行 监听套接字的 监听方法 注:因为监听消息方法会阻断线程,所以需要创建新的线程 thrWatch = new Thread(WatchConnecting); thrWatch.IsBackground = true; thrWatch.Start(); ShowMsg("服务器启动啦~~!"); } void WatchConnecting() { //2.4开始监听:此方法会阻断当前线程,直到有 其它程序 连接过来,才执行完毕 Socket sokMsg = sokWatch.Accept(); //将当前连接成功的 【与客户端通信的套接字】 的 标识 保存起来,并显示到 列表中 //将 远程客户端的 ip和端口 字符串 存入 列表 lbOnline.Items.Add(sokMsg.RemoteEndPoint.ToString()); //将 服务端的通信套接字 存入 字典集合 dictCon.Add(sokMsg.RemoteEndPoint.ToString(), sokMsg); ShowMsg("有客户端连接了~~!"); //2.5创建 通信线程 注:因为接受消息方法会阻断线程,所以必须穿件新的线程进行消息接受 Thread thrMsg = new Thread(ReceiveMsg); thrMsg.IsBackground = true; thrMsg.Start(sokMsg); } bool isReceive = true; void ReceiveMsg(object obj) { Socket sokMsg = obj as Socket; //3.通信套接字 监听 客户端的 消息 //3.1创建 消息缓存区 byte[] arrMsg = new byte[1024 * 1024 * 1]; while (isReceive) { //3.2接收客户端的消息 并存入 缓存区, //注意:Receive方法也会阻断当前的线程 sokMsg.Receive(arrMsg); //3.3将接收到的消息 转成 字符串 string strMsg = System.Text.Encoding.UTF8.GetString(arrMsg); //3.4将消息 显示到 文本框 ShowMsg(strMsg); } } void ShowMsg(string strmsg) { this.txtShow.AppendText(strmsg + " "); } private void btnSend_Click(object sender, EventArgs e) { string strOnline = ""; if (lbOnline.SelectedItem==null) { MessageBox.Show("请选择要回复的客户端"); } else { strOnline = lbOnline.SelectedItem.ToString(); string strMsg = txtInput.Text.Trim().ToString(); if (dictCon.ContainsKey(strOnline)) { ShowMsg("向客户端[" + strOnline + "]说: "); //3.通信套接字 监听 客户端的 消息 //3.3将接收到的消息 转成 字符串 byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg); dictCon[strOnline].Send(arrMsg); } } } } }
客户端:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Net.Sockets; using System.Net; using System.Threading; namespace C04聊天室客户端 { public partial class Form1 : Form { public Form1() { InitializeComponent(); //在多线程程序中,新创建的线程不能访问UI线程创建的窗口控件,如果需要访问窗口中的控件,可以在窗口构造函数中将CheckForIllegalCrossThreadCalls设置为 false TextBox.CheckForIllegalCrossThreadCalls = false; } Socket sokMsg = null; private void btnConnect_Click(object sender, EventArgs e) { //1.创建监听套接字 使用 ip4协议,流式传输,TCP连接 sokMsg = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //2.获取要连接的服务端 节点 //2.1获取网络节点对象 IPAddress address = IPAddress.Parse(txtIP.Text); IPEndPoint endPoint = new IPEndPoint(address, int.Parse(txtPort.Text)); //3.向服务端 发送链接请求 sokMsg.Connect(endPoint); ShowMsg("服务器连接成功"); Thread threceive = new Thread(Receives); threceive.IsBackground = true; threceive.Start(); } bool strtype = true; void Receives() { //3.1创建 消息缓存区 byte[] arrMsg = new byte[1024 * 1024 * 1]; while (strtype) { sokMsg.Receive(arrMsg); //3.3将接收到的消息 转成 字符串 string strMsg = System.Text.Encoding.UTF8.GetString(arrMsg); ShowMsg("服务器说:" + strMsg + " "); } } void ShowMsg(string strmsg) { this.txtShow.AppendText(strmsg + " "); } private void btnSend_Click(object sender, EventArgs e) { string strMsg = txtInput.Text.Trim(); byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg); sokMsg.Send(arrMsg); } } }
服务端页面:
客户端页面: