C# 网络编程之使用Socket类Send、Receive方法的同步通讯

      经过几天学习,终于解决了再C#网络编程中使用Socket类Send和Receive方法开发的客户端和服务端的同步通讯程序;实现了又客户端想服务器发送消息的界面程序.主要使用的方法是:
      1.Socket套接字编程的知识,通过IPAddress定义一个IP地址,IPEndPoint定义一个主机,Socket实例套接字对象sock和线程Thread的的成员变量;
      2.再调用方法bind绑定端口、listen监听端口、accept接受连接请求、connect请求连接来连接客户端和服务器;
      3.建立连接后通过Send和Receive方法通过线程循环接受连接请求中发送的消息,实现通信并显示在相应的控件中;
      4.最后调用socket的close和shutdown方法关闭套接字,停止连接监听.
      下面是程序运行后的结果:
      (服务端接受客户端发送的消息:这是一个单方的通信,但实现双方的方法相同,因为服务端的"欢饮使用本服务器"也反馈显示在了客户端)

      (客户端)

      下面是本程序的源代码:

      服务端

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 tbServer
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        //添加私有成员
        private IPAddress myIP = IPAddress.Parse("127.0.0.1");   //定义IP对象
        private IPEndPoint MyServer;                             //定义主机
        private Socket sock;                                     //套接字对象实例
        private bool sign = true;                                //控制循环
        private Thread thread;                                   //创建控制线程
        private Socket socklin;                                  //临时套接字,接受客户端连接请求

        //双击"开始监听"按钮添加Click事件
        private void button1_Click(object sender, EventArgs e)
        {
            try
            {
                myIP = IPAddress.Parse(textBox1.Text);           //字符串转换为IP
            }
            catch
            {
                MessageBox.Show("你输入的IP地址格式错误!");
            }

            try
            {
                //定义主机
                MyServer = new IPEndPoint(myIP, Int32.Parse(textBox2.Text));
                //构造套接字
                sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                //绑定端口
                sock.Bind(MyServer);
                //开始监听
                sock.Listen(10);
                //状态栏信息添加 textBox3替代statusStrip1(不会用)
                textBox3.Text = "主机" + textBox1.Text + " 端口" + textBox2.Text + " 开始监听";
                //构造线程
                thread = new Thread(new ThreadStart(targett)); //targett自定义函数:接受客户端连接请求
                //启动线程用于接受连接和接受数据
                thread.Start();
            }
            catch(Exception msg) {
                textBox3.Text = msg.Message;
            }
        }

        //targett():自定义函数,该方法循环开始接受客户端的连接请求
        private void targett()
        {
            socklin = sock.Accept();   //接受连接请求
            sign = true;               //循环标志变量true

            //连接
            if (socklin.Connected)
            {
                textBox3.Text = "与客户端连接";

                //信息反馈给客户端Client
                Byte[] byteNum = new Byte[64];                     //构造字节数组
                byteNum = System.Text.Encoding.BigEndianUnicode.GetBytes("欢饮使用本服务器".ToCharArray());
                socklin.Send(byteNum,byteNum.Length,0);            //发送数据

                //sign为true 循环接受数据
                while (sign)
                {
                    Byte[] byteNum2 = new Byte[128];
                    socklin.Receive(byteNum2,byteNum2.Length,0);   //接受数据
                    string str = System.Text.Encoding.BigEndianUnicode.GetString(byteNum2);
                    richTextBox1.AppendText(str+"\r\n");           //显示字符串

                    //获取richTextBox1行数
                    int length = richTextBox1.Lines.Length;
                    //如果客户端发送倒数第二行的字符串为"@@@" 断开连接
                    if(richTextBox1.Lines[length-2]=="@@@")
                    {
                        textBox3.Text = "与客户端断开连接";
                        //关闭套接字实例(both表示发送和接受关闭)
                        socklin.Shutdown(System.Net.Sockets.SocketShutdown.Both);
                        socklin.Close();
                        sign = false;                //设为false退出循环
                    }
                }
            }
        }

        //双击"停止监听"按钮添加Click事件
        private void button2_Click(object sender, EventArgs e)
        {
            try
            {
                sign = false;
                sock.Close();
                textBox3.Text = "主机" + textBox1.Text + "端口" + textBox2.Text + "监听停止";
            }
            catch
            {
                MessageBox.Show("监听尚未开始,关闭无效!");
            }

        }

        //载入Form是设置非安全访问,防止线程无效操作
        private void Form1_Load(object sender, EventArgs e)
        {
            //非安全线程访问,不检查线程是否安全
            Control.CheckForIllegalCrossThreadCalls = false;
        }
    }
}

      客户端

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 tbClient
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        //添加私有成员
        private IPAddress myIP = IPAddress.Parse("127.0.0.1");   //定义IP对象
        private IPEndPoint MyServer;                             //定义主机
        private Socket sock;                                     //套接字对象实例
        private Thread thread;                                   //创建控制线程

        //双击"请求连接"按钮添加Click事件
        private void button1_Click(object sender, EventArgs e)
        {
            try
            {
                myIP = IPAddress.Parse(textBox1.Text);           //字符串转换为IP
            }
            catch
            {
                MessageBox.Show("你输入的IP地址格式错误!");
            }

            try
            {
                //构造主机
                MyServer = new IPEndPoint(myIP, Int32.Parse(textBox2.Text));
                //构造套接字
                sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                //请求连接
                sock.Connect(MyServer);
                //构造线程
                thread = new Thread(new ThreadStart(targett)); //targett自定义函数:接受客户端连接请求
                //启动线程用于接受连接和接受数据
                thread.Start();
                //输出信息
                textBox4.Text = "与主机" + textBox1.Text + " 端口" + textBox2.Text + " 连接成功";
            }
            catch(Exception msg)
            {
                MessageBox.Show(msg.Message);
            }
        }

        //targett():自定义函数,该方法循环开始接受客户端的连接请求
        private void targett()
        {
            //构造字节数组
            Byte[] byteNum = new Byte[64];
            //接受数据
            sock.Receive(byteNum, byteNum.Length, 0);
            //将字符数组转换为字符串
            string str = System.Text.Encoding.BigEndianUnicode.GetString(byteNum);
            textBox3.Text = str;
        }

        //双击"发送消息"按钮添加Click事件
        private void button2_Click(object sender, EventArgs e)
        {
            //构造字节数组
            Byte[] byteNum = new Byte[64];
            //发送内容
            string send = richTextBox1.Text + "\r\n";
            //将字符串转换为字节数组
            byteNum = System.Text.Encoding.BigEndianUnicode.GetBytes(send.ToCharArray());
            //发送数据
            sock.Send(byteNum,byteNum.Length,0);
            //构造线程
            Thread threadSend = new Thread(new ThreadStart(targett));
            //启动线程接受数据
            threadSend.Start();
        }

        //双击"关闭连接"按钮添加Click事件
        private void button3_Click(object sender, EventArgs e)
        {
            Byte[] byteNum = new Byte[64];
            string send = "@@@" + "\r\n";
            byteNum = System.Text.Encoding.BigEndianUnicode.GetBytes(send.ToCharArray());
            sock.Send(byteNum, byteNum.Length, 0);   //将"@@@"发送给服务器

            try
            {
                sock.Close();
                textBox4.Text="主机" + textBox1.Text + "端口" + textBox2.Text + "断开连接";
            }
            catch
            {
                MessageBox.Show("连接尚未建立,断开无效!");
            }
        }

        //载入Form是设置非安全访问,防止线程无效操作
        private void Form1_Load(object sender, EventArgs e)
        {
            //非安全线程访问,不检查线程是否安全
            Control.CheckForIllegalCrossThreadCalls = false;
        }
    }
}

      该程序中我遇到的几个主要问题及解决方法如下:

      1.程序初期总是很卡,出现多次未响应情况?
      因为socket的Accept()函数是阻塞模式,它的执行会造成程序的阻塞,应该把它放置到线程中执行,否则会阻塞当前线程,出现卡死状态不响应消息,后续代码也不会执行,所以需要把accept放到创建的线程thread中,放入targett()函数中的“socklin = sock.accept()”即可实现;

      2.在定义的socket对象实例中sock与socklin(临时接受客户端连接请求)中混淆?
      socklin = sock.accept,它就是客户端发送连接的请求,因此在判断连接时是if(socklin.Connected),同时使用socklin的send和receive方法发送和接受数据;

      3.总是出现“线程间操作无效:从不是创建控件的线程访问它”的错误?
      因为windows窗体控件不是线程安全的,如果几个线程操作某一控件的状态,可能会使该控件的状态不一致,出现争用或死锁状态.我采用的解决方法是添加Form的载入load事件,在load时将CheckForIllegalCrossThreadCalls 属性的值设置为 false .这样进行非安全线程访问时,运行环境就不去检验它是否是线程安全的.这是来自与该博客,详细情况见:http://blog.csdn.net/wangchao0605/article/details/5010864

      总结:
     
