一、写法流程
1.创建套接字Socket
2.用Bind方法将套接字与本地地址绑定
3.用Listen方法监听
4.用Accept方法等待客户端连接
5.建立连接,Accept返回新套接字
6.用Send和Receive相关方法收发数据
7.用Shutdown方法释放连接
8.关闭套接字
二、写法思路
1、创建Socket成员变量 作为服务器的Socket。
2、创建一个List列表存储客户端的Socket, 因为可能是多台设备 所以用列表存储
3、在Main函数中 进行实例化Socket 设置Soxket的网络协议、传输模式和通信类型
4、进行绑定IP 使用IPEndPoint ipPoint=new IPEndPoint(IPAdress.Parse(ip),port)); socket.Bind(ipPoint);
5、绑定完之后进行监听 使用socket.Listen(监听设备数);
6、建立连接以及收信息(发消息不需要)需要使用异步的方式 去实现循环监听,如果放在主线程的话会卡死
7、创建 建立连接 异步线程 Thread acceptThread=New Thread(方法名); acceptThread.Start();
8、建立连接方法 AcceptClientConnect 使用while死循环 去获取接入服务端的客户端Socket, Socket clientSocket = socket.Accept(); 然后存放到list容器中。
9、创建收消息 的 异步线程 Thread receiveThread = new Thread(ReceiveMsg);receiveThread.Start();
10、接收消息的方法 ReceiveMsg();创建一个局部Socket变量,以及创建一个字节数组,接收字节长度receiveNum和变量i,使用while死循环,内部遍历List中的Socket,用clientSocket.Available > 0判断是否发送消息至服务端,如果发送了的话,使用接收字节长度receiveNum获取字节长度receiveNum= clientSocket.Receive(resoult);,最后使用线程池去处理客户端发过来的消息ThreadPool.QueueUserWorkItem(HandleMsg, (clientSocket, Encoding.UTF8.GetString(resoult, 0, receiveNum)));
11、ThreadPool.QueueUserWorkItem(HandleMsg, (clientSocket, Encoding.UTF8.GetString(resoult, 0, receiveNum)));使用的是线程池,其中(clientSocket, Encoding.UTF8.GetString(resoult, 0, receiveNum))是传给HandleMsg的元组参数客户端clientSocket和解析成string的消息。
12、HandleMsg(object obj)方法 主要是用来将obj转成11中的元组。
13、发消息通过 拿到列表中的Socket调用Send方法 socket.Send(Encoding.UTF8.GetBytes(info));
14、关闭相关Socket是直接遍历列表中的Socket使用Shutdown(SocketShutdown.Both);方法和Close()方法进行关闭,最后使用Clear()清空列表;
15、广播消息是遍历后进行分发Send
16、不懂看代码示例
代码示例
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace TeachTcpExercise
{
class Program
{
static Socket socket;
static List<Socket> clientSockets=new List<Socket>();
//使用开关去进行控制接收连接 收消息的循环
static bool isClose = false;
static void Main(string[] args)
{
//1、建立Socket 绑定 监听
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint ipPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080);
socket.Bind(ipPoint); //绑定
socket.Listen(1024); //监听
//2、等待客户端连接 创建一个线程去进行接收客户端连接服务端 不会出现卡死的情况
Thread acceptThread = new Thread(AcceptClientConnect);
//开启线程
acceptThread.Start();
//3、收发信息
Thread receiveThread = new Thread(ReceiveMsg);
receiveThread.Start();
//4、关闭相关
while (!isClose)
{
string input = Console.ReadLine();
if(input == "Quit")
{
//把开关变成true 关闭接收消息和接收记客户端的接入
isClose = true;
for(int i = 0; i < clientSockets.Count; i++)
{
clientSockets[i].Shutdown(SocketShutdown.Both);
clientSockets[i].Close();
}
clientSockets.Clear();
break;
}
//广播消息 用于给所有的客户端发消息
else if(input.Substring(0,2)=="B:")
{
for(int i= 0; i < clientSockets.Count; i++)
{
clientSockets[i].Send(Encoding.UTF8.GetBytes(input.Substring(2)));
}
}
}
}
//这个接收客户端连入的方法中 用循环一直去接收
static void AcceptClientConnect()
{
while (true)
{
//clientSocket 接收连入的客户端
Socket clientSocket = socket.Accept();
//后面需要用到的话 可以存起来 放入容器中 在外部创建一个字典容器
clientSockets.Add(clientSocket);
//向客户端发送消息
clientSocket.Send(Encoding.UTF8.GetBytes("欢迎连入服务端"));
}
}
//接收消息
static void ReceiveMsg()
{
Socket clientSocket;
byte[] resoult=new byte[1024*1024]; //接受容器
int receiveNum; //接收的字节长度
int i;
while (!isClose)
{
//先遍历列表中的Socket 然后判断每个是否发送过来
for (i = 0; i < clientSockets.Count; i++)
{
clientSocket = clientSockets[i];
//判断是否有客户端发送消息过来
//因为就算不发消息过来 这句也会执行 receiveNum= clientSocket.Receive(resoult);
// 所以用clientSocket.Available判断是否字节数大于0
if (clientSocket.Available > 0)
{
receiveNum= clientSocket.Receive(resoult);
//处理消息的不能放在这里面 需要异步进行处理
//因为放在这 容易不能及时处理其他Socket的消息
ThreadPool.QueueUserWorkItem(HandleMsg, (clientSocket, Encoding.UTF8.GetString(resoult, 0, receiveNum)));
}
}
}
}
static void HandleMsg(object obj)
{
(Socket s, string str) info = ((Socket s, string str))obj;
Console.WriteLine("收到客户端{0}发来的信息:{1}", info.s.RemoteEndPoint, info.str);
}
}
}