1、理解socket
1)、
Socket接口是TCP/IP网络的应用程序接口(API)。Socket接口定义了许多函数和例程,程序员可以用它们来开发TCP/IP网络应用程序。Socket可以看成是网络通信上的一个端点,也就是说,网络通信包括两台主机或两个进程,通过网络传递它们之间的数据。为了进行网络通信,程序在网络对话的每一端都需要一个Socket。

2)、TCP/IP传输层使用协议端口将数据传送给一台主机的特定应用程序,从网络的观点看,协议端口是一个应用程序的进程地址。当传输层模块的网络软件模块要与另一个程序通信时,它将使用协议端口。Socket是运行在传输层的API,所以在使用Socket建立连接发送数据时,要指定一个端口给它。

3)、根据通信性质的不同,可以把Socket分成3类:
① Stream socket(流套接字):该类Socket提供双向、有序、无重复的数据流服务,它使用于处理大量网络数据;
② DgramSocket(数据报套接字):该类Socket支持双向的数据流,但不保证数据传输的可靠性、有序性和无重复性,也就是说一个从DgramSocket接收信息的进程,有可能发现信息重复或和发出时顺序不同的情况;
③ Raw Socket(原始套接字):该类 Socket可以访问底层的协议。

4)、使用Socket接口进行网络通信的过程如图5-1所示,简要步骤如下:
① 建立一个socket;
② 按要求配置socket,即将socket连接到远程主机或给socket指定一个本地协议端口;
③ 按要求通过socket发送和接收数据;
④ 关闭此socket。

2、C#编程要点
根据上述的步骤,使用C#设计通过Socket实现点对点通信的程序需要掌握4个编程要点:socket的构造、socket的配置和连接、数据的发送和接收、socket的关闭。

1)、命名空间的添加
using System.Net;
using System.Net.Socket; //用于操纵Socket类

2)、构造一个新的socket对象
在C#中,采用socket函数构造一个socket对象,socket函数原型如下:
public Socket (AddressFamily addressFamily, SocketType socketType,ProtocolType protocolType);

AddressFamily成员指定socket用来解析地址的寻址方案。例如,InterNetwork表示需要一个IP版本4的地址,InterNetworkV6表示IP版本6的地址。

SocketType参数指定Socket的类型。例如,Raw支持对基础传输协议的访问,Stream支持可靠、双向、基于连接的数据流。

ProtocolType指定Socket类支持的协议。例如,IP表示网际协议,TCP表示传输控制协议。
注意:3个参数不是独立的,有些地址族会限制可与其一起使用的协议,并且套接字类型在协议中通常是隐式的。如果地址族、套接字类型和协议类型不匹配将导致无效的Socket。

例如,构造一个新的Socket 对象,采用IP版本4的地址,支持可靠、双向、基于连接的数据流,采用TCP协议:
Socket sock=newSocket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp)

3)、Socket的配置和连接
为了将Socket和主机关联,必须将主机表示成网络端点的形式。在C#中,采用IPEndPoint类表示网络端点,IPEndPoint函数原型如下:
① public IPEndPoint(IPAddress address,int port)
参数:address表示IP地址,port表示提供服务的端口号。
在服务器端将构造socket对象与表示服务器的网络端点绑定,然后开始进行监听,在收到连接请求后建立连接。

主要用语以下3个函数:Bind、Listen和Accept。
函数原型如下:
② public void Bind(EndPoint localEP)
参数localEP为与socket关联的网络端点。

③ public void Listen(int backlog)
参数backlog为挂连接队列的最大长度

④ public Socket Accept()
返回值为socket,用于处理接收的连接请求。

:构造一个服务器的网络端点,对socket进行绑定,开始监听,接受连接请求。

IPAddress ServerIP=IPAdress.Parse("192.18.16.186");//设定服务器IP地址
IPEndPoint Server=new IPEndPoint(ServerIP,8866); //生成服务器网络端点
Socket Sock=newSocket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp)// 构造一个socket
Sock.Bind(Server); //将socket和服务器绑定
Sock.Listen(8); //开始监听,允许连接队列的长度为8
Socket connectsock=sock.Accept(); //返回socket,用于同连接请求的socket通信

客户端向服务器端发出连接请求,用到Connect函数,Connect函数原型如下:
⑤ public Connect(EndPoint remoteEP)
参数:remoteEP表示要连接的服务器端点。例如向服务器端发出连接请求,服务器IP为ServerIP,端口为Port。
IPEndPoint Server=new IPEndPoint(ServerIP,Port);//定义要连接的服务器端点
Sock=newSocket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp)// 构造一个socket
Sock.Connect(Server); // 与服务器连接

