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. Java_SSH项目主要步骤记录

    建立Spring-Struts-Hibernate的步骤整理 1. 建立web project 2. 建立hernate, action, service包 3. 右击项目,add myeclipse ...

  2. CC++初学者编程教程(8) VS2013配置编程助手与QT

    1. 2. 配置编程助手 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26 ...

  3. 【Python】iiacm_filemaker ——简易的.cpp文件创建即初始化脚本,ACMer专用

    代码已全部重写,上次写的真是不忍直视…… 今天刚刚接触Python,本着学以致用的原则,就写了一个按照要求自动生成.cpp文件并初始化头文件的脚本. 确定你的linux中安装了Python,将下面的代 ...

  4. centos6.5 eclipse C/C++开发环境

  5. SpringMVC中的异步提交表单

    1.前言 近期在做一个项目,前台框架用的是EasyUI+SpringMVC,因为对SpringMVC不太了解,所以刚開始接触的时候有点吃力,在此通过一个EasyUi中的DataGrid表格来总结一下. ...

  6. 实现ModelDriver接口的功能

    ModelDriver接口 来自com.opensymphony.xwork2.ModelDriven.是xwork-2.1.2-750.jar包的东西. 下面是源码: package com.ope ...

  7. HTML meta refresh 刷新与跳转(重定向)页面

    <meta http-equiv="refresh" content="5; url=http://www.cnblogs.com/" /> 可用于 ...

  8. Mysql中文乱码问题完美解决方案[转]

    原文地址 MySQL会出现中文乱码的原因不外乎下列几点:1.server本身设定问题,例如还停留在latin12.table的语系设定问题(包含character与collation)3.客户端程式( ...

  9. JavaScript装逼指南

    如何写JavaScript才能逼格更高呢?怎样才能组织JavaScript才能让别人一眼看出你不简单呢?是否很期待别人在看完你的代码之后感叹一句“原来还可以这样写”呢?下面列出一些在JavaScrip ...

  10. jQuery相关知识

    1.jQuery中$符号有何作用? $作为jQuery的别名,如$(document).ready() 即是 jQuery(document).ready() 2.jQuery选择器有哪几种? 基本选 ...