Socket通信

本文详细介绍了基于TCP/IP协议的通信原理,包括服务器与客户端的建立连接过程、数据的发送与接收流程,以及如何在Windows环境下使用WinForm实现具体的通信实例。重点讲解了Socket编程的细节,如创建Socket对象、绑定IP地址和端口、监听客户端连接请求等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、要和应用进程之间进行通信必须要知道其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;
        }
    }
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值