C#のsocket通信

博主要做一个手机和电脑端(C#)通讯的程序,便览了网络上关乎socket的东西。但是接收文件的时候卡住了,怎么也接收不全。后来做了分片处理,如果分片,发送的时候就会有不同的socket(客户端开发不是我,故我不能控制人家怎么发),结果撞山了。

因为发送的时候for循环发,导致不是有重帧就是丢失,故进行了深入的研究。

1.Socket

Socket包括Ip地址和端口号两部分,程序通过Socket来通信,Socket相当于操作系统的一个组件。Socket作为进程之间通信机制,通常也称作”套接字”,用于描述IP地址和端口号,是一个通信链的句柄。说白了,就是两个程序通信用的。

更深刻理解引用一个同行的博文:http://blog.csdn.net/jiajia4336/article/details/8798421

生活案例对比:

Socket之间的通信可以类比生活中打电话的案例。任何用户在通话之前,首先要占有一部电话机,相当于申请一个Socket,同时要知道对方的号码,相当于对方有一个固定的Socket,然后向对方拨号呼叫,相当于发出连接请求。假如对方在场并空闲,拿起 电话话筒,双方就可以进行通话了。双方的通话过程,是一方向电话机发出信号和对方从电话机接收信号的过程,相当于向socket发送数据和从socket接收数据。通话结束后,一方挂起电话机,相当于关闭socket,撤销连接。

注意:Socket不仅可以在两台电脑之间通信,还可以在同一台电脑上的两个程序间通信。

2,端口进阶(深入)

端口号范围:0-65535,总共能表示65536个数。

        按端口号可分为3大类

  (1)公认端口(WellKnownPorts):从0到1023,它们紧密绑定(binding)于一些服务。通常这些端口的通讯明确表明了某种服务的协议。例如:80端口实际上总是HTTP通讯。

  (2)注册端口(RegisteredPorts):从1024到49151。它们松散地绑定于一些服务。也就是说有许多服务绑定于这些端口,这些端口同样用于许多其它目的。例如:许多系统处理动态端口从1024左右开始。

  (3)动态和/或私有端口(Dynamicand/orPrivatePorts):从49152到65535。理论上,不应为服务分配这些端口。实际上,机器通常从1024起分配动态端口。

通过IP地址确定了网络中的一台电脑后,该电脑上可能提供很多提供服务的应用,每一个应用都对应一个端口。

在Internet上有很多这样的主机,这些主机一般运行了多个服务软件 ,同时提供几种服务,每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务(应用程序)

例如:http 使用80端口,   ftp使用21端口     smtp使用25端口

3.Socket分类

Socket主要有两种类型:

  1. 流式Socket

是一种面向连接的Socket,针对于面向连接的TCP服务应用,安全,但是效率低

2,数据报式Socket

是一种无连接的Socket,对应于无连接的UDP服务应用,不安全,但效率高

4. Socket一般应用模式(服务器端和客户端)

服务器端的Socket(至少需要两个)

01.一个负责接收客户端连接请求(但不负责与客户端通信)

02.每成功接收到客户端的连接便在服务器端产生一个对应的复杂通信的Socket

021.在接收到客户端连接时创建

022. 为每个连接成功的客户端请求在服务器端都创建一个对应的Socket(负责和客户端通信)

客户端的Socket

  1. 必须指定要连接的服务器地址和端口
  2. 通过创建一个Socket对象来初始化一个到服务器端的TCP连接

通过上图,我们可以看出,首先服务器会创建一个负责监听的socket,然后客户端通过socket连接到服务器指定端口,最后服务器端负责监听的socket,监听到客户端有连接过来了,就创建一个负责和客户端通信的socket。

下面我们来看下Socket更具体的通信过程:

Socket的通讯过程

服务器端:

01,申请一个socket

02,绑定到一个IP地址和一个端口上

03,开启侦听,等待接收连接

客户端:

01,申请一个socket

02,连接服务器(指明IP地址和端口号)

服务器端接收到连接请求后,产生一个新的socket(端口大于1024)与客户端建立连接并进行通信,原监听socket继续监听。

注意:负责通信的Socket不能无限创建,创建的数量和操作系统有关。

5.Socket的构造函数

Public Socket(AddressFamily addressFamily,SocketType  socketType,ProtocolType  protocolTYpe)

AddressFamily:指定Socket用来解析地址的寻址方案。例如:InterNetWork指示当Socket使用一个IP版本4地址连接

SocketType:定义要打开的Socket的类型

Socket类使用ProtocolType枚举向Windows  Sockets  API通知所请求的协议

注意:

1,端口号必须在 1 和 65535之间,最好在1024以后。

2,要连接的远程主机必须正在监听指定端口,也就是说你无法随意连接远程主机。

如:

IPAddress addr = IPAddress.Parse("127.0.0.1");

IPEndPoint endp = new IPEndPoint(addr,,9000);

服务端先绑定:serverWelcomeSocket.Bind(endp)

客户端再连接:clientSocket.Connect(endp)

3,一个Socket一次只能连接一台主机

4,Socket关闭后无法再次使用

5,每个Socket对象只能与一台远程主机连接。如果你想连接到多台远程主机,你必须创建多个Socket对象。

6.Socket常用类和方法

相关类:

IPAddress:包含了一个IP地址

IPEndPoint:包含了一对IP地址和端口号

方法:

Socket():创建一个Socket

Bind():绑定一个本地的IP和端口号(IPEndPoint)

Listen():让Socket侦听传入的连接吃那个病,并指定侦听队列容量

Connect():初始化与另一个Socket的连接

Accept():接收连接并返回一个新的Socket

Send():输出数据到Socket

Receive():从Socket中读取数据

Close():关闭Socket,销毁连接

7、socket接收

博主就是因为网上的一些不良代码和思路被坑了不少时间,故此特意说明。

socket的接收缓冲区

我们都知道socket的接收方法使receive,receive是从socket缓冲区中读取,socket缓冲区是一个动态的东西,你读取一个,就相当于处理一个,就减少一个。

此缓冲区的意义:当前接收到的数据,意思是截止到你读取的时候里面的数据,这里面的数据是可以随着时间增加的。

我们可以理解为一个队列,发送方往队列中添加数据,receive取数据。

socket接收数据

windows的此缓冲区限制了大小为8k,故最多我们可以接受8k的数据,如果我们处理的过慢,又超过了发送方的超时时间,发送方就会显示超时。故同步处理就需要我们及时读取。否则,请异步处理,或者另起线程处理。

当然,如果我们双方约定了消息的大小,比如1k,那么我们接受的时候就可以每次读取1024个byte。

   重点是,如果我们要读取一个文件,发送方使用一个socket的一次send,那么我们只能一次读取一个字节(约定为偶数的可以读取2个,约定4个或者8个的倍数的,可以读取4个或者8个),缓冲区的数据是按顺序到达的。

读取后,我们在重新拼装成消息,然后解析文件字段,还原为文件。

另:如果文件过大,切片后,不停的new socket发送是不可取的,可以尝试同一个socket发送(博主未验证)

上一段我接收的代码:

      private  void ServerStart()
        {
            //创建IPEndPoint实例
            IPEndPoint ipep = new IPEndPoint(IPAddress.Any, Port);
            //创建socket套接字
            serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //将所创建的套接字与IPEndPoint绑定
            serverSocket.Bind(ipep);
            //设置socket为监听Listen模式
            serverSocket.Listen(9999);

            while (true)
            {
                try
                {
                    //在套接字上接收接入的连接
                    Socket acceptSocket = serverSocket.Accept();
                    if (acceptSocket != null)
                    {
                        Thread socketConnectedThread = new Thread(ReceiveData);
                        socketConnectedThread.IsBackground = true;
                        socketConnectedThread.Start(acceptSocket);

                        //ThreadPool.SetMinThreads(100, 100);
                        //ThreadPool.QueueUserWorkItem(new WaitCallback(ReceiveData), acceptSocket);

                        Dispatcher.BeginInvoke((Action)(()=> {

                            richTextBox.AppendText("\n有客户端接入...");

                        }));

                    }

                }
                catch (Exception ex)
                {
                    MessageBox.Show("listening Error: " + ex.Message);
                }
            }
        }

        private  void ReceiveData(object obj)
        {
            Socket s = (Socket)obj;
            //根据收听到的客户端套接字向客户端发送信息
            IPEndPoint clientep = (IPEndPoint)s.RemoteEndPoint;

            try
            {
                List<byte> list = new List<byte>();
                byte[] buffer = new byte[1];
                int readBytes = 0;
                do
                {
                    readBytes = s.Receive(buffer, 0, buffer.Length,SocketFlags.None);
                    list.AddRange(buffer);
                }
                while (s.Available!=0);
                byte[] buffers = list.ToArray();

                Dispatcher.BeginInvoke((Action)(() =>
                {
                    richTextBox.AppendText("\nIP地址:" + clientep.Address + "  端口号:" + clientep.Port);
                }));

                byte[] messageBuffer = new byte[list.Count-8];
                messageBuffer = buffers.Skip(8).Take(list.Count - 8).ToArray();
                Message receive = MessageHelp.DeSerialize(messageBuffer);
                if (receive != null)
                {
                    switch (receive.messageType)
                    {
                        case "link":
                            ClientIp = receive.sourceIp;
                            ClientPort = Convert.ToInt32(receive.sourcePort);

                            //接到后发送ok
                            byte[] sendData = MessageHelp.Serialize(new Message { messageType = "link", sourceIp = LocalIpAddress, sourcePort = Port.ToString(), content = "ok" });
                            //s.Send(sendData, sendData.Length, SocketFlags.None);

                            Socket toClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                            toClient.Connect(new IPEndPoint(IPAddress.Parse(ClientIp), ClientPort));//与该ip地址进行连接
                            toClient.Send(sendData, sendData.Length, SocketFlags.None);

                            break;
                        case "command":

                            byte[] responseData = MessageHelp.Serialize(new Message { messageType = "command", sourceIp = LocalIpAddress, sourcePort = Port.ToString(), content = receive.content});
                            Socket reClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                            reClient.Connect(new IPEndPoint(IPAddress.Parse(ClientIp), ClientPort));//与该ip地址进行连接
                            reClient.Send(responseData, responseData.Length, SocketFlags.None);

                            Dispatcher.BeginInvoke((Action)(() =>
                            {
                                richTextBox.AppendText("\n" + receive.content);
                            }));
                            break;

                        case "file":

                            FileName = receive.fileName;
                            byte[] csharpFileByte = new byte[receive.fileTotalLength];

                            for (int i = 0; i < receive.bytes.Length; i++)
                            {
                                csharpFileByte[i] = Convert.ToByte(receive.bytes[i] & 0xff);
                            }

                            System.IO.File.WriteAllBytes(FileName, csharpFileByte);
                            MessageBox.Show("文件接收成功");

                            //接到后发送ok
                            byte[] responseFile = MessageHelp.Serialize(new Message { messageType = "file", sourceIp = LocalIpAddress, sourcePort = Port.ToString(), content = receive.content });
                            //s.Send(sendData, sendData.Length, SocketFlags.None);

                            Socket fClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                            fClient.Connect(new IPEndPoint(IPAddress.Parse(ClientIp), ClientPort));//与该ip地址进行连接
                            fClient.Send(responseFile, responseFile.Length, SocketFlags.None);
                            break;
                    }
                }

                }
                catch (Exception ex)
                {
                    s.Close();
                }
            s.Close();
            s.Dispose();
        }
时间: 2024-08-07 13:10:34

C#のsocket通信的相关文章

TCP及socket通信原理

一.网络互联模型 因特网在刚面世时,只有同一制造商生产的计算机才能彼此通信,制定网络互联模型的目的就是为异种的计算机互连提供一个共同的基础和标准框架,并为保持相关标准的一致性和兼容性提供共同的参考. 互联参考模型: OSI七层模型(Open System Interconnect):应用层.表示层.会话层.传输层.网络层.数据链路层.物理层 DoD四层模型:是OSI七层模型的浓缩版,包括 进程/应用层.主机到主机层.因特网层.网络接入层 以上两种模型是层次型的,分层模型的优点主要在于: ①将网络

java socket通信-传输文件图片--传输图片

ClientTcpSend.java   客户端发送类 package com.yjf.test; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.net.InetSocketAddress; import java.net.Socket; public class ClientTcpSend { public static String clien

c# socket通信较完善方案

c#的socket通信应用.文件较多.附件为工程.  core AbstractBytesWorker.cs    字节工作器(基类),用于用于同一不同功能的字节工作器 BinaryHand.cs  2进制处理器.  ThDispose.cs 处理回收相关 crc  entity ThPersonInfo.cs manager ThSocketManager.cs  ThSocketManagerBusiness.cs 所有的业务 request RequestCode.cs  请求码 ThPr

深入浅出讲解:php的socket通信

深入浅出讲解:php的socket通信 对TCP/IP.UDP.Socket编程这些词你不会很陌生吧?随着网络技术的发展,这些词充斥着我们的耳朵.那么我想问: 1.         什么是TCP/IP.UDP?2.         Socket在哪里呢?3.         Socket是什么呢?4.         你会使用它们吗? 什么是TCP/IP.UDP? TCP/IP(Transmission Control Protocol/Internet Protocol)即传输控制协议/网间协

Android简单Socket通信demo

一.Android Socket 通信简介 Android与服务器的通信方式主要有两种,一是Http通信,一是Socket通信.两者的最大差异在于,http连接使用的是“请求—响应方式”,即在请求时建立连接通道,当客户端向服务器发送请求后,服务器端才能向客户端返回数据.而Socket通信则是在双方建立起连接后就可以直接进行数据的传输,在连接时可实现信息的主动推送,而不需要每次由客户端想服务器发送请求. 那么,什么是socket?Socket又称套接字,在程序内部提供了与外界通信的端口,即端口通信

php的socket通信

对TCP/IP.UDP.Socket编程这些词你不会很陌生吧?随着网络技术的发展,这些词充斥着我们的耳朵.那么我想问: 1.         什么是TCP/IP.UDP?2.         Socket在哪里呢?3.         Socket是什么呢?4.         你会使用它们吗? 什么是TCP/IP.UDP? TCP/IP(Transmission Control Protocol/Internet Protocol)即传输控制协议/网间协议,是一个工业标准的协议集,它是为广域网

Socket通信(转)

一.Socket通信简介 Android与服务器的通信方式主要有两种,一是Http通信,一是Socket通信.两者的最大差异在于,http连接使用的是“请求—响应方式”,即在请求时建立连接通道,当客户端向服务器发送请求后,服务器端才能向客户端返回数据.而Socket通信则是在双方建立起连接后就可以直接进行数据的传输,在连接时可实现信息的主动推送,而不需要每次由客户端想服务器发送请求. 那么,什么是socket?Socket又称套接字,在程序内部提供了与外界通信的端口,即端口通信.通过建立sock

基于Java NIO的Socket通信

Java NIO模式的Socket通信,是一种同步非阻塞IO设计模式,它为Reactor模式实现提供了基础. 下面看看,Java实现的一个服务端和客户端通信的例子. NIO模式的基本原理描述如下: 服务端打开一个通道(ServerSocketChannel),并向通道中注册一个选择器(Selector),这个选择器是与一些感兴趣的操作的标识(SelectionKey,即通过这个标识可以定位到具体的操作,从而进行响应的处理)相关联的,然后基于选择器(Selector)轮询通道(ServerSock

Socket通信

1:ServerSocket. 构造方法:ServerSocket(int port) : 创建一个绑定端口号的服务器 accept();监听并且接受Socket,并且返回一个Socket通信包. 2:Socket. 构造方法:Socket(String host,int port); 字节流: getOutputStream(); 输出流方法 (outputStream类) getInputStream();输入流方法 字符流: PrintWriter pw = new printWriter

VS下使用Google Protobuf完成SOCKET通信

如何在Windows环境下的VS中安装使用Google Protobuf完成SOCKET通信 出处:如何在Windows环境下的VS中安装使用Google Protobuf完成SOCKET通信 最近一段时间,由于项目的需要,接触到了Protobuf这个东东,在Linux环境下,体验了一把,感觉挺不错,很方便,且高效.是一个非常值得学习掌握和应用的数据抽象.平台无关.功能强大.…(此处省略1000字)的开源工具. Google虽然把Protobuf做成了跨平台.跨语言,但作为微软的死对头,它在re