socket通信中,web服务器 socket通信ip改变,怎么维持通信

博客访问: 1293549
博文数量: 350
博客积分: 10221
博客等级: 上将
技术积分: 4009
注册时间:
分类: Java 14:21:57
要写程序就必须用Socket,这是程序员都知道的。而且,面试的时候,我们也会问对方会不会Socket编程?一般来说,很多人都会说,Socket编程基本就是listen,accept以及send,write等几个基本的操作。是的,就跟常见的文件操作一样,只要写过就一定知道。对于网络编程,我们也言必称TCP/IP,似乎其它网络协议已经不存在了。对于TCP/IP,我们还知道TCP和UDP,前者可以保证数据的正确和可靠性,后者则允许数据丢失。最后,我们还知道,在建立连接前,必须知道对方的IP地址和端口号。除此,普通的程序员就不会知道太多了,很多时候这些知识已经够用了。最多,写服务程序的时候,会使用多线程来处理并发访问。我们还知道如下几个事实:1。一个指定的端口号不能被多个程序共用。比如,如果IIS占用了80端口,那么Apache就不能也用80端口了。2。很多防火墙只允许特定目标端口的数据包通过。3。服务程序在listen某个端口并accept某个连接请求后,会生成一个新的socket来对该请求进行处理。于是,一个困惑了我很久的问题就产生了。如果一个socket创建后并与80端口绑定后,是否就意味着该socket占用了80端口呢?如果是这样的,那么当其accept一个请求后,生成的新的socket到底使用的是什么端口呢(我一直以为会默认给其分配一个空闲的端口号)?如果是一个空闲的端口,那一定不是80端口了,于是以后的TCP数据包的目标端口就不是80了--防火墙一定会组织其通过的!实际上,我们可以看到,防火墙并没有阻止这样的连接,而且这是最常见的连接请求和处理方式。我的不解就是,为什么防火墙没有阻止这样的连接?它是如何判定那条连接是因为connet80端口而生成的?是不是TCP数据包里有什么特别的标志?或者防火墙记住了什么东西?后来,我又仔细研读了TCP/IP的协议栈的原理,对很多概念有了更深刻的认识。比如,在TCP和UDP同属于传输层,共同架设在IP层(网络层)之上。而IP层主要负责的是在节点之间(End to End)的数据包传送,这里的节点是一台网络设备,比如计算机。因为IP层只负责把数据送到节点,而不能区分上面的不同应用,所以TCP和UDP协议在其基础上加入了端口的信息,端口于是标识的是一个节点上的一个应用。除了增加端口信息,UPD协议基本就没有对IP层的数据进行任何的处理了。而TCP协议还加入了更加复杂的传输控制,比如滑动的数据发送窗口(Slice Window),以及接收确认和重发机制,以达到数据的可靠传送。不管应用层看到的是怎样一个稳定的TCP数据流,下面传送的都是一个个的IP数据包,需要由TCP协议来进行数据重组。所以,我有理由怀疑,防火墙并没有足够的信息判断TCP数据包的更多信息,除了IP地址和端口号。而且,我们也看到,所谓的端口,是为了区分不同的应用的,以在不同的IP包来到的时候能够正确转发。TCP/IP只是一个协议栈,就像操作系统的运行机制一样,必须要具体实现,同时还要提供对外的操作接口。就像操作系统会提供标准的编程接口,比如Win32编程接口一样,TCP/IP也必须对外提供编程接口,这就是Socket编程接口--原来是这么回事啊!在Socket编程接口里,设计者提出了一个很重要的概念,那就是socket。这个socket跟文件句柄很相似,实际上在BSD系统里就是跟文件句柄一样存放在一样的进程句柄表里。这个socket其实是一个序号,表示其在句柄表中的位置。这一点,我们已经见过很多了,比如文件句柄,窗口句柄等等。这些句柄,其实是代表了系统中的某些特定的对象,用于在各种函数中作为参数传入,以对特定的对象进行操作--这其实是C语言的问题,在C++语言里,这个句柄其实就是this指针,实际就是对象指针啦。现在我们知道,socket跟TCP/IP并没有必然的联系。Socket编程接口在设计的时候,就希望也能适应其他的网络协议。所以,socket的出现只是可以更方便的使用TCP/IP协议栈而已,其对TCP/IP进行了抽象,形成了几个最基本的函数接口。比如create,listen,accept,connect,read和write等等。现在我们明白,如果一个程序创建了一个socket,并让其监听80端口,其实是向TCP/IP协议栈声明了其对80端口的占有。以后,所有目标是80端口的TCP数据包都会转发给该程序(这里的程序,因为使用的是Socket编程接口,所以首先由Socket层来处理)。所谓accept函数,其实抽象的是TCP的连接建立过程。accept函数返回的新socket其实指代的是本次创建的连接,而一个连接是包括两部分信息的,一个是源IP和源端口,另一个是宿IP和宿端口。所以,accept可以产生多个不同的socket,而这些socket里包含的宿IP和宿端口是不变的,变化的只是源IP和源端口。这样的话,这些socket宿端口就可以都是80,而Socket层还是能根据源/宿对来准确地分辨出IP包和socket的归属关系,从而完成对TCP/IP协议的操作封装!而同时,放火墙的对IP包的处理规则也是清晰明了,不存在前面设想的种种复杂的情形。明白socket只是对TCP/IP协议栈操作的抽象,而不是简单的映射关系,这很重要!1、TCP连接手机能够使用联网功能是因为手机底层实现了TCP/IP协议,可以使手机终端通过无线网络建立TCP连接。TCP协议可以对上层网络提供接口,使上层网络数据的传输建立在“无差别”的网络之上。建立起一个TCP连接需要经过“三次握手”:第一次握手:客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。握手过程中传送的包里不包含数据,三次握手完毕后,客户端与服务器才正式开始传送数据。理想状态下,TCP连接一旦建立,在通信双方中的任何一方主动关闭连接之前,TCP 连接都将被一直保持下去。断开连接时服务器和客户端均可以主动发起断开TCP连接的请求,断开过程需要经过“四次握手”(过程就不细写了,就是服务器和客户端交互,最终确定断开)2、HTTP连接HTTP协议即超文本传送协议(Hypertext Transfer Protocol ),是Web联网的基础,也是手机联网常用的协议之一,HTTP协议是建立在TCP协议之上的一种应用。HTTP连接最显著的特点是客户端发送的每次请求都需要服务器回送响应,在请求结束后,会主动释放连接。从建立连接到关闭连接的过程称为“一次连接”。1)在HTTP 1.0中,客户端的每次请求都要求建立一次单独的连接,在处理完本次请求后,就自动释放连接。2)在HTTP 1.1中则可以在一次连接中处理多个请求,并且多个请求可以重叠进行,不需要等待一个请求结束后再发送下一个请求。由于HTTP在每次请求结束后都会主动释放连接,因此HTTP连接是一种“短连接”,要保持客户端程序的在线状态,需要不断地向服务器发起连接请求。通常的做法是即时不需要获得任何数据,客户端也保持每隔一段固定的时间向服务器发送一次“保持连接”的请求,服务器在收到该请求后对客户端进行回复,表明知道客户端“在线”。若服务器长时间无法收到客户端的请求,则认为客户端“下线”,若客户端长时间无法收到服务器的回复,则认为网络已经断开。3、SOCKET原理3.1套接字(socket)概念套接字(socket)是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元。它是网络通信过程中端点的抽象表示,包含进行网络通信必须的五种信息:连接使用的协议,本地主机的IP地址,本地进程的协议端口,远地主机的IP地址,远地进程的协议端口。应用层通过传输层进行数据通信时,TCP会遇到同时为多个应用程序进程提供并发服务的问题。多个TCP连接或多个应用程序进程可能需要通过同一个 TCP协议端口传输数据。为了区别不同的应用程序进程和连接,许多计算机操作系统为应用程序与TCP/IP协议交互提供了套接字(Socket)接口。应用层可以和传输层通过Socket接口,区分来自不同应用程序进程或网络连接的通信,实现数据传输的并发服务
阅读(39528) | 评论(1) | 转发(7) |
给主人留下些什么吧!~~
SOCKET4和5有什么区别呢?
请登录后评论。没有更多推荐了,
不良信息举报
举报内容:
Socket通信之客户端和服务器对话
举报原因:
原文地址:
原因补充:
最多只允许输入30个字
加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!在说socket之前。我们先了解下相关的网络知识;
&在Internet上有很多这样的主机,这些主机一般运行了多个服务软件,同时提供几种服务。每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务(应用程序)。
例如:http 使用80端口 ftp使用21端口 smtp使用 25端口
端口用来标识计算机里的某个程序   1)公认端口:从0到1023   2)注册端口:从   3)动态或私有端口:从4
Socket相关概念
socket的英文原义是&孔&或&插座&。作为进程通信机制,取后一种意思。通常也称作&套接字&,用于描述IP地址和端口,是一个通信链的句柄。(其实就是两个程序通信用的。)
socket非常类似于电话插座。以一个电话网为例。电话的通话双方相当于相互通信的2个程序,电话号码就是IP地址。任何用户在通话之前,
首先要占有一部电话机,相当于申请一个socket;同时要知道对方的号码,相当于对方有一个固定的socket。然后向对方拨号呼叫,
相当于发出连接请求。对方假如在场并空闲,拿起电话话筒,双方就可以正式通话,相当于连接成功。双方通话的过程,
是一方向电话机发出信号和对方从电话机接收信号的过程,相当于向socket发送数据和从socket接收数据。通话结束后,一方挂起电话机相当于关闭socket,撤消连接。
Socket有两种类型
流式Socket(STREAM): 是一种面向连接的Socket,针对于面向连接的TCP服务应用,安全,但是效率低;
数据报式Socket(DATAGRAM): 是一种无连接的Socket,对应于无连接的UDP服务应用.不安全(丢失,顺序混乱,在接收端要分析重排及要求重发),但效率高.
TCP/IP协议
TCP/IP(Transmission Control Protocol/Internet Protocol)即传输控制协议/网间协议,是一个工业标准的协议集,它是为广域网(WANs)设计的。
UDP(User Data Protocol,用户数据报协议)是与TCP相对应的协议。它是属于TCP/IP协议族中的一种。
应用层 (Application):应用层是个很广泛的概念,有一些基本相同的系统级 TCP/IP 应用以及应用协议,也有许多的企业商业应用和互联网应用。 解释:我们的应用程序
传输层 (Transport):传输层包括 UDP 和 TCP,UDP 几乎不对报文进行检查,而 TCP 提供传输保证。 解释;保证传输数据的正确性
网络层 (Network):网络层协议由一系列协议组成,包括 ICMP、IGMP、RIP、OSPF、IP(v4,v6) 等。 解释:保证找到目标对象,因为里面用的IP协议,ip包含一个ip地址
链路层 (Link):又称为物理数据网络接口层,负责报文传输。 解释:在物理层面上怎么去传递数据
你可以cmd打开命令窗口。输入
netstat -a
查看当前电脑监听的端口,和协议。有TCP和UDP
TCP/IP与UDP有什么区别呢?该怎么选择?
  UDP可以用广播的方式。发送给每个连接的用户   而TCP是做不到的
  TCP需要3次握手,每次都会发送数据包(但不是我们想要发送的数据),所以效率低   但数据是安全的。因为TCP会有一个校验和。就是在发送的时候。会把数据包和校验和一起   发送过去。当校验和和数据包不匹配则说明不安全(这个安全不是指数据会不会   别窃听,而是指数据的完整性)
  UDP不需要3次握手。可以不发送校验和
  web服务器用的是TCP协议