最后经过一星期的学习与查阅资料,还是把这个程序弄出来了,也学到了很多东西,同时感谢上面的博主和一些书籍.希望这篇文章对大家有用,有错或不足之处见谅!
      (BY:Eastmount 2013-7-22  http://blog.csdn.net/eastmount/)

时间: 2024-12-23 10:53:16

C# 网络编程之使用Socket类Send、Receive方法的同步通讯的相关文章

Java网络编程基础(二) Socket类的使用方法

当客户程序需要与服务器程序通讯的时候,客户程序在客户机创建一个socket对象,Socket类有几个构造函数. 两个常用的构造函数是 Socket(InetAddress addr, int port) 和 Socket(String host, int port),两个构造函数都创建了一个基于Socket的连接服务器端流套接字的流套接字.对于第一个InetAddress子类对象通过addr参数获得服务器主机的IP地址,对于第二个函数host参数包被分配到InetAddress对象中,如果没有I

iOS网络编程之五——请求回执类NSURLResponse属性简介

iOS网络编程之五--请求回执类NSURLResponse属性简介         NSURLResponse类中存放请求的回执信息,在发送网络请求时,如果请求成功,首先会接收到服务端的回执信息,直接开始接收具体的返回数据.NSURLResponse对象中主要有以下属性: ? 1 2 3 4 5 6 7 8 9 10 //请求的URL地址 @property (nullable, readonly, copy) NSURL *URL; //返回数据的数据类型 @property (nullabl

iOS网络编程之六——数据缓存类NSURLCache使用解析

iOS网络编程之六--数据缓存类NSURLCache使用解析 一.引言         在前面博客中,介绍了NSURLRequest请求类的相关使用方法,其中有介绍关于请求返回数据的缓存策略,实际上,iOS中具体缓存操作的管理是由NSURLCache类来实现的.NSURLRequest类介绍的博客地址如下: iOS中NSURLRequest相关使用:http://my.oschina.net/u/2340880/blog/620225. 二.NSURLCache中方法与属性 ? 1 2 3 4

从零开始学Python第八周:详解网络编程基础(socket)_python

一,Socket编程 (1)Socket方法介绍 Socket是网络编程的一个抽象概念.通常我们用一个Socket表示"打开了一个网络链接",而打开一个Socket需要知道目标计算机的IP地址和端口号,再指定协议类型即可. 套接字是一个双向的通信信道的端点.套接字可能在沟通过程,进程之间在同一台机器上,或在不同的计算机之间的进程 要创建一个套接字,必须使用Socket模块的socket.socket()方法 在socket模块中的一般语法: s = socket.socket(sock

Python网络编程学习:Socket基础

1. Socket基础 客户端与服务器连接有两种方式:TCP和UDP,TCP是面向连接的方式(三次握手.四次挥手等),可靠但耗资源,而UDP采用无连接方式,不可靠但速度快.这里面的学问很多,但大部分人知道这些就足够了 2. 一个简单的TCP例子(阻塞方式) 不管是Python还是其它语言,Socket编程几乎都有一个固定模板,下面看一个简单例子,用于计算阶乘和,比如客户端发送5,服务器端返回5!+4!+3!+2!+1!. Server端Python代码: import socket sock =

iOS网络编程-iOS中Socket编程介绍

使用Socket进行C/S结构编程,连接过程   服务器端监听某个端口是否有连接请求.服务器端程序处于堵塞状态,直到客户端向服务器端发出连接请求,服务器端接受请求程序才能向下运行.一旦连接建立起来,通过Socket可以获得输入输出流对象.借助于输入输出流对象就可以实现与客户端的通讯,最后不要忘记关闭Socket和释放一些资源(包括:关闭输入输出流). 客户端流程是先指定要通讯的服务器IP地址.端口和采用的传输协议(TCP或UDP),向服务器发出连接请求,服务器有应答请求之后,就会建立连接.之后与

Java网络编程基础(一) InetAddress类的使用

InetAddress类在网络API套接字编程中扮演了一个重要角色. 参数传递给流套接字类和自寻址套接字类构造器或非构造器方法.InetAddress描述了32位或64位IP地址,要完成这个功能,InetAddress类主要依靠两个支持类Inet4Address 和 Inet6Address,这三个类是继承关系,InetAddrress是父类,Inet4Address 和 Inet6Address是子类. 由于InetAddress类只有一个构造函数,而且不能传递参数,所以不能直接创建InetA

网络编程-c++的socket编程,无客户端连接accept却不阻塞,直接返回了,怎么解决

问题描述 c++的socket编程,无客户端连接accept却不阻塞,直接返回了,怎么解决 以下是服务端的代码: #include "stdafx.h" #include "winsock2.h" #include "iostream" #pragma comment (lib,"ws2_32") using namespace std; int _tmain(int argc, _TCHAR* argv[]) { WSADA

Java网络编程基础(四) ServerSocket类使用

由于SSClient使用了流套接字,所以服务程序也要使用流套接字. 这就要创建一个ServerSocket对象,ServerSocket有几个构造函数,最简单的是ServerSocket(int port),当使用ServerSocket(int port)创建一个ServerSocket对象,port参数传递端口号,这个端口就是服务器监听连接请求的端口,如果在这时出现错误将抛出IOException异常对象,否则将创建ServerSocket对象并开始准备接收连接请求. 接下来服务程序进入无限