一、要和应用进程之间进行通信必须要知道其IP地址和端口号。
1、必须要先开启服务器,再开启客户端。
2、在计算机中只有二进制才是可传输的状态,所以通过send和receive的信息都要先转化成二进制的形式,才可以再进行传输。
3、在声明Socket对象时,第一个参数AddressFamily.InterNetwork表示socket对象使用的寻址方式,如IPV4或IPV6
* 第二个参数SocketType.Stream 表示socket对象的套接字的类型,在此使用的是Stream,表示流式类型。
* 第三个参数表示socket对象支持的协议,TCP协议或UDP协议。在此使用的是TCP协议。
4、通信的具体流程图如下:
二、服务器端(以winform窗口作为服务器端)
1、服务器端的基本流程为:
- 创建一个服务端负责监听的Socket对象
- 将IP地址由字符串格式解析成IPAddress的形式
- 创建一个终结点,作为程序间进行通信的唯一标识
- 绑定终结点(客户端不需要绑定终结点)
- 设置客户端监听排队数量
- 创建一个新的线程来对客户端进行监听
2、被线程所执行的函数,如果该函数有参数的话,其参数类型必须为 object
3、当创建了新线程时,通常会提示线程间操作无效的错误。这是新线程访问了主线程的控件,可以设置程序加载时取消跨线程的检查来解决该问题:
private void Form1_Load(object sender, EventArgs e)
{
Control.CheckForIllegalCrossThreadCalls = false;
}
4、服务端程序实例:
(1)、winform界面窗口
(2)、实例代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace TheServerAndClient
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private static int myport = 8880; //myport必须为静态的
Socket ServerSocket; //负责监听客户端并建立连接
Socket ExchangeSocket; //负责与客户端之间收发信息通信
/// <summary>
/// 开始创建服务端对象,并指定其终结点
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button1_Click(object sender, EventArgs e)
{
try
{
// 创建服务器端的Socket实例,并指定其地址类型(在此为ipv4)、流方式、传输协议为TCP/IP
ServerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
// 指定服务器端的IP地址,并用Parse方法将字符串形式的ip地址转换成计算机能处理的IPAddress类型
IPAddress ip = IPAddress.Parse("127.0.0.1");
// 创建一个终结点,并指定IP地址和端口号。该终结点为客户端和服务器的唯一识别标识
IPEndPoint ipendpoint = new IPEndPoint(ip, myport);
// 通过服务端的Socket实例来绑定服务器的终结点,使其作为服务器进程在网络中的唯一标识
ServerSocket.Bind(ipendpoint);//以上的创建终结点和绑定也可以放在一行代码中:
// ServerSocket.Bind(new IPEndPoint(ip, myport));
//开始监听,并设定最多排队的请求数量
ServerSocket.Listen(10);
receiveBox.Text = "The server success listen client.";
//创建一个新的线程来对客户端进行监听
Thread mythread = new Thread(ListenClient);//对ListenClient函数开启一个新的线程
mythread.IsBackground = true;//将其设置为后台模式
mythread.Start();//启动该线程
}
catch
{
receiveBox.Text = "Failed to listen the Client!";
}
}
/// <summary>
/// 监听客户端
/// </summary>
void ListenClient()
{
try
{
while (true)
{
//Accept()方法能接受客户端的连接,并为新连接创建一个负责通信的Socket,如:ExchangeSocket
//在客户端与服务端建立连接之前,Accept方法会阻塞主线程的运行,它会一直等待客户端的连接
//这也是上面单独对ListenClient函数开启一个新的线程的原因。
ExchangeSocket = ServerSocket.Accept(); //ExchangeSocket是负责与客户端通信的Socket
receiveBox.Text = "Successfully connect to the Clinet";
//开启一个新的线程,不停的接受客户端发来的消息
Thread th = new Thread(ReceiveMessage);
th.IsBackground = true;
th.Start(ExchangeSocket);
}
}
catch
{
receiveBox.Text = "Failed to connect the Clinet";
}
}
/// <summary>
/// 该函数将作为一个单独的线程,来不断的接受客户端发来的信息
/// </summary>
/// <param name="ob"></param>
void ReceiveMessage(object ob)
{
Socket ExchangeSocket = ob as Socket;
try
{
while (true)
{
byte[] buffer = new byte[1024];
int r = ExchangeSocket.Receive(buffer); //Receive函数接受数据的格式为byte类型的数组,并返回接受的字节长度
string str = Encoding.ASCII.GetString(buffer, 0, r);//将接受到的字节数组解码成字符串的格式
//判断当客户端输入为空时,服务端将不接受信息
if (r == 0)
{
break;
}
receiveBox.Text = str;
}
}
catch
{
}
}
/// <summary>
/// 设置程序加载时取消跨线程的检查,防止提示线程间操作无效
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Form1_Load(object sender, EventArgs e)
{
Control.CheckForIllegalCrossThreadCalls = false;
}
/// <summary>
/// 服务端将文本框中的消息发给客户端
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void sendbtn_Click(object sender, EventArgs e)
{
string str = sendBox.Text;
byte[] buffer = Encoding.ASCII.GetBytes(str);
ExchangeSocket.Send(buffer); //通过Send函数来发送信息
}
}
}
三、客户端(以winform窗口作为客户端)
1、客户端的Socket对象声明与服务端类似,区别在于客户端不需要绑定IP地址和端口号。
2、客户端实例
(1)、winform界面窗口
(2)、实例代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace TheClient
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private static int myport = 8880; //myport必须为静态的
Socket ClientSocket;
/// <summary>
/// 建立与服务器端的连接
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ClientBtn_Click(object sender, EventArgs e)
{
try
{
//创建负责通信的客户端Socket对象,并指定其地址类型(在此为ipv4)、流方式、传输协议为TCP/IP
ClientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPAddress ip = IPAddress.Parse("127.0.0.1");
// 创建终结点,并指定与服务器端相同的IP地址和端口号。客户端不需要绑定终结点。
IPEndPoint ipendPoint = new IPEndPoint(ip, myport);
//建立与服务器的连接,获取要连接的远程服务器应用程序的IP地址和端口号
ClientSocket.Connect(ipendPoint);
//连接成功将再文本框中输入如下内容
reBox1.Text = "Successfully Connect to the Server";
//客户端与服务端连接成功之后就不停的接受消息,为了不卡死主线程,所以在此重新开启一个线程
//来不停的接受服务端发来的消息
Thread th = new Thread(ReceiveMessage);
th.IsBackground = true;
th.Start();
}
catch
{
reBox1.Text = "Failed to connect the Server";
}
}
/// <summary>
/// 客户端将文本框中的内容发送给服务端
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void sendbtn_Click(object sender, EventArgs e)
{
string str = sendBox2.Text.Trim();//获取发送文本框中的字符串
byte[] buffer = Encoding.ASCII.GetBytes(str);//将该字符串转换成字节数组的形式以进行传输
ClientSocket.Send(buffer); //客户端向服务端发送字节数组
}
/// <summary>
/// 客户端循环不断地接受服务端的信息
/// </summary>
void ReceiveMessage()
{
try
{
while (true)
{
// 创建一个用来接受服务端消息的byte数组
byte[] buffer = new byte[1024];
// 实际接受到的有效字节数
int r = ClientSocket.Receive(buffer);
// 将字节数组解码成字符串
string str = Encoding.ASCII.GetString(buffer, 0, r);
if (r == 0)
{
break;
}
reBox1.Text = str;
}
}
catch
{
}
}
private void Form1_Load(object sender, EventArgs e)
{
Control.CheckForIllegalCrossThreadCalls = false;
}
}
}