失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > Socket编程概念和 Socket之异步TCP客户端断线重连

Socket编程概念和 Socket之异步TCP客户端断线重连

时间:2021-09-25 14:22:10

相关推荐

Socket编程概念和 Socket之异步TCP客户端断线重连

一:什么是SOCKET​​​​​​​

socket的英文原义是“孔”或“插座”。作为进程通信机制,取后一种意思。通常也称作“套接字”,用于描述IP地址和端口,是一个通信链的句柄(其实就是两个程序通信用的)。

socket非常类似于电话插座。以一个电话网为例:电话的通话双方相当于相互通信的2个程序,电话号码就是ip地址。任何用户在通话之前,首先要占有一部电话机,相当于申请一个socket;同时要知道对方的号码,相当于对方有一个固定的socket。然后向对方拨号呼叫,相当于发出连接请求。对方假如在场并空闲,拿起电话话筒,双方就可以正式通话,相当于连接成功。双方通话的过程,是一方向电话机发出信号和对方从电话机接收信号的过程,相当于向socket发送数据和从socket接收数据。通话结束后,一方挂起电话机相当于关闭socket,撤销连接。

1、套接字分类

为了满足不同程序对通信质量和性能的要求,一般的网络系统都提供了以下3种不同类型的套接字,以供用户在设计程序时根据不同需要来选择:

流式套接字(SOCK_STREAM):提供了一种可靠的、面向连接的双向数据传输服务。实现了数据无差错,无重复的发送,内设流量控制,被传输的数据被看做无记录边界的字节流。在TCP/IP协议簇中,使用TCP实现字节流的传输,当用户要发送大批量数据,或对数据传输的可靠性有较高要求时使用流式套接字。

数据报套接字(SOCK_DGRAM):提供了一种无连接、不可靠的双向数据传输服务。数据以独立的包形式被发送,并且保留了记录边界,不提供可靠性保证。数据在传输过程中可能会丢失或重复,并且不能保证在接收端数据按发送顺序接收。在TCP/IP协议簇中,使用UDP实现数据报套接字。

原始套接字(SOCK_RAW):该套接字允许对较低层协议(如IP或ICMP)进行直接访问。一般用于对TCP/IP核心协议的网络编程。

二:SOCKET相关概念

1、端口

在Internet上有很多这样的主机,这些主机一般运行了多个服务软件,同时提供几种服务。每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务(应用程序),因此,在网络协议中使用端口号识别主机上不同的进程。

例如:http使用80端口,FTP使用21端口。

可以认为是设备与外界通讯交流的出口。端口可分为虚拟端口和物理端口,其中虚拟端口指计算 机内部或交换机路由器内的端口,不可见。例如计算机中的80端口、21端口、23端口等。物理端口又称为接口,是可见端口,计算机背板的RJ45网口,交换机路由器集线器等RJ45端口。电话使用RJ11插口也属于物理端口的范畴。

规定一个设备有216个,也就是65536个端口,每个端口对应一个唯一的程序。每个网络程序,无论是客户端还是服务器端,都对应一个或多个特定的端口号。由于0-1024之间多被操作系统占用 ,所以实际编程时一般采用1024以后的端口号

端口详解

端口是指接口电路中的一些寄存器,这些寄存器分别用来存放数据信息、控制信息和状态信息,相应的端口分别称为数据端口、控制端口和状态端口。

电脑运行的系统程序,其实就像一个闭合的圆圈,但是电脑是为人服务的,他需要接受一些指令,并且要按照指令调整系统功能来工作,于是系统程序设计者,就把这个圆圈截成好多段,这些线段接口就叫端口(通俗讲是断口,就是中断),系统运行到这些端口时,一看端口是否打开或关闭,如果关闭,就是绳子接通了,系统往下运行,如果端口是打开的,系统就得到命令,有外部数据输入,接受外部数据并执行.

端口可分为虚拟端口和物理端口

“端口”是英文port的意译,可以认为是设备与外界通讯交流的出口。端口可分为虚拟端口和物理端口,其中虚拟端口指计算机内部或交换机路由器内的端口,不可见。例如计算机中的80端口、21端口、23端口等。物理端口又称为接口,是可见端口,计算机背板的RJ45网口,交换机路由器集线器等RJ45端口。电话使用RJ11插口也属于物理端口的范畴。

硬件端口

CPU通过接口寄存器或特定电路与外设进行数据传送,这些寄存器或特定电路称之为端口。

其中硬件领域的端口又称接口,如:并行端口、串行端口等。

网络端口