4)、数据的传送和接收
使用两个用于传送和接收数据的函数:Send、Receive。函数原型如下:
◆ public int Send(byte[] buffer,int size,SocketFlagsocketFlags)
参数:buffer表示要发送的数据;size表示要发送数据的大小;socketFlags提供Socket消息的常数值,具有允许按位组合其成员值的属性。
返回值为发送到socket的字节数。

◆public int Receive(byte[] buffer,int size,SocketFlagesocketFlags)
参数:buffer表示接收到的数据的存储位置;size表示要接收数据的大小;socketFlags提供Socket消息的常数值,具有允许按位组合其成员值的属性。
返回值为接收到socket的字节数。

:接收来自客户端的数据,同时将该数据返回到客户端。Socket是前面例子中定义和设置好的。

public static string data=null; //定义字符串变量存放接收到的信息
bytes=new byte[1024];
int bytesRec=connectsock.Receive(bytes,bytes.Lentgh,0);//接受来自客户端的数据
Console.WriteLine("Text received:{0}",bytes); //显示接收到的数据
connectsock.Send(bytes,bytes.Length,0); //发送数据到客户端

5)、socket的关闭
在socket关闭之前,要确保已经发送和接收完所有挂起的数据,因此在关闭socket之前,要先调用Shutdown,函数原型如下:
◆public void Shutdown(SocketShutdown how)
参数:SocketShutdown指定不再允许的操作。成员名称:Both禁止socket发送和接收;Receive禁止socket接收数据;Send禁止socket发送数据。

采用close函数强制关闭Socket连接。函数原型如下:
◆public void clsoe()
当该套接字被关闭时,Connected属性将被设置为false。

Socket的Send,Recv的长度问题:

一个包没有固定长度,以太网限制在46-1500字节,1500就是以太网的MTU,超过这个量,TCP会为IP数据报设置偏移量进行分片传输,现在一般可允许应用层设置8k(NTFS系统)的缓冲区,8k的数据由底层分片,而应用层看来只是一次发送。
windows的缓冲区经验值是4k。
Socket本身分为两种,流(TCP)和数据报(UDP),你的问题针对这两种不同使用而结论不一样。甚至还和你是用阻塞、还是非阻塞Socket来编程有关。
1、通信长度,这个是你自己决定的,没有系统强迫你要发多大的包,实际应该根据需求和网络状况来决定。对于TCP,这个长度可以大点,但要知道,Socket内部默认的收发缓冲区大小大概是8K,你可以用SetSockOpt来改变。但对于UDP,就不要太大,一般在1024至10K。注意一点,你无论发多大的包,IP层和链路层都会把你的包进行分片发送,一般局域网就是1500左右,广域网就只有几十字节。分片后的包将经过不同的路由到达接收方,对于UDP而言,要是其中一个分片丢失,那么接收方的IP层将把整个发送包丢弃,这就形成丢包。显然,要是一个UDP发包佷大,它被分片后,链路层丢失分片的几率就佷大,你这个UDP包,就佷容易丢失,但是太小又影响效率。最好可以配置这个值,以根据不同的环境来调整到最佳状态。
send()函数返回了实际发送的长度,在网络不断的情况下,它绝不会返回(发送失败的)错误,最多就是返回0。对于TCP你可以写一个循环发送。当send函数返回SOCKET_ERROR时,才标志着有错误。但对于UDP,你不要写循环发送,否则将给你的接收带来极大的麻烦。所以UDP需要用SetSockOpt来改变Socket内部Buffer的大小,以能容纳你的发包。明确一点,TCP作为流,发包是不会整包到达的,而是源源不断的到,那接收方就必须组包。而UDP作为消息或数据报,它一定是整包到达接收方。
2、关于接收,一般的发包都有包边界,首要的就是你这个包的长度要让接收方知道,于是就有个包头信息,对于TCP,接收方先收这个包头信息,然后再收包数据。一次收齐整个包也可以,可要对结果是否收齐进行验证。这也就完成了组包过程。UDP,那你只能整包接收了。要是你提供的接收Buffer过小,TCP将返回实际接收的长度,余下的还可以收,而UDP不同的是,余下的数据被丢弃并返回WSAEMSGSIZE错误。注意TCP,要是你提供的Buffer佷大,那么可能收到的就是多个发包,你必须分离它们,还有就是当Buffer太小,而一次收不完Socket内部的数据,那么Socket接收事件(OnReceive),可能不会再触发,使用事件方式进行接收时,密切注意这点。这些特性就是体现了流和数据包的区别。
补充一点,接收BuffSize >= 发送BuffSize >=实际发送Size,对于内外部的Buffer都适用,上面讲的主要是Socket内部的Buffer大小关系。
3、TCP是有多少就收多少,如果没有当然阻塞Socket的recv就会等,直到有数据,非阻塞Socket不好等,而是返回WSAEWOULDBLOCK。UDP,如果没有数据,阻塞Socket就会等,非阻塞Socket也返回WSAEWOULDBLOCK。如果有数据,它是会等整个发包到齐,并接收到整个发包,才返回。

