Socket通信介绍
socket本质是编程接口(API),对TCP/IP的封装,TCP/IP也要提供可供程序员做网络开发所用的接口,这就是Socket编程接口。一个socket接口包括ip和对应的端口,建立连接的过程如下:
1. 服务器监听:是服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态。
2. 客户端请求:是指由客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。
3. 连接确认:是指当服务器端套接字监听到或者说接收到客户端套接字的连接请求,它就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发给客户端,一旦客户端确认了此描述,连接就建立好了。而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求。
异步编程
同步(Synchronous)
在执行某个操作时,应用程序必须等待该操作执行完成后才能继续执行。
异步(Asynchronous)
在执行某个操作时,应用程序可在异步操作执行时继续执行。实质:异步操作,启动了新的线程,主线程与方法线程并行执行。
- 异步的实质是开启了新的线程,异步线程是由线程池负责管理,多线程是由编程人员控制。
- 服务端使用异步编程,可以方便的接受多个客户端进行连接,否则需要为每一个连接的客户端开辟一个新的线程。
- 当编写GUI程序时,如果使用同步编程将会造成界面无法刷新,卡死,解决方法包括开辟新线程或者使用异步编程。
- C#中提供了方便的异步编程。
代码解析
服务端
启动服务器
private void startServer()
{
ipep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 2000);
serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
serverSocket.Bind(ipep);
serverSocket.Listen(10);
// 开启异步监听端口
serverSocket.BeginAccept(new AsyncCallback(AcceptFunc), serverSocket);
Console.WriteLine("Waiting to connect");
}
监听连接
private void AcceptFunc(IAsyncResult iar)
{
try
{
Socket s = (Socket)iar.AsyncState; // 异步对象,用于递归
Socket client = s.EndAccept(iar); // 客户端的实例
IPEndPoint clientep = (IPEndPoint)client.RemoteEndPoint; // 获得远端的端点,并打印远端的实例信息
Console.WriteLine("Client:" + clientep.Address + "(" + clientep.Port + ")" + "is Connect.");
// 异步接受消息
client.BeginReceive(receiveBuffer, 0, receiveBuffer.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), client);
string str = "this is the 1st msg!";
buffer = Encoding.Unicode.GetBytes(str);
// 异步发送消息
client.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(SendFunc), client);
s.BeginAccept(new AsyncCallback(AcceptFunc), s); //使用异步对象进行递归
}
catch(Exception e)
{
Console.WriteLine("accept Exception: "+e.Message);
}
}
异步接收消息
private void ReceiveMessage(IAsyncResult ar)
{
try
{
Socket s = (Socket)ar.AsyncState;
int length = s.EndReceive(ar);
string message = Encoding.Unicode.GetString(receiveBuffer, 0, length);
Console.WriteLine(message);
//递归进行调用
s.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), s);
}
catch(Exception e)
{
Console.WriteLine(e.Message);
}
}
异步发送消息
每隔一秒发送一条消息
private void SendFunc(IAsyncResult iar)
{
try
{
Socket s = (Socket)iar.AsyncState;
var message = "this is manager from server!" + cnt;
cnt++;
buffer = Encoding.Unicode.GetBytes(message);
Thread.Sleep(1000);
s.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(SendFunc), s);
}
catch(Exception e)
{
Console.WriteLine("sendException: "+e.Message);
}
}
发送端
启动客户端
private void startConnect()
{
ipep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 2000);
clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
// 异步连接
clientSocket.BeginConnect(ipep, new AsyncCallback(ConnectFunc),clientSocket);
Console.WriteLine("Waiting to connect");
}
异步连接
在服务器未开启的时候,会一直报异常。连接成功后开启异步接收
private void ConnectFunc(IAsyncResult iar)
{
try
{
Socket s = (Socket)iar.AsyncState; // 获得原始的socket
s.EndConnect(iar);
Console.WriteLine("Connected!");
// 异步接收
s.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), s);
Console.WriteLine("begin receive!");
}
catch(Exception e)
{
Console.WriteLine(e.Message);
// 持续的连接服务器
Socket s = (Socket)iar.AsyncState;
s.BeginConnect(ipep, new AsyncCallback(ConnectFunc), s);
}
}
异步接收
private void ReceiveMessage(IAsyncResult ar)
{
try
{
Socket s = (Socket)ar.AsyncState;
int length = s.EndReceive(ar);
string message = Encoding.Unicode.GetString(buffer, 0, length);
Console.WriteLine(message);
//递归进行调用,使用获得的异步对象进行递归
s.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), s);
}
catch(Exception e)
{
Console.WriteLine("receive Exception: " + e.Message);
}
}
由于这里写的是控制台程序,不适合进行发送,客户端发送程序将在下一篇GUI发送端实现。
程序效果截图
- 左边是服务器,可以接受多个客户端连接
- 中间和右边分别是两个客户端连接服务器并接收消息。
初次接触C#的Socket通信,C#提供了丰富的接口函数,可以方便的编程。下一篇将介绍GUI的socket通信