深入 CSocket 编程之阻塞和非阻塞模式
sock的知识点总结
sock的知识点总结1. Sock 的概念和作用Sock 是 Socket 的简称,它是在网络编程中非常重要的概念,它提供了一种通信机制,使得计算机之间可以进行数据交换。
Sock 的主要作用包括:建立连接、传输数据、断开连接等。
它为应用层提供了数据传输的能力,同时也可以实现基于 TCP 或 UDP 的各种协议。
2. Sock 的类型Sock 可分为两种类型,分别是面向连接的 Socket 和面向无连接的 Socket。
面向连接的Socket 是指通过建立连接来进行数据传输,它使用 TCP 协议进行通信;而面向无连接的Socket 是指不需要事先建立连接,可以直接进行数据传输,它使用 UDP 协议进行通信。
面向连接的 Socket 保证了数据的可靠传输,但是会有一定的延迟;而面向无连接的Socket 则具有高效的特点,但是不保证数据的可靠性。
3. Sock 的地址在网络通信中,Sock 的地址包括主机地址和端口号。
主机地址用来标识网络中的计算机,而端口号则用来标识计算机上的不同进程。
主机地址和端口号组合在一起,就可以唯一确定一台计算机上的一个进程。
在建立 Sock 连接时,需要指定对方的主机地址和端口号,以便进行通信。
4. Sock 的创建和使用在编程中,要使用 Sock 进行网络通信,需要经过一系列的步骤来创建和使用 Sock。
首先需要创建一个 Sock,然后绑定地址和端口号,接着监听连接请求或者发起连接请求,最后进行数据传输和断开连接。
在 C 语言中,可以使用 socket() 函数来创建 Sock,bind() 函数来绑定地址和端口号,listen() 函数来监听连接请求,accept() 函数来接受连接请求,connect() 函数来发起连接请求,send() 函数和recv() 函数来进行数据传输,close() 函数来断开连接。
5. Sock 的通信模式Sock 的通信模式包括客户端-服务端模式和对等通信模式。
深入 CSocket 编程之阻塞和非阻塞模式
深入 CSocket 编程之阻塞和非阻塞模式2007年09月30日星期日 19:25有时,花上几个小时阅读、调试、跟踪优秀的源码程序,能够更快地掌握某些技术关键点和精髓。
当然,前提是对这些技术大致上有一个了解。
我通过几个采用 CSocket 类编写并基于 Client/Server (客户端 / 服务端)的网络聊天和传输文件的程序 ( 详见:源代码参考 ) ,在调试这些程序的过程中,追踪深入至 CSocket 类核心源码 Sockcore.cpp ,对于CSocket 类的运行机制可谓是一览无遗,并且对于阻塞和非阻塞方式下的 socket 程序的编写也是稍有体会。
阅读本文请先注意:这里的阻塞和非阻塞的概念仅适用于 Server 端 socket 程序。
socket 意为套接字,它与 Socket 不同,请注意首字母的大小写。
客户端与服务端的通信简单来讲:服务端 socket 负责监听,应答,接收和发送消息,而客户端 socket 只是连接,应答,接收,发送消息。
此外,如果你对于采用 CSocket 类编写 Client/Server 网络程序的原理不是很了解,请先查询一下(详见:参考书籍和在线帮助)。
在此之前,有必要先讲述一下:网络传输服务提供者, ws2_32.dll , socket 事件和 socket window 。
1、网络传输服务提供者(网络传输服务进程), Socket 事件, Socket Window网络传输服务提供者( transport service provider )是以 DLL 的形式存在的,在 windows 操作系统启动时由服务进程 svchost.exe 加载。
当 socket 被创建时,调用 API 函数 Socket (在 ws2_32.dll 中), Socket 函数会传递三个参数 : 地址族,套接字类型 ( 注 2 ) 和协议,这三个参数决定了是由哪一个类型的网络传输服务提供者来启动网络传输服务功能。
阻塞模式和非阻塞模式
何为阻塞?从该网络通讯过程来理解一下何为阻塞:在以上过程中若连接还没到来,那么accept 会阻塞, 程序运行到这里不得不挂起,CPU 转而执行其他线程。
在以上过程中若数据还没准备好,read 会一样也会阻塞。
阻塞式网络IO 的特点:多线程处理多个连接。
每个线程拥有自己的栈空间并且占用一些CPU 时间。
每个线程遇到外部为准备好的时候,都会阻塞掉。
阻塞的结果就是会带来大量的进程上下文切换。
且大部分进程上下文切换可能是无意义的。
比如假设一个线程监听一个端口,一天只会有几次请求进来,但是该cpu 不得不为该线程不断做上下文切换尝试,大部分的切换以阻塞告终。
何为非阻塞?下面有个隐喻:一辆从A 开往 B 的公共汽车上,路上有很多点可能会有人下车。
司机不知道哪些点会有哪些人会下车,对于需要下车的人,如何处理更好?1. 司机过程中定时询问每个乘客是否到达目的地,若有人说到了,那么司机停车,乘客下车。
( 类似阻塞式)2. 每个人告诉售票员自己的目的地,然后睡觉,司机只和售票员交互,到了某个点由售票员通知乘客下车。
( 类似非阻塞)很显然,每个人要到达某个目的地可以认为是一个线程,司机可以认为是CPU 。
在阻塞式里面,每个线程需要不断的轮询,上下文切换,以达到找到目的地的结果。
而在非阻塞方式里,每个乘客( 线程) 都在睡觉( 休眠) ,只在真正外部环境准备好了才唤醒,这样的唤醒肯定不会阻塞。
非阻塞的原理把整个过程切换成小的任务,通过任务间协作完成。
由一个专门的线程来处理所有的IO 事件,并负责分发。
事件驱动机制:事件到的时候触发,而不是同步的去监视事件。
线程通讯:线程之间通过wait,notify 等方式通讯。
保证每次上下文切换都是有意义的。
减少无谓的进程切换。
Socket模型详解 两种模式
// Create worker thread CreateThread(NULL, 0, WorkerThread, NULL, 0, &dwThreadId); while (TRUE) { // Accept a connection sClient = accept(sListen, (struct sockaddr *)&client, &iaddrSize); printf("Accepted client:%s:%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port)); // Add socket to g_CliSocketArr g_CliSocketArr[g_iTotalConn++] = sClient; } return 0; } DWORD WINAPI WorkerThread(LPVOID lpParam) { int i; fd_set fdread; int ret; struct timeval tv = {1, 0}; char szMessage[MSGSIZE]; while (TRUE) { FD_ZERO(&fdread);//将fdread初始化空集 for (i = 0; i < g_iTotalConn; i++) { FD_SET(g_CliSocketArr, &fdread);//将要检查的套接口加入到集合中 } // We only care read event ret = select(0, &fdread, NULL, NULL, &tv);//每隔一段时间,检查可读性 的套接口 if (ret == 0) { // Time expired continue; } for (i = 0; i < g_iTotalConn; i++)
Socket调用方式(同步,异步,阻塞,非阻塞)
Socket调⽤⽅式(同步,异步,阻塞,⾮阻塞)同步:我调⽤⼀个功能,该功能没有结束前,我死等结果。
异步:当⼀个异步过程调⽤发出后,调⽤者不能⽴刻得到结果。
该功能在完成后,通过状态、通知和回调来通知调⽤者。
同步和⾮同步关注的是调⽤者是否等待等待调⽤结果。
举个通俗的例⼦:你打电话问书店⽼板有没有《分布式系统》这本书,如果是同步通信机制,书店⽼板会说,你稍等,”我查⼀下",然后开始查啊查,等查好了(可能是5秒,也可能是⼀天)告诉你结果(返回结果)。
⽽异步通信机制,书店⽼板直接告诉你我查⼀下啊,查好了打电话给你,然后直接挂电话了(不返回结果)。
然后查好了,他会主动打电话给你。
在这⾥⽼板通过“回电”这种⽅式来回调。
阻塞:调⽤我(函数),我(函数)没有接收完数据或者没有得到结果之前,我不会返回。
⾮阻塞:调⽤我(函数),我(函数)⽴即返回通知调⽤者以最常⽤的send和recv两个函数为例⽐如你调⽤send函数发送⼀定的Byte,在系统内部send做的⼯作其实只是把数据传输(Copy)到TCP/IP协议栈的输出缓冲区,它执⾏成功并不代表数据已经成功的发送出去了,如果TCP/IP协议栈没有⾜够的可⽤缓冲区来保存你Copy过来的数据的话...这时候就体现出阻塞和⾮阻塞的不同之处了:对于阻塞模式的socket send函数将不返回直到系统缓冲区有⾜够的空间把你要发送的数据Copy过去以后才返回,⽽对于⾮阻塞的socket来说send会⽴即返回WSAEWOULDDBLOCK告诉调⽤者说:"发送操作被阻塞了!!!你想办法处理吧..."对于recv函数,同样道理,对于阻塞模式的socket来说如果TCP/IP协议栈的接收缓冲区没有通知⼀个结果给它它就⼀直不返回:耗费着系统资源....对于⾮阻塞模式的socket该函数会马上返回,然后告诉你:WSAEWOULDDBLOCK---"现在没有数据,回头再来看看"阻塞I/O模型:⾮阻塞I/O模型:阻塞和⾮阻塞关注的是调⽤者在等待调⽤结果时的状态。
socket 非阻塞 select read 例子
socket 非阻塞select read 例子Socket是一种在计算机网络中进行通信的工具,它允许不同的计算机之间进行数据传输和交互。
在使用Socket进行通信时,常常会遇到阻塞和非阻塞的情况。
其中,非阻塞方式可以使用select函数进行处理。
本文将逐步讲解非阻塞Socket中使用select函数进行读取操作的例子。
首先,我们需要了解什么是非阻塞方式和select函数。
在传统的阻塞方式中,当进行数据读取或写入时,程序会阻塞在该操作上,直到操作完成才会继续执行后续代码。
而非阻塞方式则不会等待操作完成,而是立即返回。
这样可以提高程序的响应速度,并允许程序可以同时处理多个Socket连接。
select函数是一种用于监视文件描述符变化的函数。
它可以监听多个文件描述符的读、写和异常事件,并在事件发生时返回。
通过select函数,我们可以实现对非阻塞Socket进行读取操作,从而实现同时处理多个连接的需求。
下面我们将逐步实现一个简单的非阻塞Socket服务器,并使用select函数进行读取操作。
首先,我们需要创建一个非阻塞Socket。
在C语言中,可以使用socket 函数创建一个Socket,并使用fcntl函数将其设置为非阻塞模式。
下面是创建Socket的代码:c#include <sys/socket.h>#include <fcntl.h>int main() {int server_socket = socket(AF_INET, SOCK_STREAM, 0);fcntl(server_socket, F_SETFL, O_NONBLOCK);...}在上述代码中,我们首先使用socket函数创建了一个Socket。
AF_INET 表示使用IPv4协议,SOCK_STREAM表示使用面向连接的TCP协议。
然后使用fcntl函数将Socket设置为非阻塞模式,通过传入O_NONBLOCK 标记将其设置为非阻塞。
Socket C++ TCP阻塞 非阻塞总结
0、可运行实例及基本知识1、如何设置socket函数的非阻塞调用?2、深入CSocket 编程之阻塞和非阻塞模式3、SOCKET类的设计和实现服务器#include"stdafx.h"#include<WinSock2.h>#include<Windows.h>#include<vector>#include<stdlib.h>#include<iostream>#include<algorithm>#pragma comment(lib, "ws2_32.lib")#define SERVPORT 7861 /*服务器监听端口号*/#define MAXDATASIZE 100#define BACKLOG 10using namespace std;std::vector<SOCKET> client_fd;char buf[MAXDATASIZE];DWORD WINAPI qtPingServerThreadFunc(LPVOID lpThreadParameter);int_tmain(int argc, _TCHAR* argv[]){SOCKET sockfd; /*sock_fd:监听socket;client_fd:数据传输socket */struct sockaddr_in my_addr; /* 本机地址信息*/struct sockaddr_in remote_addr; /* 客户端地址信息*/char szMsg[]="hello";WORD wVersionRequested;WSADATA wsaData;int err;wVersionRequested = MAKEWORD(2,0);err = WSAStartup(wVersionRequested,&wsaData);if (0 != err){cout<<"Socket failed";return 0;}if (LOBYTE(wsaData.wVersion)!= 2 || HIBYTE(wsaData.wVersion) != 0){WSACleanup();return 0;}sockfd = socket(AF_INET,SOCK_STREAM,0);if (INVALID_SOCKET == sockfd){cout<<"Soket Create Failed";return 0;}/*sockfd = socket(AF_INET, SOCK_STREAM, 0);*/if (sockfd == -1){perror( "socket创建出错!");exit(1);}my_addr.sin_family=AF_INET;my_addr.sin_port=htons(SERVPORT);my_addr.sin_addr.s_addr = INADDR_ANY;memset( &(my_addr.sin_zero),0,8);if (bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr)) == -1){perror( "bind出错!");//exit(1);}int n = 0;while(1){if (listen(sockfd, BACKLOG) != -1){int sin_size = sizeof(struct sockaddr_in);SOCKET nSocket = accept(sockfd, (struct sockaddr *) &remote_addr, &sin_size);std::vector<SOCKET>::iterator itr_end = client_fd.end();std::vector<SOCKET>::iterator itr =std::find(client_fd.begin(),itr_end,nSocket);if (itr == itr_end){client_fd.push_back(nSocket);printf( "received a connection from %s\n", inet_ntoa(remote_addr.sin_addr));DWORD dwPingThreadID;HANDLE hPingHandle = CreateThread(0, 0, qtPingServerThreadFunc, 0, 0,&dwPingThreadID);}}}for (int n = 0;n < client_fd.size(); n++){closesocket(client_fd[n]);}client_fd.clear();}DWORD WINAPI qtPingServerThreadFunc(LPVOID lpThreadParameter){while (1){for (int n = 0;n < client_fd.size(); n++){int recvbytes;if ((recvbytes=recv(client_fd[n], buf, MAXDATASIZE, 0)) !=-1){buf[recvbytes] = '\0';printf( "Received: %d,%s\n",n,buf);send(client_fd[n], buf, sizeof(buf), 0);}}}return 0x1001;}客户端:#include<iostream>#pragma comment(lib, "ws2_32.lib")using namespace std;#define SERVPORT 7861 /*服务器监听端口号*/#define DEST_IP"192.168.1.35"#define MAXDATASIZE 100;int_tmain(int argc, _TCHAR* argv[]){int sockfd, recvbytes;char buf[MAXDATASIZE];struct hostent *host;struct sockaddr_in serv_addr;struct sockaddr_in dest_addr; /* 目的地址*/WORD wVersionRequested;WSADATA wsaData;int err;wVersionRequested = MAKEWORD(2,0);err = WSAStartup(wVersionRequested,&wsaData);if (0 != err){cout<<"Socket failed";return 0;}if (LOBYTE(wsaData.wVersion)!= 2 || HIBYTE(wsaData.wVersion) != 0) {WSACleanup();return 0;}if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){perror("socket创建出错!");exit(1);}serv_addr.sin_family=AF_INET;serv_addr.sin_port=htons(SERVPORT);/* serv_addr.sin_addr = *((struct in_addr *)host->h_addr);*/ serv_addr.sin_addr.s_addr = inet_addr(DEST_IP);memset( &(serv_addr.sin_zero),0,8);if (connect(sockfd, (struct sockaddr *) &serv_addr, \sizeof(struct sockaddr)) == -1) {perror("connect出错!");//exit(1);}char szMsg[] = "hao";int nlen = sizeof(serv_addr);int uIndex = 0;while (1){Sleep(1000);if (send(sockfd, "Hello, are connected!\n", 23, 0) == -1){cout<<WSAGetLastError()<<endl;}else{cout<<uIndex++<<":an TCP package is sended"<<endl;}if ((recvbytes=recv(sockfd, buf, MAXDATASIZE, 0)) !=-1){buf[recvbytes] = '\0';printf( "Received: %s",buf);}}closesocket(sockfd);return 0;}一.WinSock基本知识这里不打算系统地介绍socket或者WinSock的知识。
Socket阻塞模式和非阻塞模式
Socket阻塞模式和⾮阻塞模式阻塞I/O模型:简介:进程会⼀直阻塞,直到数据拷贝完成应⽤程序调⽤⼀个IO函数,导致应⽤程序阻塞,等待数据准备好。
如果数据没有准备好,⼀直等待….数据准备好了,从内核拷贝到⽤户空间,IO函数返回成功指⽰。
阻塞I/O模型图:在调⽤recv()/recvfrom()函数时,发⽣在内核中等待数据和复制数据的过程。
当调⽤recv()函数时,系统⾸先查是否有准备好的数据。
如果数据没有准备好,那么系统就处于等待状态。
当数据准备好后,将数据从系统缓冲区复制到⽤户空间,然后该函数返回。
在套接应⽤程序中,当调⽤recv()函数时,未必⽤户空间就已经存在数据,那么此时recv()函数就会处于等待状态。
当使⽤socket()函数和WSASocket()函数创建套接字时,默认的套接字都是阻塞的。
这意味着当调⽤Windows Sockets API不能⽴即完成时,线程处于等待状态,直到操作完成。
并不是所有Windows Sockets API以阻塞套接字为参数调⽤都会发⽣阻塞。
例如,以阻塞模式的套接字为参数调⽤bind()、listen()函数时,函数会⽴即返回。
将可能阻塞套接字的Windows Sockets API调⽤分为以下四种:1.输⼊操作: recv()、recvfrom()、WSARecv()和WSARecvfrom()函数。
以阻塞套接字为参数调⽤该函数接收数据。
如果此时套接字缓冲区内没有数据可读,则调⽤线程在数据到来前⼀直睡眠。
2.输出操作: send()、sendto()、WSASend()和WSASendto()函数。
以阻塞套接字为参数调⽤该函数发送数据。
如果套接字缓冲区没有可⽤空间,线程会⼀直睡眠,直到有空间。
3.接受连接:accept()和WSAAcept()函数。
以阻塞套接字为参数调⽤该函数,等待接受对⽅的连接请求。
如果此时没有连接请求,线程就会进⼊睡眠状态。
4.外出连接:connect()和WSAConnect()函数。
c语言 tcpip 阻塞非阻塞用法
在C语言中,TCP/IP套接字可以以阻塞(blocking)或非阻塞(non-blocking)模式运行。
这两种模式决定了套接字在进行网络通信时的行为。
1. 阻塞模式:在阻塞模式下,当套接字执行输入/输出操作时,程序会一直等待,直到操作完成或出现错误。
阻塞模式是默认的套接字行为。
例如,在阻塞模式下,如果调用recv()函数接收数据,但没有数据可供接收,程序将一直等待,直到有数据可用为止。
2. 非阻塞模式:在非阻塞模式下,当套接字执行输入/输出操作时,程序不会等待操作完成,而是立即返回。
如果操作无法立即完成,则返回一个错误代码(例如EWOULDBLOCK或EAGAIN),表示操作当前不可用。
程序可以通过轮询套接字状态或使用回调函数等方式来检查操作是否完成。
非阻塞模式可以让程序在等待网络操作期间能够处理其他任务,提高了程序的响应性能。
下面是一个简单的示例,演示了如何设置和使用阻塞和非阻塞套接字:```c#include <stdio.h>#include <sys/socket.h>#include <fcntl.h>int main() {int sockfd;// 创建套接字sockfd = socket(AF_INET, SOCK_STREAM, 0);// 设置为非阻塞模式int flags = fcntl(sockfd, F_GETFL, 0);fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);// 在非阻塞模式下进行操作int ret = connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)); if (ret == -1) {// 连接操作当前不可用if (errno == EINPROGRESS) {// 连接正在进行中,可以继续处理其他任务} else {// 发生错误perror("connect");return 1;}}// 恢复为阻塞模式fcntl(sockfd, F_SETFL, flags);// 在阻塞模式下进行操作ret = send(sockfd, buffer, sizeof(buffer), 0);if (ret == -1) {// 发生错误perror("send");return 1;}close(sockfd);return 0;}```在上面的示例中,首先创建了一个套接字,并将其设置为非阻塞模式。
socket阻塞和非阻塞的用法
在Python中,socket默认是阻塞的。
对于阻塞的socket,当调用recv()函数时,如果数据没有准备好,那么系统就会等待数据。
如果数据准备好后,系统会将数据从系统内核缓冲区复制到用户空间,然后该函数返回。
对于非阻塞的socket,当调用recv()函数时,即使数据没有准备好,也不会阻塞,而是直接返回。
以下是socket阻塞和非阻塞的用法示例:1. socket阻塞模式:```pythonimport socket# 创建一个socket对象s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 连接服务器s.connect()# 接收数据,如果数据没有准备好,就会一直等待data = s.recv(1024)2. socket非阻塞模式:```pythonimport socketimport select# 创建一个socket对象s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 设置socket为非阻塞模式s.setblocking(False)# 连接服务器s.connect()# 创建select对象,将socket对象添加进去r, w, e = select.select([s], [], [])# 接收数据,如果数据没有准备好,就会直接返回None data = s.recv(1024) if r else None注意:在非阻塞模式下,如果没有数据可读,recv()函数会立即返回None。
因此,在使用非阻塞模式时,需要不断循环检查是否有数据可读,直到接收到所有需要的数据。
socket阻塞式和非阻塞式通信
我们拿最常用的send和recv两个函数来说吧...比如你调用send函数发送一定的Byte在系统内部send做的工作其实只是把数据传输(Copy)到TCP/IP协议栈的输出缓冲区它执行成功并不代表数据已经成功的发送出去了如果TCP/IP 协议栈没有足够的可用缓冲区来保存你Copy过来的数据的话...这时候就体现出阻塞和非阻塞的不同之处了:对于阻塞模式的socket send函数将不返回直到系统缓冲区有足够的空间把你要发送的数据Copy过去以后才返回而对于非阻塞的socket来说send会立即返回WSAEWOULDDBLOCK告诉调用者说:"发送操作被阻塞了!!!你想办法处理吧..."对于recv函数同样道理该函数的内部工作机制其实是在等待TCP/IP协议栈的接收缓冲区通知它说:嗨你的数据来了.对于阻塞模式的socket来说如果TCP/IP协议栈的接收缓冲区没有通知一个结果给它它就一直不返回:耗费着系统资源....对于非阻塞模式的socket该函数会马上返回然后告诉你:WSAEWOULDDBLOCK---"现在没有数据回头在来看看"同步、异步、阻塞和非阻塞四种调用方式。
这些方式彼此概念并不好理解。
下面是我对这些术语的理解。
同步所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回。
按照这个定义,其实绝大多数函数都是同步调用(例如sin isdigit等)。
但是一般而言,我们在说同步、异步的时候,特指那些需要其他部件协作或者需要一定时间完成的任务。
最常见的例子就是 SendMessage。
该函数发送一个消息给某个窗口,在对方处理完消息之前,这个函数不返回。
当对方处理完毕以后,该函数才把消息处理函数所返回的 LRESULT值返回给调用者。
异步异步的概念和同步相对。
当一个异步过程调用发出后,调用者不能立刻得到结果。
实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者。
Socket编程介绍
Socket编程介绍Socket编程是一种计算机网络编程,它利用Socket库和通信协议将不同计算机之间的进程相互联系起来,以完成数据通信和资源共享等功能。
Socket编程是一种跨平台的网络编程方式,可以在多种操作系统上使用,比如Windows、UNIX、Linux等。
Socket编程的核心在于网络协议,其中最常用的是TCP/IP协议。
TCP/IP协议是一个以分组交换方式进行数据传输的网络协议,它将数据分成许多小的数据包进行传输,每个小的数据包在传输过程中都可以独立处理。
这种分段传输的方式使得TCP/IP协议具有高效、安全、灵活、可靠、可扩展、可配置等特点,被广泛应用于Internet上。
Socket编程可以使用不同的编程语言实现,比如C、C++、Java、Python等。
其中C、C++语言是最常用的,因为它们可以更好地控制底层操作,提高性能和效率。
而Python编程语言则由于其简洁、易学、易用等特点,成为很多初学者的首选。
Socket编程常见的应用有:网络浏览器、邮件客户端、文件传输工具、远程控制工具、网游等。
以网络浏览器为例,当用户在浏览器中输入网址时,浏览器会利用Socket编程与Web服务器建立连接,向服务器请求相应的网页资源,服务器接收到请求后,会将相应的网页资源发回给浏览器,浏览器将网页资源显示在用户的屏幕上。
在Socket编程中,每个进程都是一个网络服务,并且都有一个唯一的IP地址和端口号。
IP地址是指互联网协议地址,用于唯一标识一台计算机所在的网络,它通常由四个十进制数(xxx.xxx.xxx.xxx)表示。
端口号是指进程与操作系统通信的口令,表示计算机传输数据的通道,其取值范围为0~65535,其中0~1023被系统保留,一般用于常用的网络服务,比如HTTP、FTP、Telnet等。
Socket编程中两个进程之间的数据传输通常分为两种模式:阻塞模式和非阻塞模式。
在阻塞模式下,进程需要等待数据传输完毕后才能继续处理其他事情,这种方式适用于数据处理量不大的情况,但在数据传输量大、网络状况差的情况下,会导致性能降低。
linuxCsocket编程—阻塞式与非阻塞式
linuxCsocket编程—阻塞式与非阻塞式阻塞和非阻塞阻塞函数在完成其指定的任务以前不允许程序调用另一个函数。
例如,程序执行一个读数据的函数调用时,在此函数完成读操作以前将不会执行下一程序语句。
当服务器运行到accept语句时,而没有客户连接服务请求到来,服务器就会停止在accept语句上等待连接服务请求的到来。
这种情况称为阻塞(blocking)。
而非阻塞操作则可以立即完成。
比如,如果你希望服务器仅仅注意检查是否有客户在等待连接,有就接受连接,否则就继续做其他事情,则可以通过将Socket 设置为非阻塞方式来实现。
非阻塞socket在没有客户在等待时就使accept调用立即返回。
#include <unistd.h>#include <fcntl.h>……sockfd = socket(AF_INET,SOCK_STREAM,0);fcntl(sockfd,F_SETFL,O_NONBLOCK);……通过设置socket为非阻塞方式,可以实现“轮询“若干Socket。
当企图从一个没有数据等待处理的非阻塞Socket读入数据时,函数将立即返回,返回值为-1,并置errno值为EWOULDBLOCK。
但是这种“轮询“会使CPU处于忙等待方式,从而降低性能,浪费系统资源。
而调用 select()会有效地解决这个问题,它允许你把进程本身挂起来,而同时使系统内核监听所要求的一组文件描述符的任何活动,只要确认在任何被监控的文件描述符上出现活动,select()调用将返回指示该文件描述符已准备好的信息,从而实现了为进程选出随机的变化,而不必由进程本身对输入进行测试而浪费 CPU开销。
Select函数原型为: int select(int numfds,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,struct timeval *timeout);其中readfds、writefds、exceptfds分别是被select()监视的读、写和异常处理的文件描述符集合。
非阻塞赋值和阻塞赋值
非阻塞赋值和阻塞赋值非阻塞赋值和阻塞赋值,听起来像个程序员的外星语,别担心,咱们慢慢来聊聊,保证让你明白得通透透。
想象一下,你在厨房忙得不可开交,家里一堆事情等着你去做。
这个时候,非阻塞赋值就像一个聪明的助手,你给了他一个任务,他就会立刻去做,根本不管你有没有空。
你可以继续切菜,煮汤,甚至顺便打个电话,反正这位助手会自动完成他的工作,等他做好了再告诉你。
就这么简单。
你心里想着,哎,这个助手真不错,我可以省出好多时间来。
再说说阻塞赋值,这就有点儿像你的亲戚来访,非得跟你聊个痛快,才能离开。
你一边听着他滔滔不绝,心里想着还有一大堆事情没做,完全无法分身。
这个过程就像程序里的阻塞赋值,你要等到这个亲戚说完,才能继续做自己的事。
可能你心里已经默念无数遍“快点吧,赶紧聊完”,可是他就是不知好歹,唠唠叨叨。
哦,生活就是这样,有些时候你只能耐心等待,没办法。
我们再深入聊聊,非阻塞赋值在实际应用中就像你在跑步机上健身。
你可以同时做很多事情,放歌、看视频,甚至吃零食,根本不影响你跑步的节奏。
程序执行的时候,可以同时进行多个操作,效率极高,简直像飞一样。
那种爽快感,仿佛自己是个超人,可以处理无数任务而不觉得累。
但是,前提是你得有个好心态,不然就容易分心了,这时候就得小心了,跑步机可不会等你。
反观阻塞赋值,咱们就得把这比作在电影院看电影。
你得等着前面的广告结束,才能看到你想看的剧情,心里那种着急感,简直要炸了。
好不容易等到广告结束,你才终于能看到精彩的片段。
就像程序里,某个任务必须完成,才能进行下一个,这个时候你就得耐心等待。
虽然有时候觉得无聊,但也好比是人生的一种沉淀,等到对的时机,所有的努力都能水到渠成。
聊到这儿,有趣的是,非阻塞和阻塞赋值在实际编程中其实是相辅相成的。
你可以选择在合适的时候用非阻塞赋值,让自己轻松应对更多任务;又能在某些关键时刻用阻塞赋值,确保数据的一致性。
两者之间的平衡,就像生活中的甜与咸,缺一不可。
LinuxUDPsocket设置为的非阻塞模式与阻塞模式区别
LinuxUDPsocket设置为的⾮阻塞模式与阻塞模式区别Linux UDP socket 设置为的⾮阻塞模式与阻塞模式区别UDP socket 设置为的⾮阻塞模式Len = recvfrom(SocketFD, szRecvBuf, sizeof(szRecvBuf), MSG_DONTWAIT, (struct sockaddr *)&SockAddr,&ScokAddrLen);UDP socket 设置为的阻塞模式Len = recvfrom(SocketFD, szRecvBuf, sizeof(szRecvBuf), 0, (struct sockaddr *)&SockAddr,&ScokAddrLen);Linux socket编程之阻塞套接字和⾮阻塞套接字 每⼀个TCP套接⼝有⼀个发送缓冲区,可以⽤SO_SNDBUF套接⼝选项来改变这个缓冲区的⼤⼩。
当应⽤进程调⽤ write 时,内核从应⽤进程的缓冲区中拷贝所有数据到套接⼝的发送缓冲区。
如果套接⼝的发送缓冲区容不下应⽤程序的所有数据(或是应⽤进程的缓冲区⼤于套接⼝发送缓冲区,或是套接⼝发送缓冲区还有其他数据),应⽤进程将被挂起(睡眠)。
这⾥假设套接⼝是阻塞的,这是通常的缺省设置。
内核将不从write系统调⽤返回,直到应⽤进程缓冲区中的所有数据都拷贝到套接⼝发送缓冲区。
因此从写⼀个TCP套接⼝的write调⽤成功返回仅仅表⽰我们可以重新使⽤应⽤进程的缓冲区。
它并不告诉我们对端的 TCP或应⽤进程已经接收了数据。
TCP取套接⼝发送缓冲区的数据并把它发送给对端TCP,其过程基于TCP数据传输的所有规则。
对端TCP必须确认收到的数据,只有收到对端的ACK,本端TCP才能删除套接⼝发送缓冲区中已经确认的数据。
TCP必须保留数据拷贝直到对端确认为⽌。
1 输⼊操作: read、readv、recv、recvfrom、recvmsg如果某个进程对⼀个阻塞的TCP套接⼝调⽤这些输⼊函数之⼀,⽽且该套接⼝的接收缓冲区中没有数据可读,该进程将被投⼊睡眠,直到到达⼀些数据。
Socket详解之阻塞非阻塞
这里不打算系统地介绍socket或者WinSock的知识。
首先介绍WinSock API函数,讲解阻塞/非阻塞的概念;然后介绍socket的使用。
APISocket接口是网络编程(通常是TCP/IP协议,也可以是其他协议)的API。
最早的Socket 接口是Berkeley接口,在Unix小小操作系统中实现。
WinSock也是一个基于Socket模型的API,在Microsoft Windows操作系统类中使用。
它在Berkeley接口函数的基础之上,还增加了基于消息驱动机制的Windows扩展函数。
只支持TCP/IP网络,增加了对更多协议的支持。
这里,讨论TCP/IP网络上的API。
Socket接口包括三类函数:第一类是WinSock API包含的Berkeley socket函数。
这类函数分两部分。
第一部分是用于网络I/O的函数,如accept、Closesocket、connect、recv、recvfrom、Select、Send、Sendto。
另一部分是不涉及网络I/O、在本地端完成的函数,如bind、getpeername、getsockname、getsocketopt、htonl、htons、inet_addr、inet_nton、ioctlsocket、listen、ntohl、ntohs、setsocketopt、shutdow、socket等第二类是检索有关域名、通信服务和协议等Internet信息的数据库函数,如gethostbyaddr、gethostbyname、gethostname、getprotolbyname、getprotolbynumber、getserverbyname、getservbyport。
第三类是Berkekley socket例程的Windows专用的扩展函数,如gethostbyname对应的WSAAsynGetHostByName(其他数据库函数除了gethostname都有异步版本),select对应的WSAAsynSelect,判断是否阻塞的函数WSAIsBlocking,得到上一次Windsock API错误信息的WSAGetLastError,等等。
socket编程面试题
socket编程面试题Socket编程是一种用于实现网络通信的常见方式,特别适用于客户端与服务器之间的通信。
在Socket编程的面试中,面试官经常会问到与Socket相关的问题。
下面将介绍一些常见的Socket编程面试题及其答案。
问题1:什么是Socket编程?回答:Socket编程是一种用于实现网络通信的编程方式,它允许不同设备之间在网络上进行数据传输。
在Socket编程中,使用SocketAPI来创建、连接、接收和发送数据。
问题2:Socket编程的工作原理是什么?回答:Socket编程使用TCP/IP协议栈,其中TCP(传输控制协议)用于可靠的数据传输,IP(网际协议)用于寻址和路由。
Socket编程中,客户端和服务器之间建立一个Socket连接。
客户端通过Socket发送请求给服务器,服务器接收请求并发送相应数据给客户端。
这种通信是通过网络上的TCP/IP连接实现的。
问题3:Socket编程中,什么是服务器Socket和客户端Socket?回答:在Socket编程中,服务器Socket和客户端Socket是两种不同的Socket。
服务器Socket绑定到一个已知的地址和端口,等待客户端连接请求。
一旦接收到客户端连接请求,服务器Socket会创建一个与客户端通信的新Socket。
客户端Socket用于向服务器发出连接请求,并与服务器Socket建立连接后进行通信。
问题4:如何创建一个Socket连接?回答:在Java中,使用Socket类来创建一个Socket连接。
客户端需要指定服务器的主机名(或IP地址)和端口号,然后调用Socket类的构造函数创建Socket对象。
服务器端需要绑定到一个特定的端口,并通过ServerSocket类的accept()方法监听客户端连接请求。
问题5:Socket编程中,如何实现数据的发送和接收?回答:在Socket编程中,使用InputStream和OutputStream类来进行数据的发送和接收。
C#阻塞和非阻塞模式及其应用
C#阻塞和非阻塞模式及其应用同步、异步、阻塞、非阻塞的概念:同步模式:客户端发送请求后,在发送下一个请求之前,必须从服务器获得响应。
此时,所有请求都在服务器上同步异步模式:客户端发送请求后,无需等待服务器的响应就可以发送下一个请求。
阻塞模式:指的是socket的调用函数直到得到结果才会返回。
在调用结果返回之前,当前线程会被挂起,即套接字在线程调用上已经被阻塞,不会执行下一条语句。
非阻塞模式:当执行socket的调用函数时,即使不能立即得到结果,函数也不会阻塞当前线程,而是立即返回。
同步和异步属于通信模式,阻塞和非阻塞属于套接字模式。
在实现结果方面,同步和阻塞是一致的,异步和非阻塞是一致的。
阻塞模式:1、阻塞写操作:write()、send()、sendto()、sendmsg()2、阻塞读操作:read()、recv()、recvfrom()、recvmsg()3、阻塞接收连接:accept()4、阻塞连接:connect()阻塞模式的特点:优点:1、结构简单2、通信双方比较容易保持同步3、编程逻辑简单,当函数成功返回时,则进程继续;否则,当函数返回错误时,检查错误类型,实施错误处理缺点:1.在读取操作期间,该过程可能会被永远阻塞。
在读取操作期间,如果另一台主机崩溃,该进程将无法接收任何数据,从而导致永久阻塞。
其他操作一般不会永远阻塞,但可能会阻塞很长时间。
2.工艺效率相对较低。
当一个进程在读取过程中被阻塞时,它必须等待读取操作的返回,在等待过程中不能执行任何其他操作。
如果一个进程同时从多个套接字读取数据,只能串行完成:首先读取第一个套接字,进程阻塞,等待套接字的数据到达。
在这个过程中,即使其他套接字的数据到达,也无法唤醒该进程,只能在第一个套接字的数据到达后唤醒。
解决阻塞模式的效率的方法:1、超时控制方法,能够防止进程阻塞时间过长。
常用的控制方法是使用套接字选项设置函数SetSockOption()。
基于winsock的阻塞和非阻塞通信模型
摘要:在应用程序开发中,经常涉及各式各样的机器的交互通信问题。
在Windows操作系统下,可以使用MFC中的CSocket,也可以使用以Windows Api 为基础的Winsock等等。
本文主要描述了Winsock的两种实现方式,即阻塞方式和非阻塞方式。
并对应这两种方式,描述了Select模式和IOCP模式。
关键字:Winsock Blocking NonBlocking Select模式完成端口(IOCP)模式一、Winsock简介对于众多底层网络协议,Winsock是访问它们的首选接口。
而且在每个Win32平台上,Winsock都以不同的形式存在着。
Winsock是网络编程接口,而不是协议。
在Win32平台上,Winsock接口最终成为一个真正的“与协议无关”接口,尤其是在Winsock 2发布之后。
Win32平台提供的最有用的特征之一是能够同步支持多种不同的网络协议。
Windows重定向器保证将网络请求路由到恰当的协议和子系统;但是,有了Winsock,就可以编写可直接使用任何一种协议的网络应用程序了。
在广泛使用的windows平台下,winsock2被简单包装为一组庞大的Api库,通过WSA Start up加载的关于Winsock版本的信息,初始了winsock相关的dll 和lib,在成功调用了WSA Startup之后,即可设计自主的与通信有关的行为,当确认了执行完操作后,调用相应的WSA Cleanup,释放对winsock DLL的引用次数。
几乎所有在windows平台下可使用的通信框架都是由Winsock扩展而来的。
这里,之所以要再提windows下的winsock Api编程,并不多余,虽然也可以使用CSocket或ACE(ADAPTIVE Communication Environment)框架,但直接对较底层的本地操作系统API,会使我们更深的理解隐藏在框架下的实现,有时也可以解决一些实用问题。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
深入 CSocket 编程之阻塞和非阻塞模式2007年09月30日星期日 19:25有时,花上几个小时阅读、调试、跟踪优秀的源码程序,能够更快地掌握某些技术关键点和精髓。
当然,前提是对这些技术大致上有一个了解。
我通过几个采用 CSocket 类编写并基于 Client/Server (客户端 / 服务端)的网络聊天和传输文件的程序 ( 详见:源代码参考 ) ,在调试这些程序的过程中,追踪深入至 CSocket 类核心源码 Sockcore.cpp ,对于CSocket 类的运行机制可谓是一览无遗,并且对于阻塞和非阻塞方式下的 socket 程序的编写也是稍有体会。
阅读本文请先注意:这里的阻塞和非阻塞的概念仅适用于 Server 端 socket 程序。
socket 意为套接字,它与 Socket 不同,请注意首字母的大小写。
客户端与服务端的通信简单来讲:服务端 socket 负责监听,应答,接收和发送消息,而客户端 socket 只是连接,应答,接收,发送消息。
此外,如果你对于采用 CSocket 类编写 Client/Server 网络程序的原理不是很了解,请先查询一下(详见:参考书籍和在线帮助)。
在此之前,有必要先讲述一下:网络传输服务提供者, ws2_32.dll , socket 事件和 socket window 。
1、网络传输服务提供者(网络传输服务进程), Socket 事件, Socket Window网络传输服务提供者( transport service provider )是以 DLL 的形式存在的,在 windows 操作系统启动时由服务进程 svchost.exe 加载。
当 socket 被创建时,调用 API 函数 Socket (在 ws2_32.dll 中), Socket 函数会传递三个参数 : 地址族,套接字类型 ( 注 2 ) 和协议,这三个参数决定了是由哪一个类型的网络传输服务提供者来启动网络传输服务功能。
所有的网络通信正是由网络传输服务提供者完成 , 这里将网络传输服务提供者称为网络传输服务进程更有助于理解,因为前文已提到网络传输服务提供者是由svchost.exe 服务进程所加载的。
下图描述了网络应用程序、 CSocket ( WSock32.dll )、 SocketAPI(ws2_32.dll) 和网络传输服务进程之间的接口层次关系:当 Client 端 socket 与 Server 端 socket 相互通信时,两端均会触发socket 事件。
这里仅简要说明两个 socket 事件:∙FD_CONNECT: 连接事件 , 通常 Client 端 socket 调用 socket API 函数 Connect 时所触发,这个事件发生在 Client 端。
∙FD_ACCEPT :正在引入的连接事件,通常 Server 端 socket 正在接收来自 Client 端 socket 连接时触发,这个事件发生在 Server 端。
网络传输服务进程将 socket 事件保存至 socket 的事件队列中。
此外,网络传输服务进程还会向 socket window 发送消息 WM_SOCKET_NOTIFY , 通知有 socket 事件产生,见下文对 socket window 的详细说明。
调用 CSocket::Create 函数后,socket 被创建。
socket 创建过程中调用CAsyncSocket::AttachHandle(SOCKET hSocket, CAsyncSocket* pSocket, BOOL bDead) 。
该函数的作用是:∙将 socket 实例句柄和 socket 指针添加至当前模块状态(注 1 )的一个映射表变量 m_pmapSocketHandle 中。
∙在 AttachHandle 过程中,会 new 一个 CSocketWnd 实例 ( 基于 CWnd 派生 ) ,这里将这个实例称之为 socket window ,进一步理解为它是存放所有 sockets 的消息池( window 消息),请仔细查看,这里 socket 后多加了一个 s ,表示创建的多个 socket 将共享一个消息池。
∙当 Client 端 socket 与 Server 端相互通信时 , 此时网络传输服务进程向 socket window 发送消息 WM_SOCKET_NOTIFY ,需要说明的是CSocketWnd 窗口句柄保存在当前模块状态的 m_hSocketWindow 变量中。
2、阻塞模式阻塞模式下 Server 端与 Client 端之间的通信处于同步状态下。
在Server 端直接实例化 CSocket 类,调用 Create 方法创建 socket ,然后调用方法 Listen 开始侦听,最后用一个 while 循环阻塞调用 Accept 函数用于等待来自 Client 端的连接,如果这个 socket 在主线程(主程序)中运行,这将导致主线程的阻塞。
因此,需要创建一个新的线程以运行 socket 服务。
调试跟踪至 CSocket::Accept 函数源码:while(!Accept(...)){// The socket is marked as nonblocking and no connections are present to be accepted.if (GetLastError() == WSAEWOULDBLOCK)PumpMessage(FD_ACCEPT);elsereturn FALSE;}它不断调用 CAsyncSocket::Accept ( CSocket 派生自 CAsyncSocket 类)判断 Server 端 socket 的事件队列中是否存在正在引入的连接事件 -FD_ACCEPT (见 1 ),换句话说,就是判断是否有来自 Client 端 socket 的连接请求。
如果当前 Server 端 socket 的事件队列中存在正在引入的连接事件,Accept 返回一个非 0 值。
否则, Accept 返回 0,此时调用 GetLastError 将返回错误代码 WSAEWOULDBLOCK ,表示队列中无任何连接请求。
注意到在循环体内有一句代码:PumpMessage(FD_ACCEPT);PumpMessage 作为一个消息泵使得 socket window 中的消息能够维持在活动状态。
实际跟踪进入 PumpMessage 中,发现这个消息泵与 Accept 函数的调用并不相关,它只是使很少的 socket window 消息(典型的是 WM_PAINT 窗口重绘消息)处于活动状态,而绝大部分的 socket window 消息被阻塞,被阻塞的消息中含有 WM_SOCKET_NOTIFY。
很显然,如果没有来自 Client 端 socket 的连接请求, CSocket 就会不断调用 Accept 产生循环阻塞,直到有来自 Client 端 socket 的连接请求而解除阻塞。
阻塞解除后,表示 Server 端 socket 和 Client 端 socket 已成功连接,Server 端与 Client 端彼此相互调用 Send 和 Receive 方法开始通信。
3、非阻塞模式在非阻塞模式下利用 socket 事件的消息机制, Server 端与 Client 端之间的通信处于异步状态下。
通常需要从 CSocket 类派生一个新类,派生新类的目的是重载 socket 事件的消息函数,然后在 socket 事件的消息函数中添入合适的代码以完成Client 端与 Server 端之间的通信,与阻塞模式相比,非阻塞模式无需创建一个新线程。
这里将讨论当 Server 端 socket 事件- FD_ACCEPT 被触发后,该事件的处理函数 OnAccept 是如何进一步被触发的。
其它事件的处理函数如OnConnect, OnReceive 等的触发方式与此类似。
在 1 中已提到 Client/Server 端通信时, Server 端 socket 正在接收来自Client 端 socket 连接请求,这将会触发 FD_ACCEPT 事件,同时 Server 端的网络传输服务进程向 Server 端的 socket window (CSocketWnd )发送事件通知消息 WM_SOCKET_NOTIFY , 通知有 FD_ACCEPT 事件产生 , CsocketWnd 在收到事件通知消息后,调用消息处理函数 OnSocketNotify:LRESULT CSocketWnd::OnSocketNotify(WPARAM wParam, LPARAM lParam) {CSocket::AuxQueueAdd(WM_SOCKET_NOTIFY, wParam, lParam);CSocket::ProcessAuxQueue();return 0L ;}消息参数 wParam 是 socket 的句柄, lParam 是 socket 事件。
这里稍作解释一下,CSocketWnd 类是作为 CSocket 类的友元类,这意味着它可以访问 CSocket 类中的保护和私有成员函数和变量, AuxQueueAdd 和ProcessAuxQueue 是 CSocket 类的静态成员函数,如果你对友元不熟悉,请迅速找本有关 C++ 书看一下友元的使用方法吧!ProcessAuxQueue 是实质处理 socket 事件的函数,在该函数中有这样一句代码:CAsyncSocket* pSocket = CAsyncSocket::LookupHandle((SOCKET)wParam, TRUE);其实也就是由 socket 句柄得到发送事件通知消息的 socket 指针pSocket:从 m_pmapSocketHandle 中查找(见 1 )!最后, WSAGETSELECTEVENT(lParam) 会取出事件类型,在一个简单的switch 语句中判断事件类型并调用事件处理函数。
在这里,事件类型是FD_ACCEPT ,当然就调用 pSocket->OnAccept !结束语Server 端 socket 处于阻塞调用模式下,它必须在一个新创建的线程中工作,防止主线程被阻塞。
当有多个 Client 端 socket 与 Server 端 socket 连接及通信时,Server 端采用阻塞模式就显得不适合了,应该采用非阻塞模式 , 利用 socket 事件的消息机制来接受多个 Client 端 socket 的连接请求并进行通信。