[转] C#.Net Socket网络通讯编程总结的更多相关文章

  1. Socket网络通讯开发总结之:Java 与 C进行Socket通讯 + [备忘] Java和C之间的通讯

    Socket网络通讯开发总结之:Java 与 C进行Socket通讯 http://blog.sina.com.cn/s/blog_55934df80100i55l.html (2010-04-08 ...

  2. [dotnet core]使用Peach简化Socket网络通讯协议开发

    Peach是基于DotNetty的Socket网络通讯帮助类库,可以帮助开发者简化使用DotNetty,关于DotNetty可参考我之前的这篇文章. Peach内置实现了一个基于文本协议的Comman ...

  3. Socket网络通讯

    网络编程 使用C#进行网络编程时,通常都需要用到System.Net命名空间.System.Net.Sockets命名空间和System.Net.Mail命名空间: 1. System.Net命名空间 ...

  4. JAVA TCP/IP网络通讯编程(一)

    一个实例通过client端和server端通讯 客户端发送:“我是客户端,请多关照” 服务端回复:“收到来自于"+s.getInetAddress().getHostName()+" ...

  5. Socket网络通讯开发总结之:Java 与 C进行Socket通讯(转)

    先交待一下业务应用背景:服务端:移动交费系统:基于C语言的Unix系统客户端:增值服务系统:基于Java的软件系统通迅协议:采用TCP/IP协议,使用TCP以异步方式接入数据传输:基于Socket流的 ...

  6. JAVA TCP/IP网络通讯编程(二)

    一个实例通过client端和server端通讯 客户端通过TCP/IP传输资源文件,比如图片,文字,音频,视频等..... 服务端接受到文件存入本地磁盘,返回接受到:“收到来自于"+s.ge ...

  7. C++ 用libcurl库进行http 网络通讯编程

      一.LibCurl基本编程框架libcurl是一个跨平台的网络协议库,支持http, https, ftp, gopher, telnet, dict, file, 和ldap 协议.libcur ...

  8. MFC socket网络通讯核心代码

    服务器: AfxSocketInit();//初始化,必须执行这个函数socket才能正常执行 server.Create(10086); server.Listen(10); while(1) { ...

  9. bsd socket 网络通讯必备工具类

    传输数据的时候都要带上包头,包头有简单的又复杂的,简单的只要能指明数据的长度就够了. 这里我写了一个工具类,可以方便地将整型的数据长度转换为长度为 4 的字节数组. 另一方面,可以方便的将长度为 4 ...

随机推荐

  1. InputStream和OutputStream

    1.在java中stream代表一种数据流(源),javaio的底层数据元,---(想像成水龙头) 2.任何有能力产生数据流(源)的javaio对象就可以看作是一个InputStream对象既然它能产 ...

  2. jquery validation plugin 使用

    <!DOCTYPE html> <!-- To change this license header, choose License Headers in Project Prope ...

  3. Spring、Spring自动扫描和管理Bean

    Spring2.5为我们引入了组件自动扫描机制,它可以在类路径下寻找标记了@Component.@Service.@Controller.@Repository注解的类,并把这些类纳入到spring容 ...

  4. 学习DTD和Schema的几个例子

    可以使用DTD(文档类型定义)来规定xml文档构建模块,可用起对xml文件进行验证.具体用法转:http://www.w3school.com.cn/dtd/dtd_intro.asp.同样也可以用S ...

  5. Java RMI 学习笔记

    概况 功能:提供了客户辅助对象和服务辅助对象,为客户辅助对象创建和服务辅助对象形同的方法. 优点:客户不必写任何网络或I/O代码,调用远程方法就和运行在客户自己的本地JVM上对对象进行的正常方法一样. ...

  6. leetcode Single Number python

    #question : Given an array of integers, every element appears twice except for one. Find that single ...

  7. struts2 + jquery 开发环境下的ajax构建方法(action写法 + struts.xml配置 + js调用代码)

    1.action写法 public class RegisterAction extends ActionSupport { private InputStream inputStream; /** ...

  8. 判断浏览器及设备的打开方式,自动跳转app中

    如果安装了APP则自动条状app,如果没安装则自动跳转下载页面 <head> 放在head中加载 <script> function redirect() { var appU ...

  9. You have not agreed to the Xcode license.

    You have not agreed to the Xcode license. Before running the installer again please agree to the lic ...

  10. 线程间使用socket通信的计算器

    该程序是处理平时的算数运算,程序也没有什么特别之处,只是将所有运算分开运算,每个函数(线程)处理不同的运算符号里面的运算,然后将所有结果都汇总到main函数中进行最后汇总(相加减)运算,每个函数内都处 ...