c# TCP/IP协议利用Socket的简单通信
完全是基础,新手可以随意看看,大牛可以关闭浏览页了,哈哈。
TCP/IP协议
TCP/IP是一系列网络通信协议的统称,其中最核心的两个协议是TCP和IP。TCP称为传输控制协议,IP称为互联网络协议。
网络分层除了OSI模型分层,还有TCP/IP模型分层,将网络划分为四层,应用层、传输层、网际层、网络接口层。TCP/IP模型分层是OSI模型分层的浓缩版
OSI模型和TCP/IP网络分层的对应结构及TCP/IP部分协议族,
TCP三次握手
第一次握手:建立连接。客户端发送连接请求报文段,将SYN位置为1,Sequence Number为x;然后,客户端进入SYN_SEND状态,等待服务器的确认;
第二次握手:服务器收到SYN报文段。服务器收到客户端的SYN报文段,需要对这个SYN报文段进行确认,设置Acknowledgment Number为x+1(Sequence Number+1);同时,自己自己还要发送SYN请求信息,将SYN位置为1,Sequence Number为y;服务器端将上述所有信息放到一个报文段(即SYN+ACK报文段)中,一并发送给客户端,此时服务器进入SYN_RECV状态;
第三次握手:客户端收到服务器的SYN+ACK报文段。然后将Acknowledgment Number设置为y+1,向服务器发送ACK报文段,这个报文段发送完毕以后,客户端和服务器端都进入ESTABLISHED状态,完成TCP三次握手。
三次握手图解
在此基础上,socket连接过程:
服务器监听:服务器端socket并不定位具体的客户端socket,而是处于等待监听状态,实时监控网络状态。
客户端请求:客户端clientSocket发送连接请求,目标是服务器的serverSocket。为此,clientSocket必须知道serverSocket的地址和端口号,进行扫描发出连接请求。
连接确认:当服务器socket监听到或者是受到客户端socket的连接请求时,服务器就响应客户端的请求,建议一个新的socket,把服务器socket发送给客户端,一旦客户端确认连接,则连接建立。
注:在连接确认阶段:服务器socket即使在和一个客户端socket建立连接后,还在处于监听状态,仍然可以接收到其他客户端的连接请求,这也是一对多产生的原因。
socket连接原理知道了,此处编写最基本最简单的socket通信:
客户端代码:
/// <summary>
/// 客户端发送与服务端的连接请求,创建与之通信用的Socket
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnLink_Click(object sender, EventArgs e)
{
try
{
//定义一个用于请求连接服务器的IP和端口号
IPEndPoint ip = new IPEndPoint(IPAddress.Parse(txtIp.Text), Convert.ToInt32(txtPort.Text));//txtIP:IP地址;txtPort:端口号
//定义一个套接字用于请求连接服务器,包含三个参数(IP4寻址协议,流式连接,Tcp协议)
socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//请求连接服务端
clientSocket.Connect(ip);
//创建一个通信线程,避免阻塞主线程
Thread th = new Thread(CRecive);
//设置为后台线程,随着主线程退出而退出
th.IsBackground = true;
//启动线程
th.Start(clientSocket);
//显示与服务端连接情况
ShowMsg($"{clientSocket.RemoteEndPoint.ToString()}:已连接");
}
catch (Exception ex)
{
//提示套接字监听异常
ShowMsg($"网络错误");
}
}
/// <summary>
/// 客户端不停的接收服务端发送过来的消息
/// </summary>
/// <param name="o"></param>
void CRecive(object o)
{
Socket socketSend = o as Socket;
while (true)
{
try
{
//创建一个内存缓冲区,其大小为1024*1024字节 即1M
byte[] buffer = new byte[1024 * 1024];
//将接收到的信息存入到内存缓冲区,并返回其字节数组的长度
int length = socketSend.Receive(buffer);
if (length == 0)
{
ShowMsg($"{socketSend.RemoteEndPoint.ToString()}:已断开连接");
break;
}
//将机器接受到的字节数组转换为人可以读懂的字符串
string str = Encoding.UTF8.GetString(buffer, 0, length);
//将发送的字符串信息附加到ShowMsg上
ShowMsg($"Ta({socketSend.RemoteEndPoint.ToString()}):{str}");
}
catch (Exception ex)
{ }
}
}
客户端向服务端发送数据:
clientSocket.Send(Encoding.UTF8.GetBytes($"{txtWrite.Text}"));
服务端代码:
/// <summary>
/// 开启监听,等待客户端连接
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnMonitor_Click(object sender, EventArgs e)
{
try
{
//定义一个套接字用于监听客户端发来的消息,包含三个参数(IP4寻址协议,流式连接,Tcp协议)
socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//服务端发送信息需要一个IP地址和端口号
IPAddress ip = IPAddress.Parse(txtIp.Text);//IPAddress.Any;
//将IP地址和端口号绑定到网络节点point上
IPEndPoint point = new IPEndPoint(ip, Convert.ToInt32(txtPort.Text));
//监听绑定的网络节点
socketWatch.Bind(point);
ShowMsg("开始监听。。。");
//将套接字的监听队列长度限制为10
socketWatch.Listen(10);
//负责监听客户端的线程:创建一个监听线程
Thread th = new Thread(SListen);
//将窗体线程设置为与后台同步,随着主线程结束而结束
th.IsBackground = true;
//启动线程
th.Start(socketWatch);
}
catch (Exception ex)
{ }
}
/// <summary>
/// 等待客户端的连接 并且创建与之通信用的Socket
/// </summary>
/// <param name="o"></param>
void SListen(object o)
{
Socket socketWatch = o as Socket;
while (true)
{
try
{
//等待客户端的连接,并且创建与之通信用的Socket
Socket serverSocket = socketWatch.Accept();
//获取客户端网络结点号
string socketIP = serverSocket.RemoteEndPoint.ToString();
ShowMsg($"{socketIP}:已连接");
//创建一个通信线程,避免阻塞主线程
Thread th = new Thread(SRecive);
th.IsBackground = true;
th.Start(serverSocket);
}
catch
{ }
}
}
/// <summary>
/// 服务端不停的接收客户端发送过来的消息
/// </summary>
/// <param name="o"></param>
void SRecive(object o)
{
Socket socketSend = o as Socket;
while (true)
{
//将接收到的信息存入到内存缓冲区,并返回其字节数组的长度
try
{
//创建一个内存缓冲区,其大小为1024*1024字节 即1M
byte[] buffer = new byte[1024 * 1024];
//接收客户端发送过来的消息
int length = socketSend.Receive(buffer);
if (length == 0)
{
ShowMsg($"{socketSend.RemoteEndPoint.ToString()}:已断开连接");
break;
}
//将机器接受到的字节数组转换为人可以读懂的字符串
string str = Encoding.UTF8.GetString(buffer, 0, length);
//将发送的字符串信息附加到ShowMsg上
ShowMsg($"Ta({socketSend.RemoteEndPoint.ToString()}):{str}");
}
catch (Exception ex) { }
}
}
服务端向已连接的客户端发送数据:
serverSocket.Send(Encoding.UTF8.GetBytes($"{txtWrite.Text}"));
注:c#新手的随手笔记,方便以后学习和使用,如有不足和雷同,欢迎留言!谢谢!
浙公网安备 33010602011771号