原理
远程服务器运行服务器程序,建立起socket服务器,不断监听某端口,当有信息传递过来时,对该信息进行处理,并给信息来源反馈处理结果。
本地客户端建立器socket连接,连接上服务器后,可以向对方发送和接收消息。
总而言之,socket通讯仅仅是本地客户端和远程服务器进行信息交互的“电话协议”,客户端和服务器就是两台“电话”,这一点无论tcp通讯抑或是udp通讯皆是如此。
架构
本文采用【控制台应用程序】的形式来表述,去除了桌面程序很多无必要代码生成的干扰,方便大家直接学习核心的部分。
服务器socket搭建流程。
1.创建socket实例(选择ipv4协议,流形式,tcp协议作为其参数,因为这篇博客分享的是tcp通讯的实现)
2.绑定指定ip地址和端口号(本文示例ip=127.0.0.1,port=8808,个人比较迷信,喜欢喜庆一点的数字)
3.开启监听队列,设置好监听队列的最多数目,如果同时被太多客户端连接的话,我们可怜的内存和CPU可能处理不过来,设置队列的目的是让超过指定数目的客户端连接先排好队,再依次处理,这个排队的细节不需要我们处理,大佬底层里已经封装好(本文设置队列为8)
4.开启新线程接收客户端连接(由于同一时间可能不同的客户端对服务器发起连接请求,所以我们需要利用多线程技术)
5.在每个客户端连接后,我们对其开启接收信息线程(tcp是可靠连接,连接上之后,直到连接断开之前,双方都可以互相发送消息,直至某一方主动关闭)
客户端socket搭建流程
1.创建socket实例(选择ipv4协议,流形式,tcp协议作为其参数,因为这篇博客分享的是tcp通讯的实现)
2.连接上述ip地址和端口号(本文示例ip=127.0.0.1,port=8808,个人比较迷信,喜欢喜庆一点的数字)
3.开启发送消息线程
4.开启接收消息线程
代码(服务端)
class Program
{
static Socket server;
static readonly Dictionary<string, Socket> clients = new Dictionary<string, Socket>();
static void Main()
{
CreateServer();
new Thread(() =>
{
try
{
Accept(server);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}).Start();
}
static void CreateServer()
{
server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPAddress iPAddress = new IPAddress(new byte[] { 127, 0, 0, 1 });
int port = 8088;
EndPoint endPoint = new IPEndPoint(iPAddress, port);
server.Bind(endPoint);
server.Listen(8);
Console.WriteLine("tcp端口已经打开,127.0.0.1:8088");
DisplayAllClients();
}
static void Accept(Object obj)
{
Socket socket = (Socket)obj;
while (true)
{
Socket client = socket.Accept();
string ip = client.RemoteEndPoint.ToString();
if (!clients.ContainsKey(ip))
{
clients.Add(ip, client);
Console.WriteLine(ip + "已连接");
DisplayAllClients();
}
new Thread(() =>
{
try
{
Recieve(client);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
clients.Remove(ip);
}
})
{ IsBackground = true }.Start();
}
}
static void Recieve(Object obj)
{
Socket client = (Socket)obj;
while (true)
{
try
{
byte[] buffer = new byte[1024 * 1024 * 8];
int length = client.Receive(buffer);
string message = Encoding.Default.GetString(buffer, 0, length);
if (message == null || message.Length == 0 || message == "close")
{
string ip = client.RemoteEndPoint.ToString();
Console.WriteLine(ip + "连接断开!");
clients.Remove(ip);
DisplayAllClients();
break;
}
Console.WriteLine(client.RemoteEndPoint.ToString() + ":" + message);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
clients.Remove(client.RemoteEndPoint.ToString());
DisplayAllClients();
break;
}
}
}
static void DisplayAllClients()
{
if (clients.Values.Count == 0)
{
Console.WriteLine("目前无连接");
Console.WriteLine("等待新的连接");
}
else
{
Console.WriteLine("目前所有连接");
Console.WriteLine("+==========+");
foreach (Socket client in clients.Values)
{
Console.WriteLine(client.RemoteEndPoint.ToString());
}
Console.WriteLine("+==========+");
Console.WriteLine();
}
}
}
别忘了引用名命空间
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
代码(客户端)
class Program
{
static Socket client;
static void Main(string[] args)
{
CreateClient();
new Thread(() =>
{
Thread.Sleep(500);
SendMessage();
}).Start();
new Thread(() =>
{
Thread.Sleep(500);
ReceiveMessage();
}).Start();
}
static void CreateClient()
{
try
{
IPAddress iPAdress = new IPAddress(new byte[] { 127, 0, 0, 1 });
int port = 8088;
IPEndPoint iPEndPoint = new IPEndPoint(iPAdress, port);
client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
client.Connect(iPEndPoint);
Console.WriteLine("已经连接上服务器");
}
catch (Exception e)
{
Console.WriteLine(e.Message);
Console.ReadKey();
Process.GetCurrentProcess().Close();
}
}
static void SendMessage()
{
try
{
while (true)
{
Console.WriteLine("等待发送信息,回车->发送,close->断开连接");
string s = Console.ReadLine();
client.Send(Encoding.Default.GetBytes(s));
Console.WriteLine("发送成功!\n");
if (s == "close")
{
client.Close();
break;
}
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
static void ReceiveMessage()
{
try
{
while (true)
{
byte[] buffer = new byte[1024 * 1024 * 8];
int length = client.Receive(buffer);
string msg = Encoding.Default.GetString(buffer, 0, length);
if (buffer.Length > 0)
{
Console.WriteLine("服务器:" + msg);
}
else
{
Console.WriteLine("断开连接");
break;
}
Thread.Sleep(100);
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
}
同样别忘了名命空间
using System;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
测试
1.创建完成两个项目并分别复制代码进去后,引入名命空间去除编译报错,分别生成解决方案。
2.分别找到两个exe可执行文件,先开启服务器程序,再开启客户端程序。
3.客户端主动向服务器发送消息
4.客户端主动关闭连接
总结
socket通讯在信息交互中占据非常重要的地位,我们学习C#这一门语言需要对这个知识点重点掌握,必须能够自己手撕socket通讯服务器和客户端的基本架构代码,这对个人理解多线程这一块内容也是非常有帮助的。
以上的介绍如果你都认真参考并自己动手参与代码编写,你就会发现我根本就没说得很详细。没错,我是故意的!
发现问题先自己优先思考,有问题欢迎加扣提出(17359 40176 冰凌),事先声明,免费答疑。如果你只是单纯来上课的,我可是会厚着脸皮要饭的哦。