在网络技术中,端口(Port)有好几种意思。集线器、交换机、路由器的端口指的是连接其他网络设备的接口,如RJ-45端口、Serial端口等。我们 这里所指的端口不是指物理意义上的端口,而是特指TCP/IP协议中的端口,是逻辑意义上的端口。

软件端口

缓冲区。

TCP端口

TCP[1] :Transmission Control Protocol传输控制协议,TCP是一种面向连接(连接导向)的、可靠的、基于字节流的传输层(Transport layer)通信协议,由IETF的RFC 793说明(specified)。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能,UDP是同一层内另一个重要的传输协议。

UDP端口

UDP[1] :User Datagram Protocol用户数据报协议,UDP是OSI参考模型中一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务。UDP 协议基本上是IP协议与上层协议的接口。UDP协议适用端口分别运行在同一台设备上的多个应用程序。

协议端口

如果把IP地址比作一间房子 ,端口就是出入这间房子的门。真正的房子只有几个门,但是一个IP地址的端口可以有65536(即:2^16)个之多!端口是通过端口号来标记的,端口号只有整数,范围是从0 到65535(2^16-1)。

在Internet上,各主机间通过TCP/IP协议发送和接收数据包,各个数据包根据其目的主机的ip地址来进行互联网络中的路由选择,把数据包顺利的传送到目的主机。大多数操作系统都支持多程序(进程)同时运行,那么目的主机应该把接收到的数据包传送给众多同时运行的进程中的哪一个呢?显然这个问题有待解决,端口机制便由此被引入进来。

本地操作系统会给那些有需求的进程分配协议端口(protocol port,即我们常说的端口),每个协议端口由一个正整数标识,如:80,139,445,等等。当目的主机接收到数据包后,将根据报文首部的目的端口号,把数据发送到相应端口,而与此端口相对应的那个进程将会领取数据并等待下一组数据的到来。说到这里,端口的概念似乎仍然抽象,那么继续跟我来,别走开。

端口其实就是队,操作系统为各个进程分配了不同的队,数据包按照目的端口被推入相应的队中,等待被进程取用,在极特殊的情况下,这个队也是有可能溢出的,不过操作系统允许各进程指定和调整自己的队的大小。

不光接受数据包的进程需要开启它自己的端口,发送数据包的进程也需要开启端口,这样,数据包中将会标识有源端口,以便接受方能顺利地回传数据包到这个端口。

端口详解

每种网络的服务功能都不相同,因此有必要将不同的封包送给不同的服务来处理,当你的主机同时开启了FTP与WWW服务时,别人送来的资料封包,就会依照 TCP 上面的 port 号码来给 FTP 这个服务或者是 WWW 这个服务来处理。

· 每一个 TCP 连接都必须由一端(通常为 client )发起请求,这个 port 通常是随机选择大于 1024 以上(因为0-1023一般被用作知名服务器的端口,被预定,如FTP、HTTP、SMTP等)的 port 号来进行!其 TCP封包会将(且只将) SYN旗标设定起来!这是整个联机的第一个封包;

· 如果另一端(通常为 Server ) 接受这个请求的话(特殊的服务需要以特殊的 port 来进行,例如 FTP 的 port 21 ),则会向请求端送回整个联机的第二个封包!其上除了 SYN旗标之外同时还将 ACK 旗标也设定起来,并同时在本机端建立资源以待联机之需;

· 然后,请求端获得服务端第一个响应封包之后,必须再响应对方一个确认封包,此时封包只带 ACK旗标(事实上,后继联机中的所有封包都必须带有 ACK 旗标);

· 只有当服务端收到请求端的确认( ACK )封包(也就是整个联机的第三个封包)之后,两端的联机才能正式建立。这就是所谓的 TCP 联机的’三次握手( Three-Way Handshake )’的原理。

经过三向交握之后,你的 client 端的 port 通常是高于 1024 的随机取得的 port,至于主机端则视当时的服务是开启哪一个 port 而定,例如 WWW 选择 80 而 FTP 则以 21 为正常的联机信道!

总而言之,我们这里所说的端口,不是计算机硬件的I/O端口,而是软件形式上的概念。根据提供服务类型的不同,端口分为两种,一种是TCP端口,一种是UDP端口。计算机之间相互通信的时候,分为两种方式:一种是发送信息以后,可以确认信息是否到达,也就是有应答的方式,这种方式大多采用TCP协议;一种是发送以后就不管了,不去确认信息是否到达,这种方式大多采用UDP协议。对应这两种协议的服务提供的端口,也就分为TCP端口和UDP端口。