那什么时候用UDP协议。什么时候用TCP协议呢?   视频聊天用UDP。因为要保证速度?反之相反
下图显示了数据报文的格式
Socket一般应用模式(服务器端和客户端)
服务端跟客户端发送信息的时候,是通过一个应用程序 应用层发送给传输层,传输层加头部 在发送给网络层。在加头 在发送给链路层。在加帧
然后在链路层转为信号,通过ip找到电脑 链路层接收。去掉头(因为发送的时候加头了。去头是为了找到里面的数据) 网络层接收,去头 传输层接收。去头 在到应用程序,解析协议。把数据显示出来
TCP3次握手
在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接。   第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;SYN:同步序列编号(Synchronize SequenceNumbers)。   第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;   第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。
看一个Socket简单的通信图解
1.服务端welcoming socket 开始监听端口(负责监听客户端连接信息)
2.客户端client socket连接服务端指定端口(负责接收和发送服务端消息)
3.服务端welcoming socket 监听到客户端连接,创建connection socket。(负责和客户端通信)
服务器端的Socket(至少需要两个)
一个负责接收客户端连接请求(但不负责与客户端通信)
每成功接收到一个客户端的连接便在服务端产生一个对应的负责通信的Socket 在接收到客户端连接时创建. 为每个连接成功的客户端请求在服务端都创建一个对应的Socket(负责和客户端通信).
客户端的Socket
客户端Socket 必须指定要连接的服务端地址和端口。 通过创建一个Socket对象来初始化一个到服务器端的TCP连接。
Socket的通讯过程
服务器端:
申请一个socket 绑定到一个IP地址和一个端口上 开启侦听,等待接授连接
客户端: 申请一个socket 连接服务器(指明IP地址和端口号)
服务器端接到连接请求后,产生一个新的socket(端口大于1024)与客户端建立连接并进行通讯,原监听socket继续监听。
socket是一个很抽象的概念。来看看socket的位置
好吧。我承认看一系列的概念是非常痛苦的,现在开始编码咯
看来编码前还需要看下sokcet常用的方法
Socket方法: 1)IPAddress类:包含了一个IP地址 例:IPAddress& ip = IPAddress.Parse(txtServer.Text);//将IP地址字符串转换后赋给ip 2) IPEndPoint类:包含了一对IP地址和端口号 例:IPEndPoint point = new IPEndPoint(ip, int.Parse(txtPort.Text));//将指定的IP地址和端口初始化后赋给point 3)Socket (): 创建一个Socket 例:Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//创建监听用的socket 4) Bind(): 绑定一个本地的IP和端口号(IPEndPoint) 例:socket.Bind(point);//绑定ip和端口 5) Listen(): 让Socket侦听传入的连接尝试,并指定侦听队列容量 例: socket.Listen(10); 6) Connect(): 初始化与另一个Socket的连接 7) Accept(): 接收连接并返回一个新的socket 例:Socket connSocket =socket .Accept (); 8 )Send(): 输出数据到Socket 9) Receive(): 从Socket中读取数据 10) Close(): 关闭Socket (销毁连接)
首先创建服务端,服务端是用来监听客户端请求的。
创建服务器步骤:   第一步:创建一个Socket,负责监听客户端的请求,此时会监听一个端口   第二步:客户端创建一个Socket去连接服务器的ip地址和端口号   第三步:当连接成功后。会创建一个新的socket。来负责和客户端通信
1 public static void startServer()
//第一步:创建监听用的socket
Socket socket = new Socket
AddressFamily.InterNetwork, //使用ip4
SocketType.Stream,//流式Socket,基于TCP
ProtocolType.Tcp //tcp协议
//第二步:监听的ip地址和端口号
IPAddress ip = IPAddress.Parse(_ip);
//ip地址和端口号
IPEndPoint point = new IPEndPoint(ip, _point);
//绑定ip和端口
//端口号不能占用:否则:以一种访问权限不允许的方式做了一个访问套接字的尝试
//通常每个套接字地址(协议/网络地址/端口)只允许使用一次。
socket.Bind(point);
catch (Exception)
if (new IOException().InnerException is SocketException)
Console.WriteLine("端口被占用");
//socket.Bind(point);
//第三步:开始监听端口
//监听队列的长度
/*比如:同时有3个人来连接该服务器,因为socket同一个时间点。只能处理一个连接
* 所以其他的就要等待。当处理第一个。然后在处理第二个。以此类推
* 这里的10就是同一个时间点等待的队列长度为10,即。只能有10个人等待,当第11个的时候。是连接不上的
socket.Listen(10);
string msg = string.Format("服务器已经启动........\n监听ip为:{0}\n监听端口号为:{1}\n", _ip, _point);
showMsg(msg);
Thread listen = new Thread(Listen);
listen.IsBackground = true;
listen.Start(socket);
观察上面的代码。开启了一个多线程。去执行Listen方法,Listen是什么?为什么要开启一个多线程去执行?
回到上面的 "Socket的通讯过程"中提到的那个图片,因为有两个地方需要循环执行
第一个:需要循环监听来自客户端的请求
第二个:需要循环获取来自客服端的通信(这里假设是客户端跟服务器聊天)
额。这跟使用多线程有啥关系?当然有。因为Accept方法。会阻塞线程。所以用多线程,避免窗体假死。你说呢?
看看Listen方法
1 /// &summary&
/// 多线程执行
/// Accept方法。会阻塞线程。所以用多线程
/// &/summary&
/// &param name="o"&&/param&
static void Listen(object o)
Socket socket = o as S
//不停的接收来自客服端的连接
while (true)
//如果有客服端连接,则创建通信用是socket
//Accept方法。会阻塞线程。所以用多线程
//Accept方法会一直等待。直到有连接过来
Socket connSocket = socket.Accept();
//获取连接成功的客服端的ip地址和端口号
string msg = connSocket.RemoteEndPoint.ToString();
showMsg(msg + "连接");
//获取本机的ip地址和端口号
//connSocket.LocalEndPoint.ToString();
如果不用多线程。则会一直执行ReceiveMsg
* 就不会接收客服端连接了
Thread th = new Thread(ReceiveMsg);
th.IsBackground = true;
th.Start(connSocket);
细心的你在Listen方法底部又看到了一个多线程。执行ReceiveMsg,对,没错。这就是上面说的。循环获取消息
ReceiveMsg方法定义:
/// &summary&
/// 接收数据
/// &/summary&
/// &param name="o"&&/param&
static void ReceiveMsg(object o)
Socket connSocket = o as S
while (true)
//接收数据
byte[] buffer = new byte[1024 * 1024];//1M
int num = 0;
//接收数据保存发送到buffer中
//num则为实际接收到的字节个数
//这里会遇到这个错误:远程主机强迫关闭了一个现有的连接。所以try一下
num = connSocket.Receive(buffer);
//当num=0.说明客服端已经断开
if (num == 0)
connSocket.Shutdown(SocketShutdown.Receive);
connSocket.Close();
catch (Exception ex)
if (new IOException().InnerException is SocketException)
Console.WriteLine("网络中断");
Console.WriteLine(ex.Message);
//把实际有效的字节转化成字符串
string str = Encoding.UTF8.GetString(buffer, 0, num);
showMsg(connSocket.RemoteEndPoint + "说:\n" + str);
提供服务器的完整代码如下:
2 using System.Collections.G
3 using System.L
4 using System.T
5 using System.Net.S
6 using System.N
7 using System.T
8 using System.IO;
9 namespace CAServer
class Program
//当前主机ip
static string _ip = "192.168.1.2";
static int _point = 8000;
static void Main(string[] args)
//Thread thread = new Thread(startServer);
//thread.Start();
startServer();
Console.ReadLine();
public static void startServer()
//第一步:创建监听用的socket
Socket socket = new Socket
AddressFamily.InterNetwork, //使用ip4
SocketType.Stream,//流式Socket,基于TCP
ProtocolType.Tcp //tcp协议
//第二步:监听的ip地址和端口号
IPAddress ip = IPAddress.Parse(_ip);
//ip地址和端口号
IPEndPoint point = new IPEndPoint(ip, _point);
//绑定ip和端口
//端口号不能占用:否则:以一种访问权限不允许的方式做了一个访问套接字的尝试
//通常每个套接字地址(协议/网络地址/端口)只允许使用一次。
socket.Bind(point);
catch (Exception)
if (new IOException().InnerException is SocketException)
Console.WriteLine("端口被占用");
//socket.Bind(point);
//第三步:开始监听端口
//监听队列的长度
/*比如:同时有3个人来连接该服务器,因为socket同一个时间点。只能处理一个连接
* 所以其他的就要等待。当处理第一个。然后在处理第二个。以此类推
* 这里的10就是同一个时间点等待的队列长度为10,即。只能有10个人等待,当第11个的时候。是连接不上的
socket.Listen(10);
string msg = string.Format("服务器已经启动........\n监听ip为:{0}\n监听端口号为:{1}\n", _ip, _point);
showMsg(msg);
Thread listen = new Thread(Listen);
listen.IsBackground = true;
listen.Start(socket);
/// &summary&
/// 多线程执行
/// Accept方法。会阻塞线程。所以用多线程
/// &/summary&
/// &param name="o"&&/param&
static void Listen(object o)
Socket socket = o as S
//不停的接收来自客服端的连接
while (true)
//如果有客服端连接,则创建通信用是socket
//Accept方法。会阻塞线程。所以用多线程
//Accept方法会一直等待。直到有连接过来
Socket connSocket = socket.Accept();
//获取连接成功的客服端的ip地址和端口号
string msg = connSocket.RemoteEndPoint.ToString();
showMsg(msg + "连接");
//获取本机的ip地址和端口号
//connSocket.LocalEndPoint.ToString();
如果不用多线程。则会一直执行ReceiveMsg
* 就不会接收客服端连接了
Thread th = new Thread(ReceiveMsg);
th.IsBackground = true;
th.Start(connSocket);
/// &summary&
/// 接收数据
/// &/summary&
/// &param name="o"&&/param&
static void ReceiveMsg(object o)
Socket connSocket = o as S
while (true)
//接收数据
byte[] buffer = new byte[1024 * 1024];//1M
int num = 0;
//接收数据保存发送到buffer中
//num则为实际接收到的字节个数
//这里会遇到这个错误:远程主机强迫关闭了一个现有的连接。所以try一下
num = connSocket.Receive(buffer);
//当num=0.说明客服端已经断开
if (num == 0)
connSocket.Shutdown(SocketShutdown.Receive);
connSocket.Close();
catch (Exception ex)
if (new IOException().InnerException is SocketException)
Console.WriteLine("网络中断");
Console.WriteLine(ex.Message);
//把实际有效的字节转化成字符串
string str = Encoding.UTF8.GetString(buffer, 0, num);
showMsg(connSocket.RemoteEndPoint + "说:\n" + str);
/// &summary&
/// 显示消息
/// &/summary&
static void showMsg(string msg)
Console.WriteLine(msg);
//Console.ReadKey();
运行代码。显示如下
是不是迫不及待的想试试看效果。好吧其实我也跟你一样,cmd打开dos命令提示符,输入
telnet &192.168.1.2 8000
回车,会看到窗体名称变了
然后看到服务器窗口
然后在客户端输入数字试试
我输入了1 2 3 。当然,在cmd窗口是不显示的。这不影响测试。
小技巧:为了便于测试,可以创建一个xx.bat文件。里面写命令
telnet &192.168.1.2 8000
这样只有每次打开就会自动连接了。
当然。这仅仅是测试。现在写一个客户端,
创建一个winfrom程序,布局如下显示
请求服务器代码就很容易了。直接附上代码
2 using System.Collections.G
3 using System.ComponentM
4 using System.D
5 using System.D
6 using System.L
7 using System.T
8 using System.Windows.F
9 using System.N
10 using System.Net.S
12 namespace WFAClient
public partial class Form1 : Form
public Form1()
InitializeComponent();
private void btnOk_Click(object sender, EventArgs e)
//客户端连接IP
IPAddress ip = IPAddress.Parse(tbIp.Text);
IPEndPoint point = new IPEndPoint(ip, int.Parse(tbPoint.Text));
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.Connect(point);
msg("连接成功");
btnOk.Enabled = false;
catch (Exception ex)
msg(ex.Message);
private void msg(string msg)
tbMsg.AppendText(msg);
private void btnSender_Click(object sender, EventArgs e)
//发送信息
if (socket != null)
byte[] buffer = Encoding.UTF8.GetBytes(tbContent.Text);
socket.Send(buffer);
* 如果不释放资源。当关闭连接的时候
* 服务端接收消息会报如下异常:
* 远程主机强迫关闭了一个现有的连接。
//socket.Close();
//socket.Disconnect(true);
运行测试,这里需要同时运行客户端和服务器,
首先运行服务器,那怎么运行客户端呢。
右键客户端项目。调试--》启用新实例
好了。一个入门的过程就这样悄悄的完成了。
以上内容来自:http://www.cnblogs.com/nsky/p/4501782.html
根据上面的内容,已经可以开发出一个可以正常通信的Socket示例了,
接下来首先要考虑的就是服务器性能问题
1)在服务器接收数据的时候,定义了一个1M的Byte Buffer,有些设计的更大。更大Buffer可以保证客户端发送数据量很大的情况全部能接受完全。但是作为一个服务器每收到一条客户端请求,都要申请一个1M的Buffer去装客户端发送的数据。如果客户端的并发量很大的情况,还没等到网络的瓶颈,服务器内存开销已经吃不消了。
对于这个问题的解决思路是:
定义一个小Buffer,每次接受客户端请求用:
byte[] bufferTemp = new byte[1024];
和一个大Buffer,装客户端的所有数据,其中用到了strReceiveLength,是客户端发送的总长度,稍后再解释:
byte[] buffer = new byte[Convert.ToInt32(strReceiveLength)];
改写while (true)循环,每次接受1K的数据,然后用Array.Copy方法,把bufferTemp中的数据复制给buffer:
num = connSocket.Receive(bufferTemp, SocketFlags.None);
ArrayUtil.ArrayCopy(bufferTemp, buffer, check, num);
这个Array.Copy是重点,因为TCP数据流在传输过程中也是一个包一个包的传送,最大不超过8K。所以每次接受到的数据,也就是bufferTemp这个变量有可能装满,也有可能装不满。所以在拷贝的时候一定按照这次接受的长度顺序的放入buffer中。等到客户端全部数据发送完成后,再把buffer转换:
strReceive = Encoding.UTF8.GetString(buffer, 0, buffer.Length);
而不能够每次都转换,再strReceive += 一个Byte数组。这样做的后果就是中文会被截断,因为中文在UTF-8编码下占3-4个字节,很容易出现乱码。
2)数据长度校验
TCP在传输过程中难免会有数据发送不全或者丢失的情况。所以在客户端发送数据的时候一定带上校验长度:
byte[] btyLength = Encoding.UTF8.GetBytes(strContent);
string strLength = btyLength.Length.ToString().PadLeft(8, '0');
string sendData = strLength + strC
byte[] buffer = Encoding.UTF8.GetBytes(sendData);
socketClient.Send(buffer);
这样在服务器端,先把要接受的长度收到:
byte[] bufferLength = new byte[8];num = connSocket.Receive(bufferLength);
strReceiveLength = Encoding.UTF8.GetString(bufferLength, 0, bufferLength.Length);
在循环里用下面的判断,来校验和判断是否已经接受完毕:
if (check == Convert.ToInt32(strReceiveLength))
3)设计上一些方式
很多局域网的部署是分层的,也就是分内网和外网。服务器部署一定要在外网上部署,这里的外网指的是在客户端之上的网段上。
比如192.168.1.22下有个无线路由,无线连接的IP段为192.168.2.1~254
服务器搭建在192.168.1网段下,192.168.2的客户端是可以访问的。但是相反则不行,192.168.1网段下的设备无法主动找到192.168.2的服务器。
阅读(...) 评论()

我要回帖

更多关于 web服务器 socket通信 的文章

 

随机推荐