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. MSSQL 获取指定日期所在星期的第一天和最后一天日期 获取指定日期坐在月的第一天和最后一天

    ufn_GetWeekFirstAndEndDay    获取指定日期所在星期的第一天和最后一天日期 ALTER FUNCTION [dbo].[ufn_GetWeekFirstAndEndDay]( ...

  2. iOS 开发常用设置

    1. iOS设置app应用程序文件共享 设置流程 xcode 打开项目----在 info.plist 文件,添加 UIFileSharingEnabled 并设置属性为 YES, 在app内部,将您 ...

  3. 设置dialog显示,自定义时间到后dialog消失

    方法一: public class MyDialog extends Dialog { private int FLAG_DISMISS = 1; private boolean flag = tru ...

  4. BZOJ 2878: [Noi2012]迷失游乐园( 树形dp )

    一棵树的话直接树形dp(求出往下走和往上走的期望长度). 假如是环套树, 环上的每棵树自己做一遍树形dp, 然后暴力枚举(环上的点<=20)环上每个点跑经过环上的路径就OK了. -------- ...

  5. JVM学习之JVM1.6 GC详解

    转自:http://www.cnblogs.com/ggjucheng/p/3977384.html,多谢分享 前言  JVM GC是JVM的内存回收算法,调整JVM GC(Garbage Colle ...

  6. C#正则提取HTML中img的url值

    /// <summary> /// 取得HTML中所有图片的 URL. /// </summary> /// <param name="sHtmlText&qu ...

  7. java/php/c#版rsa签名以及验签实现

    本文为转载,请转载请注明地址: 原文地址为        http://xw-z1985.iteye.com/blog/1837376 在开放平台领域,需要给isv提供sdk,签名是Sdk中需要提供的 ...

  8. ScrollView属性fillViewport解决android布局不能撑满全屏的问题

    转:http://blog.sina.com.cn/s/blog_6cf2ea6a0102v61f.html 开发项目中遇到一个问题,布局高度在某些国产酷派小屏幕手机上高度不够全部显示,于是使用了Sc ...

  9. Could not find class 'XXX.activity‘', referenced from method 'YYYY'

    Could not find class 'XXX.activity‘', referenced from method 'YYYY'的解决方案: 出现这个错误.是由于eclipse升级ADT所导致的 ...

  10. Microsoft.AlphaImageLoader滤镜讲--透明处理<转>

    Microsoft.AlphaImageLoader是IE滤镜的一种,其主要作用就是对图片进行透明处理.虽然FireFox和IE7以上的IE浏览器已经支持透明的PNG图片,但是就IE5-IE6而言还是 ...