那么,如果攻击者使用软件扫描目标计算机,得到目标计算机打开的端口,也就了解了目标计算机提供了哪些服务。我们都知道,提供服务就一定有服务软件的漏洞,根据这些,攻击者可以达到对目标计算机的初步了解。如果计算机的端口打开太多,而管理者不知道,那么,有两种情况:一种是提供了服务而管理者没有注意,比如安装IIS的时候,软件就会自动增加很多服务,而管理员可能没有注意到;一种是服务器被攻击者安装木马,通过特殊的端口进行通信。这两种情况都是很危险的,说到底,就是管理员不了解服务器提供的服务,减小了系统安全系数。

端口类型

TCP端口和UDP端口。由于TCP和UDP 两个协议是独立的,因此各自的端口号也相互独立,比如TCP有235端口,UDP也 可以有235端口,两者并不冲突。

1.周知端口(Well Known Ports)

周知端口是众所周知的端口号,范围从0到1023,其中80端口分配给WWW服务,21端口分配给FTP服务等。我们在IE的地址栏里输入一个网址的时候是不必指定端口号的,因为在默认情况下WWW服务的端口是“80”

网络服务是可以使用其他端口号的,如果不是默认的端口号则应该在 地址栏上指定端口号,方法是在地址后面加上冒号“:”(半角),再加上端口号。比如使用“8080”作为WWW服务的端口,则需要在地址栏里输入“网址:8080”。

但是有些系统协议使用固定的端口号,它是不能被改变的,比如139 端口专门用于NetBIOS与TCP/IP之间的通信,不能手动改变。

2.动态端口(Dynamic Ports)

动态端口的范围是从49152到65535。之所以称为动态端口,是因为它 一般不固定分配某种服务,而是动态分配。

3.注册端口

端口1024到49151,分配给用户进程或应用程序。这些进程主要是用户选择安装的一些应用程序,而不是已经分配好了公认端口的常用程序。这些端口在没有被服务器资源占用的时候,可以用用户端动态选用为源端口。

端口作用

我们知道,一台拥有IP地址的主机可以提供许多服务,比如Web服务、FTP服务、SMTP服务等,这些服务完全可以通过1个IP地址来实现。那么,主机是怎样区分不同的网络服务呢?显然不能只靠IP地址,因为IP 地址与网络服务的关系是一对多的关系。实际上是通过“IP地址+端口号”来区分不同的服务的。

需要注意的是,端口并不是一一对应的。比如你的电脑作为客户机访 问一台WWW服务器时,WWW服务器使用“80”端口与你的电脑通信,但你的电脑则可能使用“3457”这样的端口。

动态端口(Dynamic Ports)

端口号。

端口在入侵中的作用

有人曾经把服务器比作房子,而把端口比作通向不同房间(服务)的门,如果不考虑细节的话,这是一个不错的比喻。入侵者要占领这间房子,势必要破门而入(物理入侵另说),那么对于入侵者来说,了解房子开了几扇门,都是什么样的门,门后面有什么东西就显得至关重要。

入侵者通常会用扫描器对目标主机的端口进行扫描,以确定哪些端口是开放的,从开放的端口,入侵者可以知道目标主机大致提供了哪些服务,进而猜测可能存在的漏洞,因此对端口的扫描可以帮助我们更好的了解目标主机,而对于管理员,扫描本机的开放端口也是做好安全防范的第一步。

一,认识网卡

网卡(Network Interface Card,简称NIC),也称网络适配器,是电脑与局域网相互连接的设备。无论是普通电脑还 是高端服务器,只要连接到局域网,就都需要安装一块网卡。如果有必要,一台电脑也可以同时安装两块或多块网卡。

一块网卡包括OSI 模型的两个层, 物理层和数据链路层:

1》物理层定义了数据传送与接收所需要的电与光信号、线路状态、时钟基准、数据编码和电路等, 并向数据链路层设备提供标准接口。

2》数据链路层则提供寻址机构、数据帧的构建、数据差错检查、传送控制、向网络层 提供标准的数据接口等功能。

二:网卡的组要作用

网卡的功能主要有两个:

一是将电脑的数据封装为帧,并通过网线(对无线网络来说就是电磁波)将数据发送到网络上去;

二是接收网络上其它设备传过来的帧,并将帧重新组合成数据,发送到所在的电脑中。

网卡能接收所有在网络上传输的 信号,但正常情况下只接受发送到该电脑的帧和广播帧,将其余的帧丢弃。然后,传送到系统CPU 做进一步处理。当电 脑发送数据时,网卡等待合适的时间将分组插入到数据流中。接收系统通知电脑消息是否完整地到达,如果出现问题, 将要求对方重新发送。

————————————————

网络通信三要素:

IP地址(网络上主机设备的唯一标识)端口号(定位程序) 有效端口:0~65535,其中0~1024由系统使用,开发中一般使用1024以上端口.传输协议(用什么样的方式进行交互) 常见协议:TCP(面向连接,提供可靠的服务),UDP(无连接,传输速度快)

2.IP地址

IP地址是一个规定,现在使用的是IPv4,既由4个0-255之间的数字组成,在计算机内部存储时只需要4个字节即可。在计算机中,IP地址是分配给网卡的,每个网卡有一个唯一的IP地址,如果一个计算机有多个网卡,则该台计算机则拥有多个不同的IP地址,在同一个网络内部,IP地址不能相同。IP地址的概念类似于电话号码、身份证这样的概念。

由于IP地址不方便记忆,所以有专门创造了 域名(Domain Name) 的概念,其实就是给IP取一个字符的名字,例如、等。IP和域名之间存在一定的对应关系。如果把IP地址类比成身份证号的话,那么域名就是你的姓名。

其实在网络中只能使用IP地址进行数据传输,所以在传输以前,需要把域名转换为IP,这个由称作DNS的服务器专门来完成。

所以在网络编程中,可以使用IP或域名来标识网络上的一台设备。

2、协议

2.1 TCP:

TCP是一种面向连接的、可靠的,基于字节流的传输层通信协议。为两台主机提供高可靠性的数据通信服务。它可以将源主机的数据无差错地传输到目标主机。当有数据要发送时,对应用进程送来的数据进行分片,以适合于在网络层中传输;当接收到网络层传来的分组时,它要对收到的分组进行确认,还要对丢失的分组设置超时重发等。为此TCP需要增加额外的许多开销,以便在数据传输过程中进行一些必要的控制,确保数据的可靠传输。因此,TCP传输的效率比较低。

2.1.1 TCP的工作过程

TCP是面向连接的协议,TCP协议通过三个报文段完成类似电话呼叫的连接建立过程,这个过程称为三次握手,如图所示:

第一次握手:建立连接时,客户端发送SYN包(SEQ=x)到服务器,并进入SYN_SEND状态,等待服务器确认。

第二次握手:服务器收到SYN包,必须确认客户的SYN(ACK=x+1),同时自己也发送一个SYN包(SEQ=y),即SYN+ACK包,此时服务器进入SYN_RECV状态。

第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ACK=y+1),此包发送完毕,客户端和服务器进入Established状态,完成三次握手。

2.1.2 传输数据

一旦通信双方建立了TCP连接,连接中的任何一方都能向对方发送数据和接收对方发来的数据。TCP协议负责把用户数据(字节流)按一定的格式和长度组成多个数据报进行发送,并在接收到数据报之后按分解顺序重新组装和恢复用户数据。

利用TCP传输数据时,数据是以字节流的形式进行传输的。

2.1.3 连接的终止

建立一个连接需要三次握手,而终止一个连接要经过四次握手,这是由TCP的半关闭(half-close)造成的。具体过程如图所示:

2.1.4 TCP的主要特点

TCP最主要的特点如下。

(1)是面向连接的协议。

(2)端到端的通信。每个TCP连接只能有两个端点,而且只能一对一通信,不能一点对多点直接通信。

(3)高可靠性。通过TCP连接传送的数据,能保证数据无差错、不丢失、不重复地准确到达接收方,并且保证各数据到达的顺序与其发出的顺序相同。

(4)全双工方式传输。

(5)数据以字节流的方式传输。

(6)传输的数据无消息边界。

2.1.5 同步与异步

同步工作方式是指利用TCP编写的程序执行到监听或接收语句时,在未完成工作(侦听到连接请求或收到对方发来的数据)前不再继续往下执行,线程处于阻塞状态,直到该语句完成相应的工作后才继续执行下一条语句。

异步工作方式是指程序执行到监听或接收语句时,不论工作是否完成,都会继续往下执行

2.2 UDP

UDP是一种简单的、面向数据报的无连接的协议,提供的是不一定可靠的传输服务。所谓“无连接”是指在正式通信前不必与对方先建立连接,不管对方状态如何都直接发送过去。这与发手机短信非常相似,只要知道对方的手机号就可以了,不要考虑对方手机处于什么状态。UDP虽然不能保证数据传输的可靠性,但数据传输的效率较高。

2.1.1 UDP与TCP的区别

(1) UDP可靠性不如TCP

TCP包含了专门的传递保证机制,当数据接收方收到发送方传来的信息时,会自动向发送方发出确认消息;发送方只有在接收到该确认消息之后才继续传送其他信息,否则将一直等待直到收到确认信息为止。与TCP不同,UDP并不提供数据传送的保证机制。如果在从发送方到接收方的传递过程中出现数据报的丢失,协议本身并不能做出任何检测或提示。因此,通常人们把UDP称为不可靠的传输协议。

