1. 服务端
用c#创建一个控制台的新项目
创建方法StartServerSync()实现与客户端的同步服务,但是,这个缺陷很明显,有两个点需要等待
/// <summary>
/// 同步式服务器
/// </summary>
static void StartServerSync()
{
Socket serveSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//参数一表示使用ip4,参数二表示使用流传输,参数三表示使用tcp协议
IPAddress ipAddress = IPAddress.Parse("127.0.0.1");//本机ip地址
IPEndPoint ipEndPoint = new IPEndPoint(ipAddress, 8088);
serveSocket.Bind(ipEndPoint);//绑定端口号
serveSocket.Listen(10);//开始监听端口号,10表示一个队列,这个队列最多让十个要进行连接的客户端排队,如果设置为0则可以让无限个要连接的客户端排队让服务端进行处理
Socket clientSocket = serveSocket.Accept();//使用serveSocket.Accept()使服务器连接一个客户端,此方法会暂停程序直到接收到一个客户端,返回被连接的socket
//向客户端发送一条信息
string msg = "welcome !你好";
byte[] data = Encoding.UTF8.GetBytes(msg);//将字符串转成比特
clientSocket.Send(data);
//接受客户端的一条信息
byte[] dataBuffer = new byte[1024];
int count = clientSocket.Receive(dataBuffer);//clientSocket.Receive(dataBuffer)接受一条byte数据存入dataBuffer,返回值是接受到的byte的个数,此方法会暂停程序直到接收完数据
string msgReceive = Encoding.UTF8.GetString(dataBuffer, 0, count);//获取dataBuffer[0]至dataBuffer[count]的byte转化为字符串返回
Console.WriteLine(msgReceive);
Console.ReadKey();
clientSocket.Close();//关闭跟客户端的连接
serveSocket.Close();//关闭服务端的连接
}
接下来实现服务端与客户端的异步连接,可实现多个客户端连接服务端,这里我们用Socket.BeginAccept(AsyncCallback,Object)方法使服务器开始连接一个客户端,其参数AsyncCallback可以是我们自己定义的一个方法,这里我们用自己写的AcceptCallBack(IAsyncResult)方法,第二个参数就是为了给我们的方法传递参数的.
先定义一个全局变量
static byte[] dataBuffer = new byte[1024];//用来存储传 收的数据
/// <summary>
/// 服务器(异步)
/// </summary>
static void StartServerAsync()
{
Socket serveSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//参数一表示使用ip4,参数二表示使用流传输,参数三表示使用tcp协议
IPAddress ipAddress = IPAddress.Parse("127.0.0.1");//本机ip地址
IPEndPoint ipEndPoint = new IPEndPoint(ipAddress, 8088);
serveSocket.Bind(ipEndPoint);//绑定端口号
serveSocket.Listen(10);//开始监听端口号,10表示一个队列,这个队列最多让十个要进行连接的客户端排队,如果设置为0则可以让无限个要连接的客户端排队让服务端进行处理
serveSocket.BeginAccept(AcceptCallBack, serveSocket);//调用函数AcceptCallBack()开始异步连接客户端
}
serveSocket.BeginAccept(AcceptCallBack, serveSocket);第二个参数其实就是传递给AcceptCallBack()方法的,相当于AcceptCallBack(serveSocket);
当我们使用serveSocket.BeginAccept(AcceptCallBack, serveSocket)时程序是不会暂停的,会继续执行下去,只是我们在这里并没有需要执行的代码.这只是开始接收一个客户端,并没有接受一个客户端.
我们把服务器socket传递过去方便在方法中实现与客户端的连接
/// <summary>
/// 连接客户端的回调函数
/// </summary>
/// <param name="ar"></param>
static void AcceptCallBack(IAsyncResult ar)
{
Socket serveSocket = ar.AsyncState as Socket;
Socket clientSocket = serveSocket.EndAccept(ar);//完成一个客户端的连接
//向客户端发送一条信息
string msg = "welcome !你好";
byte[] data = Encoding.UTF8.GetBytes(msg);//将字符串转成比特
clientSocket.Send(data);
//开始监听客户端数据传递//开始接收数据,第四个参数SocketFlags这里不需要,所以.none,ReceiveCallBack()是一个自定义的方法,这里表示我们接收完数据后调用该方法,最后一个参数就是ReceiveCallBack()方法中的参数的传递,这个参数是object类型
clientSocket.BeginReceive(dataBuffer, 0, 1024, SocketFlags.None, ReceiveCallBack, clientSocket);
serveSocket.BeginAccept(AcceptCallBack, serveSocket);//继续处理下一个客户端连接
}
Socket clientSocket = serveSocket.EndAccept(ar);到这里才是完成了一个客户端socket的连接.
函数的最后从新回调,实现多个客户端的连接.
同样的,我们使用clientSocket.BeginReceive(dataBuffer, 0, 1024, SocketFlags.None, ReceiveCallBack, clientSocket);开始接受客户端发过来的数据,存到dataBuffer,并切调用ReceiveCallBack()函数,此函数也是个回掉函数,以此来实现每个客户端的多次数据发送.
使用clientScoket.EndReceive(ar)完成客户端数据的传递,此时数据已经存到dataBuffer里,如果客户端非正常关闭,此行代码会出异常,因为clientSocket会为空.如果客户端使用close()方法正常关闭,那么服务器会接受到很多条空数据,以此可以区分客户端关闭方式以便做不同处理.
/// <summary>
/// 接收数据的回调函数
/// </summary>
/// <param name="ar"></param>
static void ReceiveCallBack(IAsyncResult ar)
{
Socket clientScoket = ar.AsyncState as Socket;//将参数强制转换为socket
try
{
int count = clientScoket.EndReceive(ar);//完成客户端数据的传递,返回比特个数
//客户端正常关闭服务器就会接受到空数据,但是不会报错,所以这里处理正常关闭的情况
if(count==0)
{
clientScoket.Close();
return;
}
string msg = Encoding.UTF8.GetString(dataBuffer, 0, count);
Console.WriteLine("客户说:" + msg);
//函数回调,这样就能接收多条数据
clientScoket.BeginReceive(dataBuffer, 0, 1024, SocketFlags.None, ReceiveCallBack, clientScoket);
}
catch (Exception e)
{
Console.WriteLine(e);
//处理非正常关闭的情况
if(clientScoket==null)
{
clientScoket.Close();
}
}
}
2. 客户端
相比之下客户端代码就相当容易了
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.Sockets;
using System.Net;
namespace FirstSocketClient
{
class Program
{
static void Main(string[] args)
{
Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
clientSocket.Connect(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8088));//连接服务端
//接受信息
byte[] data =new byte[1024];
int count = clientSocket.Receive(data);
string msg = Encoding.UTF8.GetString(data);
Console.WriteLine(msg);
//向服务端发送信息
while (true)
{
string s = Console.ReadLine();
if (s.Equals("//c"))
{
clientSocket.Close();return;
}
clientSocket.Send(Encoding.UTF8.GetBytes(s));
}
}
}
}