Server和Client是一对好朋友,有一天Client想知道知道现在几点了就给Server打电话(emmmm就是想他了的意思)
Client把他的手机卡clientfd插到手机卡槽里,拨出了Server的手机号(ip+port):
//连接
Socket clientfd = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
clientfd .Connect("192.168.196.157", 2000);
但是Server积极拒绝了Client,可能Server没有开机。
Server终于开机了,他在等电话,除此之外什么都不想做(Accpet()是堵塞方法,接收客户端的连接,返回一个连接上的Socket对象,才继续往下执行):
//监听套接字
Socket listenfd = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//绑定ip和port
IPAddress ipadr = IPAddress.Parse("192.168.196.157");
IPEndPoint ipep = new IPEndPoint(ipadr, 2000);
listenfd.Bind(ipep);
//监听,0表示不限制连接个数
listenfd.Listen(0);
//等待连接
Socket clientfd = listenfd.Accept();
开心的是,Client又试着给Server打电话,这次接通了。然后Client问现在几点了:
//发送数据
sendstr = "现在几点了" + '\r';
byte[] sendbuff = System.Text.Encoding.Default.GetBytes(sendstr);
clientfd.Send(sendbuff);
Server听完了Client的问题后,看了看时间告诉了Client:
//接收数据
int count = clientfd.Receive(readbuff);
readstr = System.Text.Encoding.Default.GetString(readbuff, 0, count);
Console.WriteLine("Client::"+readstr);
//反馈
sendstr = "Server:现在是 "+System.DateTime.Now.ToShortTimeString();
Console.WriteLine(sendstr);
sendbuff = System.Text.Encoding.Default.GetBytes(sendstr);
clientfd.Send(sendbuff);
。。。。。正经分割。。。。。。。。
Server端:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace SyncServer
{
class Program
{
static Socket listenfd;
static Socket toclientfd;
static byte[] readbuff = new byte[1024];
static byte[] sendbuff = new byte[1024];
static int count;
static String readstr = "";
static String sendstr = "";
static void Main(string[] args)
{
//套接字
listenfd = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//绑定
IPAddress ipadr = IPAddress.Parse("192.168.196.157");
IPEndPoint ipep = new IPEndPoint(ipadr, 2000);
listenfd.Bind(ipep);
//监听
listenfd.Listen(0);
Console.WriteLine("Server已开机(服务器启动成功)");
toclientfd = listenfd.Accept();
Console.WriteLine("接了Client的电话(服务器同意连接)");
while (true)
{
count = toclientfd.Receive(readbuff);
readstr = System.Text.Encoding.Default.GetString(readbuff, 0, count);
Console.WriteLine(readstr);
sendstr = "Server:现在是 "+System.DateTime.Now.ToShortTimeString();
Console.WriteLine(sendstr);
sendbuff = System.Text.Encoding.Default.GetBytes(sendstr);
toclientfd.Send(sendbuff);
}
}
}
}
Client端:
using System;
using System.Net.Sockets;
using System.Windows.Forms;
namespace SyncClient
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
string sendstr = "";
string readstr = "";
Socket socket;
byte[] readBuff = new byte[1024];
private void Conn_btn_Click(object sender, EventArgs e)
{
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.Connect("192.168.196.157", 2000);
Dialog.Items.Add("Server接电话啦(服务器已连接)");
}
private void Send_btn_Click(object sender, EventArgs e)
{
sendstr = "Client:" + textBox1.Text + '\r';
byte[] sendbuff = System.Text.Encoding.Default.GetBytes(sendstr);
socket.Send(sendbuff);
Dialog.Items.Add( sendstr);
int count = socket.Receive(readBuff);
readstr = System.Text.Encoding.Default.GetString(readBuff, 0, count);
Dialog.Items.Add(readstr);
}
}
}
。。。。。正经分割。。。。。。。。
但是,Client发现有时候Server不接电话,自己只能一直打一直打(Connect()是阻塞方法),有时候自己说错了,赶快重新说一次,但是Server愣住了没有反应(Receive()是阻塞方法),有时候别人跟Server聊着天,自己就只能等他们聊完,Server也不喜欢一直守着电话(Accept()是阻塞方法)、等别人说话回复别人卡死(Send()是阻塞方法)的感觉,于是他们换了一种打电话的方式。
Server开好机后,做些别的时间,没事看两眼有没有电话(BeginAccept(....)非阻塞方法,异步):
//套接字
Socket listenfd = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//绑定
IPAddress ipadr = IPAddress.Parse("192.168.196.157");
IPEndPoint ipep = new IPEndPoint(ipadr, 2000);
listenfd.Bind(ipep);
//监听
listenfd.Listen(0);
listenfd.BeginAccept(AcceptCallback, listenfd);
public void AcceptCallback(IAsyncResult ar)
{
try
{
Socket listenfd = (Socket)ar.AsyncState;
clientfd= listenfd.EndAccept(ar);
}catch (Exception e) {
Console.WriteLine(e.ToString());
}
}
Client也是,拨出(BeginConnect(...)非阻塞方法,异步)号码后把手边的东西清一清:
private void Conn_btn_Click(object sender, EventArgs e)
{
clientfd = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//连接同一局域网
clientfd.BeginConnect("192.168.196.157", 2000,ConnectCallback, clientfd);
}
public void ConnectCallback(IAsyncResult ar)
{
try
{
Socket socket = (Socket)ar.AsyncState;
socket.EndConnect(ar);
}catch(Exception e)
{
Console.WriteLine("Socket Connect fail:" + e.ToString());
}
}
Server接了电话后,他们开始聊天了,不用等着对方说话,有声音的话就去听,没声音的时候可以做点别的。
clientfd.BeginReceive(readBuff, 0, 1024, 0, ReceiveCallback, clientfd);
clientfd.BeginSend(sendBuff)
public void ReceiveCallback(IAsyncResult ar)
{
try
{
Socket socket = (Socket)ar.AsyncState;
int count = socket.EndReceive(ar);
string readstr = System.Text.Encoding.Default.GetString(readBuff, 0, count);
socket.BeginReceive(readBuff, 0, 1024, 0, ReceiveCallback, socket);
}catch (Exception e)
{
Console.WriteLine("Socket Reveive fail: " + e.ToString());
}
}
public void SendCallback(IAsyncResult ar)
{
try
{
Socket socket = (Socket)ar.AsyncState;
socket.EndSend(ar);
}catch(Exception e)
{
Console.WriteLine(e.ToString());
}
}
。。。。。正经分割。。。。。。。。
Server端:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace EchoServer
{
public partial class ServerWin : Form
{
public ServerWin()
{
InitializeComponent();
}
Socket listenfd;
Socket clientfd;
byte[] readBuff = new byte[1024];
byte[] sendbuff = new byte[1024];
private void Listen_btn_Click(object sender, EventArgs e)
{
//套接字
listenfd = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//绑定
IPAddress ipadr = IPAddress.Parse("192.168.196.157");
IPEndPoint ipep = new IPEndPoint(ipadr, 2000);
listenfd.Bind(ipep);
//监听
listenfd.Listen(0);
Dialog.Items.Add("Server已开机(服务器启动成功)");
listenfd.BeginAccept(AcceptCallback, listenfd);
}
private void Send_btn_Click(object sender, EventArgs e)
{
byte[] sendbuff = System.Text.Encoding.Default.GetBytes(textBox1.Text);
clientfd.BeginSend(sendbuff,0,sendbuff.Length,0,SendCallback, clientfd);
SetProgressDelegate setprogress = new SetProgressDelegate(SetProgress);
this.Invoke(setprogress, new object[] { "Server:"+ textBox1.Text, "" });
}
public delegate void SetProgressDelegate(String str,String name);
public void SetProgress(String str, String name)
{
Dialog.Items.Add(str);
if(name!=null)
Online.Items.Add(name);
}
public void AcceptCallback(IAsyncResult ar)
{
try
{
Socket listenfd = (Socket)ar.AsyncState;
clientfd= listenfd.EndAccept(ar);
// Dialog.Items.Add("Client上线啦");
SetProgressDelegate setprogress = new SetProgressDelegate(SetProgress);
this.Invoke(setprogress, new object[] { "Client上线啦", "Client" });
sendbuff = System.Text.Encoding.Default.GetBytes("接了Client的电话(服务器同意连接)");
clientfd.Send(sendbuff);
clientfd.BeginReceive(readBuff, 0, 1024, 0, ReceiveCallback, clientfd);
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
public void ReceiveCallback(IAsyncResult ar)
{
try
{
Socket socket = (Socket)ar.AsyncState;
int count = socket.EndReceive(ar);
string readstr = System.Text.Encoding.Default.GetString(readBuff, 0, count);
//Dialog.Items.Add(name+":"+ readstr);
SetProgressDelegate setprogress = new SetProgressDelegate(SetProgress);
this.Invoke(setprogress, new object[] { "Client:" + readstr,"" });
socket.BeginReceive(readBuff, 0, 1024, 0, ReceiveCallback, socket);
}
catch (Exception e)
{
Console.WriteLine("Socket Reveive fail: " + e.ToString());
}
}
public void SendCallback(IAsyncResult ar)
{
try
{
Socket socket = (Socket)ar.AsyncState;
socket.EndSend(ar);
}catch(Exception e)
{
Console.WriteLine(e.ToString());
}
}
}
}
Client端:
using System;
using System.Net.Sockets;
using System.Windows.Forms;
namespace SyncClient
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
string sendstr = "";
string readstr = "";
Socket socket;
byte[] readBuff = new byte[1024];
private void Conn_btn_Click(object sender, EventArgs e)
{
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.Connect("192.168.196.157", 2000);
Dialog.Items.Add("Server接电话啦(服务器已连接)");
}
private void Send_btn_Click(object sender, EventArgs e)
{
sendstr = "Client:" + textBox1.Text + '\r';
byte[] sendbuff = System.Text.Encoding.Default.GetBytes(sendstr);
socket.Send(sendbuff);
Dialog.Items.Add( sendstr);
int count = socket.Receive(readBuff);
readstr = System.Text.Encoding.Default.GetString(readBuff, 0, count);
Dialog.Items.Add(readstr);
}
}
}
。。。。。正经分割。。。。。。。。
现在他们可以愉快的聊天啦。
。。。。。。。。。。。。。。。。。。。。。正经强分割。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
怎么连接的:
Server端的重点(同步的方法时):
1)创建服务端的套接字, 实例初始化 System.Net.Sockets.Socket 类使用指定的地址族、 套接字类型和协议。其中AddressFamily的InterNetwork指ipv4地址,SocketType.stream指定字节流,ProtocolType.Tcp指定使用TCP协议传输。
Socket listenfd = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
2)Bind绑定地址ip和端口port
//绑定
IPAddress ipadr = IPAddress.Parse("192.168.196.157");
IPEndPoint ipep = new IPEndPoint(ipadr, 2000);
listenfd.Bind(ipep);
3)Listen开启监听,socket.Listen(backlog),backlog限定队列中最多容纳等待接收的连接数,0表示不限制
//监听
listenfd.Listen(0);
4)同意连接Accept(),属于阻塞方法,当没有客户端的连接申请时,服务端阻塞在这里,直到接收到客户的连接,返回一个客户端的socket对象。
Socket clientfd= listenfd.Accept();
5)可以看出,服务端有两种套接字,一个监听套接字,用来监听绑定的地址和端口上时候有客户端的申请连接,一个对应客户端的套接字,用于接收和发送指定对象数据。
Client端重点(同步的方法时):
1)创建客户端套接字,同服务端
Socket clientfd = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
2)申请连接,Connect(ip,port),属于阻塞方法,会一直卡住等待服务器接收,超时或拒绝
clientfd.Connect("192.168.196.157", 2000);
3)发送数据Send,接收数据Receive,都属于阻塞方法
clientfd.Send(sendBuff)
clientfd.Receive(readBuff)
同步->异步
同步方法 | 异步方法 | |
---|---|---|
Accept | BeginAccept 开始一个异步操作来接收连接请求 | EndAccept 异步连接 |
Connect | BeginConnect 开始一个异步操作来申请连接 | EndConnect 结束挂起的异步连接请求 |
Receive | BeginReceive 开始一个异步操作来接收数据 | EndReceive 将数据异步发送到连接套接字 |
Send | BeginSend 开始一个异步操作来发送数据 | EndSend 结束挂起的异步发送 |
异步的核心接口:IAsyncResult
#region 程序集 mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
// C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6.1\mscorlib.dll
#endregion
using System.Runtime.InteropServices;
using System.Threading;
namespace System
{
//
// 摘要:
// 表示异步操作的状态。
[ComVisible(true)]
public interface IAsyncResult
{
//
// 摘要:
// 获取一个值,该值指示异步操作是否已完成。
//
// 返回结果:
// 如果操作已完成,则为 true;否则为 false。
bool IsCompleted { get; }
//
// 摘要:
// 获取用于等待异步操作完成的 System.Threading.WaitHandle。
//
// 返回结果:
// 用于等待异步操作完成的 System.Threading.WaitHandle。
WaitHandle AsyncWaitHandle { get; }
//
// 摘要:
// 获取一个用户定义的对象,该对象限定或包含有关异步操作的信息。
//
// 返回结果:
// 一个用户定义的对象,限定或包含有关异步操作的信息。
object AsyncState { get; }
//
// 摘要:
// 获取一个值,该值指示异步操作是否同步完成。
//
// 返回结果:
// 如果异步操作同步完成,则为 true;否则为 false。
bool CompletedSynchronously { get; }
}
}
实现IAsyncResult的回调方法是多线程的方法,以达到异步的目的。IAsyncResult中的AsyncState对象就是当前线程中操作的客户/服务端套接字。
委托机制 : delegate
回调方法中不能进行UI操作,上述异步例程中使用委托的方式更新UI,将异步接收的数据显示在对话框中,具体不作描述。
----------------------------------------------------------------------------------------------------------------------------------------------------------------
写在最后:花花冲鸭!