(2) UDP不能保证有序传输

UDP不能确保数据的发送和接收顺序。对于突发性的数据报,有可能会乱序。

2.1.2 UDP的优势

(1) UDP速度比TCP快

由于UDP不需要先与对方建立连接,也不需要传输确认,因此其数据传输速度比TCP快得多。对于强调传输性能而不是传输完整性的应用(比如网络音频播放、视频点播和网络会议等),使用UDP比较合适,因为它的传输速度快,使通过网络播放的视频音质好、画面清晰。

(2) UDP有消息边界

发送方UDP对应用程序交下来的报文,在添加首部后就向下直接交付给IP层。既不拆分,也不合并,而是保留这些报文的边界。使用UDP不需要考虑消息边界问题,这样使得UDP编程相比TCP,在对接收到的数据的处理方面要方便的多。在程序员看来,UDP套接字使用比TCP简单。UDP的这一特征也说明了它是一种面向报文的传输协议。

(3) UDP可以一对多传输

由于传输数据不建立连接,也就不需要维护连接状态(包括收发状态等),因此一台服务器可以同时向多个客户端传输相同的消息。利用UDP可以使用广播或组播的方式同时向子网上的所有客户进程发送消息,这一点也比TCP方便。

其中,速度快是UDP的首要优势

由于TCP协议中植入了各种安全保障功能,在实际执行的过程中会占用大量的系统开销,无疑使速度受到严重影响。反观UDP,由于抛弃了信息可靠传输机制,将安全和排序等功能移交给上层应用完成,极大地降低了执行时间,使速度得到了保证。简而言之,UDP的“理念”就是“不顾一切,只为更快地发送数据”。

(4) Socket与TCP/IP协议的关系

Socket是支持TCP/IP协议的网络通信的基本操作单元。数据链路层、网络层、传输层协议是在内核中实现的,因此操作系统需要实现一组系统调用,使得应用程序能够访问这些协议提供的服务,实现这组系统调用的API有socket。Socket是一套通用网络编程接口,它不但可以访问内核中TCP/IP协议栈,而且还可以访问其他网络协议栈(X.25协议栈、UNIX本地域协议栈等)。

socket与TCP/IP协议族的关系:

由socket定义的一组API提供两点功能:

一是将应用程序数据从用户缓冲区中复制到TCP/UDP内核发送缓冲区以交付内核来发送数据,或者是从内核TCP/UDP接收缓冲区中复制数据到用户缓冲区,以读取数据。

二是应用程序可以通过它们来修改内核中各层协议的某些头部信息或其他数据结构,从而精细地控制底层通信的行为。

三:socket一般应用模式:

根据socket通信基本流程图,总结通信的基本步骤:

服务器端:

第一步:创建一个用于监听连接的Socket对像;

第二步:用指定的端口号和服务器的ip建立一个EndPoint对像;

第三步:用socket对像的Bind()方法绑定EndPoint;

第四步:用socket对像的Listen()方法开始监听;

第五步:接收到客户端的连接,用socket对像的Accept()方法创建一个新的用于和客户端进行通信的socket对像;

第六步:通信结束后一定记得关闭socket;

客户端:

第一步:建立一个Socket对像;

第二步:用指定的端口号和服务器的ip建立一个EndPoint对像;

第三步:用socket对像的Connect()方法以上面建立的EndPoint对像做为参数,向服务器发出连接请求;

第四步:如果连接成功,就用socket对像的Send()方法向服务器发送信息;

第五步:用socket对像的Receive()方法接受服务器发来的信息 ;

第六步:通信结束后一定记得关闭socket;

示例程序:

using System;2 using System.Collections.Generic;3 using ponentModel;4 using System.Data;5 using System.Drawing;6 using System.Linq;7 using ;8 using .Sockets;9 using System.Text;10 using System.Threading.Tasks;11 using System.Windows.Forms;12 using System.Threading;13 using System.IO;14 15 namespace SocketServer16 {17public partial class FrmServer : Form18{19 public FrmServer()20 {21 InitializeComponent();22 }23 24 //定义回调:解决跨线程访问问题25 private delegate void SetTextValueCallBack(string strValue);26 //定义接收客户端发送消息的回调27 private delegate void ReceiveMsgCallBack(string strReceive);28 //声明回调29 private SetTextValueCallBack setCallBack;30 //声明31 private ReceiveMsgCallBack receiveCallBack;32 //定义回调:给ComboBox控件添加元素33 private delegate void SetCmbCallBack(string strItem);34 //声明35 private SetCmbCallBack setCmbCallBack;36 //定义发送文件的回调37 private delegate void SendFileCallBack(byte[] bf);38 //声明39 private SendFileCallBack sendCallBack;40 41 //用于通信的Socket42 Socket socketSend;43 //用于监听的SOCKET44 Socket socketWatch;45 46 //将远程连接的客户端的IP地址和Socket存入集合中47 Dictionary<string, Socket> dicSocket = new Dictionary<string, Socket>();48 49 //创建监听连接的线程50 Thread AcceptSocketThread;51 //接收客户端发送消息的线程52 Thread threadReceive;53 54 /// <summary>55 /// 开始监听56 /// </summary>57 /// <param name="sender"></param>58 /// <param name="e"></param>59 private void btn_Start_Click(object sender, EventArgs e)60 {61 //当点击开始监听的时候 在服务器端创建一个负责监听IP地址和端口号的Socket62 socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);63 //获取ip地址64 IPAddress ip=IPAddress.Parse(this.txt_IP.Text.Trim());65 //创建端口号66 IPEndPoint point=new IPEndPoint(ip,Convert.ToInt32(this.txt_Port.Text.Trim()));67 //绑定IP地址和端口号68 socketWatch.Bind(point);69 this.txt_Log.AppendText("监听成功"+" \r \n");70 //开始监听:设置最大可以同时连接多少个请求71 socketWatch.Listen(10);72 73 //实例化回调74 setCallBack = new SetTextValueCallBack(SetTextValue);75 receiveCallBack = new ReceiveMsgCallBack(ReceiveMsg);76 setCmbCallBack = new SetCmbCallBack(AddCmbItem);77 sendCallBack = new SendFileCallBack(SendFile);78 79 //创建线程80 AcceptSocketThread = new Thread(new ParameterizedThreadStart(StartListen));81 AcceptSocketThread.IsBackground = true;82 AcceptSocketThread.Start(socketWatch);83 }84 85 /// <summary>86 /// 等待客户端的连接,并且创建与之通信用的Socket87 /// </summary>88 /// <param name="obj"></param>89 private void StartListen(object obj)90 {91 Socket socketWatch = obj as Socket;92 while (true)93 {94 //等待客户端的连接,并且创建一个用于通信的Socket95 socketSend = socketWatch.Accept();96 //获取远程主机的ip地址和端口号97 string strIp=socketSend.RemoteEndPoint.ToString();98 dicSocket.Add(strIp, socketSend);99 this.cmb_Socket.Invoke(setCmbCallBack, strIp);100 string strMsg = "远程主机:" + socketSend.RemoteEndPoint + "连接成功";101 //使用回调102 txt_Log.Invoke(setCallBack, strMsg);103 104 //定义接收客户端消息的线程105 Thread threadReceive = new Thread(new ParameterizedThreadStart(Receive));106 threadReceive.IsBackground = true;107 threadReceive.Start(socketSend);108 109 }110 }111 112 113 114 /// <summary>115 /// 服务器端不停的接收客户端发送的消息116 /// </summary>117 /// <param name="obj"></param>118 private void Receive(object obj)119 {120 Socket socketSend = obj as Socket;121 while (true)122 {123 //客户端连接成功后,服务器接收客户端发送的消息124 byte[] buffer = new byte[2048];125 //实际接收到的有效字节数126 int count = socketSend.Receive(buffer);127 if (count == 0)//count 表示客户端关闭,要退出循环128 {129 break;130 }131 else132 {133 string str = Encoding.Default.GetString(buffer, 0, count);134 string strReceiveMsg = "接收:" + socketSend.RemoteEndPoint + "发送的消息:" + str;135 txt_Log.Invoke(receiveCallBack, strReceiveMsg);136 }137 }138 }139 140 /// <summary>141 /// 回调委托需要执行的方法142 /// </summary>143 /// <param name="strValue"></param>144 private void SetTextValue(string strValue)145 {146 this.txt_Log.AppendText(strValue + " \r \n");147 }148 149 150 private void ReceiveMsg(string strMsg)151 {152 this.txt_Log.AppendText(strMsg + " \r \n");153 }154 155 private void AddCmbItem(string strItem)156 {157 this.cmb_Socket.Items.Add(strItem);158 }159 160 /// <summary>161 /// 服务器给客户端发送消息162 /// </summary>163 /// <param name="sender"></param>164 /// <param name="e"></param>165 private void btn_Send_Click(object sender, EventArgs e)166 {167 try168 {169 string strMsg = this.txt_Msg.Text.Trim();170 byte[] buffer = Encoding.Default.GetBytes(strMsg);171 List<byte> list = new List<byte>();172 list.Add(0);173 list.AddRange(buffer);174 //将泛型集合转换为数组175 byte[] newBuffer = list.ToArray();176 //获得用户选择的IP地址177 string ip = this.cmb_Socket.SelectedItem.ToString();178 dicSocket[ip].Send(newBuffer);179 }180 catch (Exception ex)181 {182 MessageBox.Show("给客户端发送消息出错:"+ex.Message);183 }184 //socketSend.Send(buffer);185 }186 187 /// <summary>188 /// 选择要发送的文件189 /// </summary>190 /// <param name="sender"></param>191 /// <param name="e"></param>192 private void btn_Select_Click(object sender, EventArgs e)193 {194 OpenFileDialog dia = new OpenFileDialog();195 //设置初始目录196 dia.InitialDirectory = @"";197 dia.Title = "请选择要发送的文件";198 //过滤文件类型199 dia.Filter = "所有文件|*.*";200 dia.ShowDialog();201 //将选择的文件的全路径赋值给文本框202 this.txt_FilePath.Text = dia.FileName;203 }204 205 /// <summary>206 /// 发送文件207 /// </summary>208 /// <param name="sender"></param>209 /// <param name="e"></param>210 private void btn_SendFile_Click(object sender, EventArgs e)211 {212 List<byte> list = new List<byte>();213 //获取要发送的文件的路径214 string strPath = this.txt_FilePath.Text.Trim();215 using (FileStream sw = new FileStream(strPath,FileMode.Open,FileAccess.Read))216 {217 byte[] buffer = new byte[2048];218 int r = sw.Read(buffer, 0, buffer.Length);219 list.Add(1);220 list.AddRange(buffer);221 222 byte[] newBuffer = list.ToArray();223 //发送224 //dicSocket[cmb_Socket.SelectedItem.ToString()].Send(newBuffer, 0, r+1, SocketFlags.None);225 btn_SendFile.Invoke(sendCallBack, newBuffer);226 227 228 }229 230 }231 232 private void SendFile(byte[] sendBuffer)233 {234 235 try236 {237 dicSocket[cmb_Socket.SelectedItem.ToString()].Send(sendBuffer, SocketFlags.None);238 }239 catch (Exception ex)240 {241 MessageBox.Show("发送文件出错:"+ex.Message);242 }243 }244 245 private void btn_Shock_Click(object sender, EventArgs e)246 {247 byte[] buffer = new byte[1] { 2};248 dicSocket[cmb_Socket.SelectedItem.ToString()].Send(buffer);249 }250 251 /// <summary>252 /// 停止监听253 /// </summary>254 /// <param name="sender"></param>255 /// <param name="e"></param>256 private void btn_StopListen_Click(object sender, EventArgs e)257 {258 socketWatch.Close();259 socketSend.Close();260 //终止线程261 AcceptSocketThread.Abort();262 threadReceive.Abort();263 }264}265 }

1 using System;2 using System.Collections.Generic;3 using ponentModel;4 using System.Data;5 using System.Drawing;6 using System.Linq;7 using System.Text;8 using System.Threading.Tasks;9 using System.Windows.Forms;10 using .Sockets;11 using ;12 using System.Threading;13 using System.IO;14 15 namespace SocketClient16 {17public partial class FrmClient : Form18{19 public FrmClient()20 {21 InitializeComponent();22 }23 24 //定义回调25 private delegate void SetTextCallBack(string strValue);26 //声明27 private SetTextCallBack setCallBack;28 29 //定义接收服务端发送消息的回调30 private delegate void ReceiveMsgCallBack(string strMsg);31 //声明32 private ReceiveMsgCallBack receiveCallBack;33 34 //创建连接的Socket35 Socket socketSend;36 //创建接收客户端发送消息的线程37 Thread threadReceive;38 39 /// <summary>40 /// 连接41 /// </summary>42 /// <param name="sender"></param>43 /// <param name="e"></param>44 private void btn_Connect_Click(object sender, EventArgs e)45 {46 try47 {48 socketSend = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);49 IPAddress ip = IPAddress.Parse(this.txt_IP.Text.Trim());50 socketSend.Connect(ip, Convert.ToInt32(this.txt_Port.Text.Trim()));51 //实例化回调52 setCallBack = new SetTextCallBack(SetValue);53 receiveCallBack = new ReceiveMsgCallBack(SetValue);54 this.txt_Log.Invoke(setCallBack, "连接成功");55 56 //开启一个新的线程不停的接收服务器发送消息的线程57 threadReceive = new Thread(new ThreadStart(Receive));58 //设置为后台线程59 threadReceive.IsBackground = true;60 threadReceive.Start();61 }62 catch (Exception ex)63 {64 MessageBox.Show("连接服务端出错:" + ex.ToString());65 }66 }67 68 /// <summary>69 /// 接口服务器发送的消息70 /// </summary>71 private void Receive()72 {73 try74 {75 while (true)76 {77 byte[] buffer = new byte[2048];78 //实际接收到的字节数79 int r = socketSend.Receive(buffer);80 if (r == 0)81 {82break;83 }84 else85 {86//判断发送的数据的类型87if (buffer[0] == 0)//表示发送的是文字消息88{89 string str = Encoding.Default.GetString(buffer, 1, r - 1);90 this.txt_Log.Invoke(receiveCallBack, "接收远程服务器:" + socketSend.RemoteEndPoint + "发送的消息:" + str);91}92//表示发送的是文件93if (buffer[0] == 1)94{95 SaveFileDialog sfd = new SaveFileDialog();96 sfd.InitialDirectory = @"";97 sfd.Title = "请选择要保存的文件";98 sfd.Filter = "所有文件|*.*";99 sfd.ShowDialog(this);100 101 string strPath = sfd.FileName;102 using (FileStream fsWrite = new FileStream(strPath, FileMode.OpenOrCreate, FileAccess.Write))103 {104 fsWrite.Write(buffer, 1, r - 1);105 }106 107 MessageBox.Show("保存文件成功");108}109 }110 111 112 }113 }114 catch (Exception ex)115 {116 MessageBox.Show("接收服务端发送的消息出错:" + ex.ToString());117 }118 }119 120 121 private void SetValue(string strValue)122 {123 this.txt_Log.AppendText(strValue + "\r \n");124 }125 126 /// <summary>127 /// 客户端给服务器发送消息128 /// </summary>129 /// <param name="sender"></param>130 /// <param name="e"></param>131 private void btn_Send_Click(object sender, EventArgs e)132 {133 try134 {135 string strMsg = this.txt_Msg.Text.Trim();136 byte[] buffer = new byte[2048];137 buffer = Encoding.Default.GetBytes(strMsg);138 int receive = socketSend.Send(buffer);139 }140 catch (Exception ex)141 {142 MessageBox.Show("发送消息出错:" + ex.Message);143 }144 }145 146 private void FrmClient_Load(object sender, EventArgs e)147 {148 Control.CheckForIllegalCrossThreadCalls = false;149 }150 151 /// <summary>152 /// 断开连接153 /// </summary>154 /// <param name="sender"></param>155 /// <param name="e"></param>156 private void btn_CloseConnect_Click(object sender, EventArgs e)157 {158 //关闭socket159 socketSend.Close();160 //终止线程161 threadReceive.Abort();162 }163}164 }

代码测试:

using System;using System.Collections.Generic;using System.Linq;using ;using .Sockets;using System.Text;using System.Threading;using System.Threading.Tasks;namespace Socket{class ClientSocket{public class SocketClientManager{public delegate int ConnectStateEventHandler(int a, int b);public event ConnectStateEventHandler ConnectedEvent;//连接成功public event ConnectStateEventHandler DisConnectedEvent;//连接失败public delegate void ReceiveMsgEventHandler(byte[] order);public event ReceiveMsgEventHandler ReceiveMsgEvent;static .Sockets.Socket _socket = null;static IPEndPoint iep = null;static bool isConnecting = false;static bool isConnected = false;public SocketClientManager(string strIP, int port){_socket = new .Sockets.Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);IPAddress IP = IPAddress.Parse(strIP.Trim());iep = new IPEndPoint(IP, port);}public void Start(){isConnecting = true;ConnectedEvent += send1;DisConnectedEvent += send1;int aa = DisConnectedEvent(1, 2);Thread t = new Thread(Connect);t.IsBackground = true;t.Start();}private void Connect(){while (isConnecting){try{if (!IsSocketConnected(_socket) && DisConnectedEvent != null){}}catch { }finally{Thread.Sleep(500);}}}private void ReceiveMsg(){try{while (isConnected){byte[] buffer = new byte[32];int count = _socket.Receive(buffer);if (count > 0 && ReceiveMsgEvent != null){ReceiveMsgEvent(buffer);}}}catch{}}public void SendMsg(byte[] order){_socket.Send(order, order.Length, SocketFlags.None);}private bool IsSocketConnected(.Sockets.Socket s){}private static int send1(int a, int b){int aa = 0;return aa;}}}}

如果觉得《Socket编程概念和 Socket之异步TCP客户端断线重连》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。