精华内容
下载资源
问答
  • Socket原理 Socket简述 Socket,中文是“套接字”,是计算机之间进行通信的一种约定或者一种方式。通过socket,一台计算机可以准确的和多台...有很多的socket,比如Unix socket.我们常见的是internet socket,根据数据

    想到的几个库,还没有看,看得也不细,有点悲鸣!

    ——杰

    借鉴:socket技术

    Socket原理

    1. Socket简述
      Socket,中文是“套接字”,是计算机之间进行通信的一种约定或者一种方式。通过socket,一台计算机可以准确的和多台计算机通信。
      socket起源于Unix,Unix/Linux 基本哲学之一就是“一切皆文件”,都可以“打开-读写-关闭”模式。
      Socket()函数返回一个整型的文件描述符,随后建立连接、数据传输等操作都是通过该函数实现。

    2. Socket的类型
      有很多的socket,比如Unix socket.我们常见的是internet socket,根据数据的传输方式不同,可以分为两种类型。

    流格式套接字(SOCK_STREAM)
    面向连接的套接字,是一种可靠的、双向的通信数据流,可以准确无误的传输数据。(有自己的纠错机制)

    特点:
    数据在传输过程中不会消失;
    数据是按照顺序传输的;
    数据的发送和接收不是同步的 (其内部有一个缓冲区,用来传送和接收数据,他们的节奏是不同的)

    举例:http协议

    数据报格式套接字(SOCK_DGRAM)
    无连接的套接字,只管传输数据,不作数据的校验,传输的效率高。(数据丢失,小概率事件)

    特点:
    强调快速传输而非传输顺序;
    传输的数据可能丢失也可能损毁;
    限制每次传输的数据大小;
    数据的发送和接收是同步的。

    总之,数据报套接字是一种不可靠的、不按顺序传递的、以追求速度为目的的套接字。

    举例:QQ视频聊天

    1. OSI 网络七层模型
      七层模型,是物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。
      TCP/IP模型,是应用层、传输层、网络层、望楼接口。
      socket 编程,是站在传输层的基础上,所以可以使用 TCP/UDP 协议,但是不能干「访问网页」这样的事情,因为访问网页所需要的 http 协议位于应用层。
      TCP/IP模型中,包含TCP、IP、UDP、Telnet、FTP等协议,也称为“TCP/IP”协议族
      这里是socket编程的层次关系:
      在这里插入图片描述
      IP地址:“网际协议地址”,一台计算机可以拥有一个独立的IP地址,一个局域网也可以拥有一个独立的IP地址。
      MAC地址:“局域网地址 LAN Address”。能真正唯一标识一台计算机。也可以修改,局域网中的路由器/交换机会记录每台计算机的 MAC 地址。
      端口号:有了 IP 地址和 MAC 地址,虽然可以找到目标计算机,但仍然不能进行通信。为了区分不同的网络程序,计算机会为每个网络程序分配一个独一无二的端口号

    2. win下的socket编程:
      服务端代码:

    #include <stdio.h>
    #include <winsock2.h>
    #pragma comment (lib, "ws2_32.lib")  //加载 ws2_32.dll
    
    int main(){
        //初始化 DLL
        WSADATA wsaData;
        WSAStartup( MAKEWORD(2, 2), &wsaData);
    
        //创建套接字
        SOCKET servSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
    
        //绑定套接字
        sockaddr_in sockAddr;
        memset(&sockAddr, 0, sizeof(sockAddr));  //每个字节都用0填充
        sockAddr.sin_family = PF_INET;  //使用IPv4地址
        sockAddr.sin_addr.s_addr = inet_addr("127.0.0.1");  //具体的IP地址
        sockAddr.sin_port = htons(1234);  //端口
        bind(servSock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR));
    
        //进入监听状态
        listen(servSock, 20);
    
        //接收客户端请求
        SOCKADDR clntAddr;
        int nSize = sizeof(SOCKADDR);
        SOCKET clntSock = accept(servSock, (SOCKADDR*)&clntAddr, &nSize);
    
        //向客户端发送数据
        char *str = "Hello World!";
        send(clntSock, str, strlen(str)+sizeof(char), NULL);
    
        //关闭套接字
        closesocket(clntSock);
        closesocket(servSock);
    
        //终止 DLL 的使用
        WSACleanup();
    
        return 0;
    }
    

    在这里插入图片描述
    在这里插入图片描述

    客户端代码:

    #include <stdio.h>
    #include <stdlib.h>
    #include <WinSock2.h>
    #pragma comment(lib, "ws2_32.lib")  //加载 ws2_32.dll
    
    int main(){
        //初始化DLL
        WSADATA wsaData;
        WSAStartup(MAKEWORD(2, 2), &wsaData);
    
        //创建套接字
        SOCKET sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
    
        //向服务器发起请求
        sockaddr_in sockAddr;
        memset(&sockAddr, 0, sizeof(sockAddr));  //每个字节都用0填充
        sockAddr.sin_family = PF_INET;
        sockAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
        sockAddr.sin_port = htons(1234);
        connect(sock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR));
    
        //接收服务器传回的数据
        char szBuffer[MAXBYTE] = {0};
        recv(sock, szBuffer, MAXBYTE, NULL);
    
        //输出接收到的数据
        printf("Message form server: %s\n", szBuffer);
    
        //关闭套接字
        closesocket(sock);
    
        //终止使用 DLL
        WSACleanup();
    
        system("pause");
        return 0;
    }
    

    大致的编程流程是:
    加载stdlib.h和winsock2.h头文件,还有ws2_32.dll,然后初始化dll,创建套接字,定义数据传输类型。再向服务器发起请求,将一段内存块中进行清零,确定输入IP,端口,然后连接。接收服务器传回的数据,定义一个缓冲区数组,recv接受数据。输出接收的数据,关闭套接字和dll。
    socket(int af, int type, int protocol),创建套接字。其中af,指IP地址;type指数据传输类型;protocol,传输协议。

    TCP/IP协议

    1. TCP/IP【TCP(传输控制协议)和IP(网际协议)】提供点对点的链接机制,将数据应该如何封装、定址、传输、路由以及在目的地如何接收,都加以标准化。

    TCP(Transmission Control Protocol,传输控制协议)是一种面向连接的、可靠的、基于字节流的通信协议,数据在传输前要建立连接,传输完毕后还要断开连接,客户端在收发数据前要使用 connect() 函数和服务器建立连接。建立连接的目的是保证IP地址、端口、物理链路等正确无误,为数据的传输开辟通道。
    TCP建立连接时要传输三个数据包,俗称三次握手

    1. 数据的“粘包”问题,客户端发送的多个数据包被当做一个数据包接收。也称数据的无边界性,read()/recv() 函数不知道数据包的开始或结束标志(实际上也没有任何开始或结束标志),只把它们当做连续的数据流来处理。

    2. 数据结构
      在这里插入图片描述
        (1) 序号:Seq(Sequence Number)序号占32位,用来标识从计算机A发送到计算机B的数据包的序号,计算机发送数据时对此进行标记。
        (2) 确认号:Ack(Acknowledge Number)确认号占32位,客户端和服务器端都可以发送,Ack = Seq + 1。
        (3) 标志位:每个标志位占用1Bit,共有6个,分别为 URG、ACK、PSH、RST、SYN、FIN,

    3. 三次握手(建立连接的过程):

    在这里插入图片描述
    用自己的话来表述:
    客户端向服务端请求连接,发送一个数据包,里面有自己的序列号;服务端接受到后,返回一个确认号,发送一个数据包,里面有自己的序列号;客户端接收到后,回复一个确认号。

    1. 四次握手(断开连接)
      在这里插入图片描述

    用自己的话来描述:
    客户端调用close()函数,向服务端发送一个fin数据包,包含序列号,请求断开连接;服务端收到后,回复确认;客户端收到后,进入fin_wait_2状态;服务端准备完毕后,发送一个fin包;客户端收到后,回复一个ACK关闭客户端;服务端收到后断开。

    1. 关于 TIME_WAIT 状态的说明
      客户端最后一次发送 ACK包后进入 TIME_WAIT 状态,而不是直接进入 CLOSED 状态关闭连接,这是为什么呢?

    TCP 是面向连接的传输方式,必须保证数据能够正确到达目标机器,不能丢失或出错,而网络是不稳定的,随时可能会毁坏数据,所以机器A每次向机器B发送数据包后,都要求机器B”确认“,回传ACK包,告诉机器A我收到了,这样机器A才能知道数据传送成功了。如果机器B没有回传ACK包,机器A会重新发送,直到机器B回传ACK包。

    客户端最后一次向服务器回传ACK包时,有可能会因为网络问题导致服务器收不到,服务器会再次发送 FIN 包,如果这时客户端完全关闭了连接,那么服务器无论如何也收不到ACK包了,所以客户端需要等待片刻、确认对方收到ACK包后才能进入CLOSED状态。那么,要等待多久呢?

    数据包在网络中是有生存时间的,超过这个时间还未到达目标主机就会被丢弃,并通知源主机。这称为报文最大生存时间(MSL,Maximum Segment Lifetime)。TIME_WAIT 要等待 2MSL 才会进入 CLOSED 状态。ACK 包到达服务器需要 MSL 时间,服务器重传 FIN 包也需要 MSL 时间,2MSL 是数据包往返的最大时间,如果 2MSL 后还未收到服务器重传的 FIN 包,就说明服务器已经收到了 ACK 包

    1. 优雅的断开连接–shutdown()

    close()/closesocket() 会立即向网络中发送FIN包,不管输出缓冲区中是否还有数据,而shutdown() 会等输出缓冲区中的数据传输完毕再发送FIN包。也就意味着,调用 close()/closesocket() 将丢失输出缓冲区中的数据,而调用 shutdown() 不会

    函数接口及原理

    1. socket()函数创建套接字

    socket(int af, int type, int protocol);

    1. socket() 函数用来创建套接字,确定套接字的各种属性,然后服务器端要用 bind() 函数将套接字与特定的IP地址和端口绑定起来。

    bind(int sock, struct sockaddr *addr, socklen_t addrlen)
    sock 为 socket 文件描述符,addr 为 sockaddr 结构体变量的指针,addrlen 为 addr 变量的大小,可由 sizeof() 计算得出

    1. 使用 bind() 绑定套接字后,还需要使用 listen() 函数让套接字进入被动监听状态,再调用 accept() 函数,就可以随时响应客户端的请求。

    listen(int sock, int backlog)
    sock 为需要进入监听状态的套接字,backlog 为请求队列的最大长度

    展开全文
  • 几大函数 socket int socket(int family, int type, int protocol);...type:表示套接字类型,TCP表示SOCK_STREAM,UDP为SOCK_DGRAM protocol默认为0 bind bind函数将一个本地协议地址赋予一个套接字 int b...

    几大函数

    socket
    int socket(int family, int type, int protocol);
    //成功则返回非0描述符,失败则返回-1
    

    family:表示协议族,IPV4表示AF_INET

    type:表示套接字类型,TCP表示SOCK_STREAM,UDP为SOCK_DGRAM

    protocol默认为0

    bind

    bind函数将一个本地协议地址赋予一个套接字

    int bind(int sockfd, const struct sockaddr* myaddr, socklen_t addrlen);
    //成功返回0,失败返回-1
    
    1. bind可以绑定的端口号为0,此时内核将在bind被调用时选择一个临时端口
    2. bind可以绑定的IP为通配IP INADDR_ANY,当客户端发起连接后,内核根据客户端的目的IP来指定该连接的本地IP
    3. bind常见的返回错误为EADDRINUSE,该错误发生在当该端口号已经被绑定到了某个套接字时,如果再次绑定该端口号将出现该错误,(可以通过SO_REUSEADDR和SO_REUSEPORT来修改套接字选项)
    listen
    int listen(int sockfd, int backlog);
    //成功返回0,失败返回-1
    

    listen的关键还是两个队列,即未完成队列和已完成队列,详见TCP/IP节

    accept
    int accept(int sockfd, struct sockaddr* clienaddr, socklen_t* addrlen);
    

    accept表示从已完成连接队列中取出一个连接,如果成功返回则返回一个由内核生成的全新描述符,该描述符用于和客户端建立连接。注:在服务器中包含两种描述符,即监听描述符和已连接描述符,其中已连接描述符表示和客户端所建立的连接,该描述符的地址和端口还是监听描述符所绑定的地址和端口号

    在阻塞模式下,如果已完成连接队列为空,则accept会阻塞

    accept的返回值错误情况:

    1. EINTR:当accept阻塞时被信号中断,则返回此错误
    2. EAGAIN/EWOULDBLOCK:非阻塞模式下,当已完成队列为空,返回该错误
    3. 当三次握手完成,在调用accept之前,如果客户端发送了一个RST,此时调用accept,那么不同的实现效果不同,Berkeley的实现是在内核中中止该连接,而服务器进程看不到,因此会阻塞,但是SVR4等实现则是返回一个错误ECONNABORTED,此时只需要重新调用accept即可,所以这也是为什么要IO多路复用搭配非阻塞模式的原因,否则此时accept将一直阻塞,但是在epoll的ET下,如果用非阻塞模式则要循环accept
    connect
    int connect(int sockfd, struct sockaddr* serv_addr, int addrlen);
    

    该函数用于和服务器建立连接,即用于发起三次握手,connect之前,不必给sockfd,bind端口和源IP,内核会在调用connect后自动为其分配和绑定

    connect如果采用阻塞模式,则将在发起连接请求后等待对方响应SYN,如果一段时间内未收到,则重发连接请求报文,若等待了75s(75s到几分钟)后还没有收到SYN则放弃,并返回错误

    connect的各种错误返回:

    1. ETIMEDOUT:连接超时,即75s内没有收到对方的SYN响应
    2. ECONNREFUSED:若服务器对客户端的响应是RST,则返回该错误,出现这种错误的可能是服务器在指定的端口号没有进程在监听
    3. EHOSTUNREACH/ENETUNREACH,连接请求报文目的地址不可达时(收到IMCP错误),则将同1重发,75s超时后返回该错误。
    4. EBADF:参数sockfd不是合法的socket,即可能是关闭了sockfd,然后又用sockfd去处理
    5. EFAULT:参数serv_addr指针指向无法存取的内存空间
    6. ENOTSOCK:sockfd是一文件描述词,不是socket
    7. EISCONN:参数sockfd的socket已经连接,即该socket已经建立了连接
    8. EAFNOSUPPORT:sockaddr结构的sa_family不正确
    9. EINPROGRESS:当connect为非阻塞时,返回此错误,表示连接请求正在进行还没有完成
    10. EADDRINUSE:本地地址处于使用状态
    11. EALREADY:套接字为非阻塞,并且之前有连接请求还没有完成
    12. EINTR:connect如果被信号中断将返回该错误,即慢系统调用被信号中断,当connect被EINTR中断时,不能再次重启connect,这是因为已经发起了连接请求,如果再次调用将返回错误,此时必须等待select/epoll将连接请求完成

    connect需要额外的说一下,一般而言connect用非阻塞模式去连接服务器,这是因为如果用connect去连接,那么connect的超时时间较长(75s,甚至更长),而有的程序希望在短时间内结束等待,所以可以将connect设置为非阻塞,当连接失败时用select/epoll去等待很短时间,如果还没有连接,则放弃连接

    非阻塞模式下的connect流程如下:

    1. 调用connect,如果返回0,则表示成功建立连接(一般如果客户端和服务器在同一台主机上可能会出现这种情况),正常情况是返回-1,错误码是EINPROGRESS
    2. 将该sock注册到select/epoll中,利用select/epoll去检测连接是否成功建立,如果成功建立则返回连接可写(连接建立后,写缓冲区空闲,故可写),如果连接建立失败,则返回可写可读(socket出现错误,连接可读可写)
    3. 因此需要进一步判断连接是否建立成功,常见的方法有两种:
      • 调用getsockopt来检测描述符是否出现错误,如果没有错误则获取的error为0,连接成功建立,如果有错误则可以得到相应的错误值,表示连接建立失败
      • 第二种方法是此时再调用connect,如果errno是EISCONN则连接已经建立,否则任务连接建立失败

    https://blog.csdn.net/nphyez/article/details/10268723

    https://blog.csdn.net/u012377333/article/details/45391921

    read/write
    size_t read(int socket, void* buf, size_t length);
    size_t write(int socket, const void* buf, size_t length);
    
    1. read

      • read是从TCP内核缓冲区中读取数据,将读取到的数据放入应用层缓冲区中
      • 其返回成功读出的数据字节数(可能是将缓冲区中的剩余数据读完了,也可能是读取了一部分被信号打断导致返回)
      • 读取失败则返回-1,并设置errno
      • read返回0可能有两种情况,其一是read的length为0,其二是read读取到了文件结束符
      • read的常见错误码
        • EAGAIN/EWOULDBLOCK:fd设定为非阻塞,且要被阻塞时立即返回-1,设定相应的errno
        • EINTR:慢系统调用常见的错误,当系统调用函数阻塞时被其他信号打断需要执行信号函数,在信号函数处理完毕后则返回此错误,在read中常见的包括阻塞时被打断则返回该错误,如果是在读取时被打断则返回实际读取的字节数。
          • 关于该错误,发生该错误时可以考虑重启该系统调用即可(即忽略该错误)
        • ECONNREST,当read从一个收到RST包的socket上读取数据时,将会返回该错误
        • ETIMEDOUT,当对端主机崩溃,则超时后, 套接字将被标记为此错误,调用read将出现此错误
        • EHOSTUNREACH,当对端主机不可达,出现此错误
    2. write

      • write即将数据从应用层缓冲区拷贝到内核缓冲区,需要注意,write成功执行只能表明数据成功拷贝进入了缓冲区,但是不能保证数据已经成功发送了。

      • write返回值如下:

        • 等于length
        • 小于length,在阻塞模式下,write如果没有完成需要写length字节,则会阻塞直到写完为止,但是在非阻塞模式下,则是返回实际写入的字节数,也可能是写入过程中被中断信号打断
        • 返回0,write返回0,一般是因为length为0
      • write返回错误的情况:

        • EAGAIN/EWOULDBLOCK 非阻塞模式下,如果内核缓冲区没有空间则会返回-1
        • EINTR,如果write没有写入一个字节就阻塞了,那么将返回-1,否则返回实际写入的字节数
      • EPIPE:fd是一个pipe或者socket时,收到SIGPIPE信号时会返回该错误

        • ETIMEDOUT,当对端主机崩溃,则超时后, 套接字将被标记为此错误,调用read将出现此错误
        • EHOSTUNREACH,当对端主机不可达,出现此错误
    3. read和fread的区别/write和fwrite的区别

      • fread为库函数,而read为系统函数,一般而言fread效率高,因为fread有缓存,且缓存为系统自动分配,如果要读取8K的数据,但是只分配了2K的缓存,则read需要读取4次磁盘,发生4次系统调用,而fread则会自动分配缓存,先将8K一次性从磁盘中读取,只需要一次系统调用,等再读取数据时直接从缓冲区中读取即可

      • fwrite也是一样,write如果需要写入10个字节到内核,则会直接写入到内核缓冲区,而fwrtie则是先写入到应用缓冲区,等写满后再调用fflush将缓冲区中的数据写入内核,这样减少了系统调用

      • https://blog.csdn.net/q2519008/article/details/84308726

      • fread(void* buf, size_t size,1, FILE* fp);
        fwrite(void* buf, 1, size_t size, FILE* fp);
        

    https://blog.csdn.net/songchuwang1868/article/details/90665865

    https://blog.csdn.net/dangzhangjing97/article/details/79619894

    close/shutdown

    close: close在关闭fd时,只是将fd的引用计数减1,如果fd的引用计数为0,那么则会关闭此套接字(关闭读写缓冲区),注意close会关闭套接字的读和写,同时触发连接关闭操作,此时向对端发送FIN包,对端回送ACK,注意TCP允许半关闭存在,所以此时对端还可以向本端发送数据,但是close却关闭了本端的读写,所以此时如果本端收到了对端的数据,将会回复对方RST包,即如果一方调用了close,但是实际上最好还是将两条连接都关闭

    1. 当client调用close关闭连接时,如果client存在收到的远端的,没有处理的消息,这时close要丢弃这些消息。但丢弃这些数据意味着server端以为发出的消息已经被本机成功处理(ACK确认过了),但是实际上并未处理,此时不会使用正常的四次握手关闭,而是会向server发送一个RST非正常复位关闭连接,所以这样要求在编写代码时要保证在关闭连接前已经接收、处理了连接上的消息。(如果某端连续两次写入,在第一次写入后收到了RST,则第二次写入一定会引起SIGPIPE错误)如果某端收到了RST,那么RST不会立即交给应用层,等下一次在调用write函数写入时,会导致SIGPIPE信号,该信号默认终止进程,但是最好忽略该信号,此时的write会返回EPIPE错误
    2. 当没有未处理的消息时,client在调用close后会首先确保要发送的数据都尽量发出,然后发出FIN包,此时client不会再通过连接接收和发送数据。此时如果server调用read会返回0,跳出循环,也执行close操作从而关闭server端的连接。但是此时server还是可以write的,write只是负责把数据交给TCP的发送缓冲区就可以成功的返回,所以并不会报错,当TCP连接将数据再次发送给client时,由于之前client已经关闭了连接,所以client收到数据后会应答一个RST段,表示其不会再收到数据,连接重置。Server端收到RST后并不会立即通知应用层,只把这个状态保存再TCP协议层。若server再次调用write发送给client,由于其已经处于RST状态,因此不会将数据发出,而是发一个信号给SIGPIPE给应用层,从而终止程序(该信号的默认处理是终止程序)。

    shutdown

    int shutdown(int sockfd, int howto);
    /*
    howto的取值包括
    1.SHUT_RD
    2.SHUT_WR
    3.SHUT_RDWR
    */
    

    shutdown则不会理会引用计数的问题,而是直接破坏TCP连接,且会等到将缓冲区中的数据发送完毕后,直接发送FIN,但是shutdown不会释放掉套接字,即调用了shutdown还是需要close来释放套接字

    1. Shutdown关闭读时,则client不再读取数据,但是若server给client发送数据,则client根据操作系统的不同采取不同的措施,UNIX则回复ack,并扔掉该数据,linux回复ack并缓冲区下来,win会回复RST(注意关闭读,不会触发TCP连接的关闭操作)
    2. Shutdown关闭写时,client不再向连接写,则会触发TCP连接的关闭操作,client向server发送FIN包,但是server仍然可以发送数据给client,且client可以接收数据。(这个很重要,即shutdown只是关闭写,但是另一方还是可以继续给本端发送数据的,本端还是可以继续收到数据的,但是关闭了写那么FIN包就已经发送过去了,如果对端调用了read就会读到FIN)
    3. Shutdown关闭读和写则类比close,但是注意,shutdown始终不会关闭socket id本身即此fd最终还是需要close来关闭。(在muduo中是通过RAII的方式最后来close fd,但是连接关闭是通过shutdown的方式)

    最后我谈谈自己对close和shutdown的理解,这两个函数其实对应了四次挥手的两种不同做法,shutdown可以只关闭写,同时触发连接关闭,这是客户端是可以继续接收服务器的数据的,等到服务器发送数据完毕,服务器也关闭了连接,此时四次挥手结束,但是close的关闭就不是那么优雅了,其直接关闭了客户端的读和写,然后还触发了连接关闭,一旦对端再给自己发送数据,那么就触发RST包,简单粗暴的让对端关闭连接

    https://blog.csdn.net/xyyaiguozhe/article/details/30252559

    https://blog.csdn.net/qq_33113661/article/details/88428604

    展开全文
  • 使用socket函数一些常见错误

    千次阅读 2018-06-26 13:24:38
    1.socketSOCKET socket( int af, int type, int protocol ... 错误:10047(使用了与请求协议不兼容地址)type,通常为SOCK_STREAM或SOCK_DGRAM 头文件中定义只有如下几种标准类型: #define SOCK_STREAM 1...

    1.socket

    SOCKET socket( int af, int type, int protocol );

    • af:常为AF_INET 
      使用AF_ISO等其他地址族标识,而非AF_INET。 
      返回:-1。 
      错误:10047(使用了与请求的协议不兼容的地址)

    • type,通常为SOCK_STREAM或SOCK_DGRAM 
      头文件中定义的只有如下几种标准类型: 
      #define SOCK_STREAM 1 
      #define SOCK_DGRAM 2 
      #define SOCK_RAW 3 
      #define SOCK_RDM 4 
      #define SOCK_SEQPACKET 5 
      使用非如上定义的类型。 
      返回:-1。 
      错误:10044(在这个地址家族中不存在对指定的插槽类型的支持)

    • protocol,通常为0

      1. type = SOCK_STREAM,protocol = 6 
        正常
      2. type = SOCK_STREAM,protocol = 7 
        返回:-1 
        错误号:10043(请求的协议还没有在系统中配置,或者没有它存在的迹象)
      3. type = SOCK_DGRAM,protocol = 17 
        正常
      4. type = SOCK_DGRAM,protocol = 19 
        返回:-1 
        错误号:10043(请求的协议还没有在系统中配置,或者没有它存在的迹象)
    • 结论

      1. Socket暂时只支持AF_INET协议族。
      2. 对非标准的套接字类型不支持。
      3. 协议号参数可以为0,则使用与套接字类型相应的协议号;否则,协议号参数必须与相应的套接字类型相同。

    2.bind

    int bind( SOCKET s, const struct sockaddr FAR* name, int namelen );


    • 在没有用socket申请资源的套接字上操作。 
      返回:-1 
      错误号:10038(在一个非套接字上尝试了一个操作)

    • name,通常使用AF_INET地址族、INADDR_ANY(0)地址

      1. 在local结构中,sin_family成员赋值为AF_OSI, 
        返回: -1 
        错误码:10047(使用了与请求的协议不兼容的地址)
      2. 在local结构中,sin_addr成员赋值为本计算机的IP地址,
      3. 在local结构中,sin_addr成员赋值为非本计算机的IP地址,如同小组的另一个同学的IP地址; 
        返回: -1 
        错误码:10049( 在其上下文中,该请求的地址无效)
      4. 在local结构中,sin_port成员赋值为135; 
        返回: -1 
        错误码:10048(通常每个套接字地址(协议/网络地址/端口)只允许使用一次)
    • namelen,通常为name所指的结构的大小,如sizeof(SOCKADDR_IN)

      1. namelen = 10 
        返回: -1 
        错误码:10014(系统检测到在一个调用中尝试使用指针参数时的无效指针地址)
      2. namelen = 16 
        返回: 0 
        正常
      3. namelen = 40 
        返回: 0 
        正常
    • 结论

      1. 可以bind本机拥有的地址(或INADDR_ANY),非本机拥有的地址出错。
      2. bind已经被占用的端口值会出错。
      3. len参数要大于等于地址结构实际上所占的长度。
    • 思考

      1. 因为本机可以有多个IP,所以需要有方法指出从哪个实体接收数据。
      2. 当然,提供一种表达“从所有实体接收”的方法是必要的。
      3. 在头文件中INADDR_ANY被明确定义为0。
      4. 关于bind已占用的端口。是指端口被bind,并且上层还是活的。(不设置复用)处于TIME-WAIT状态的端口表面上是被占用,实际上是可以bind成功的,但connect会失败。详见关于TIME-WAIT的笔记,第六条。

    3.listen

    int listen(SOCKET s, int backlog);


    • 使用尚未半相关的套接字。(未成功bind的) 
      返回:-1 
      错误号:10022(提供了一个无效的参数)

    • backlog 
      纯引用一段:(无出处)

    “windows套接字实现中最多只允许服务器同时监听5个套接字。使用参数0,则系统将把该参数改为1,而使用超过5的值,系统将自动把该参数改为5。”

      设置参数值为0,有1个客户机可同时与服务器连接(在vista下有时有2个可以连接,有时有3个可以连接,-_0//)设置参数值为1,有1个客户机可同时与服务器连接设置参数值为10,有10个客户机可同时与服务器连接

    • 结论

      1. 第一个参数的套接字必须是成功bind过后的。
      2. 监听个数为0的话,会自动设置为1。
      3. 最大监听个数在XP SP3下可以超过5。
    • 问题

      1. 如何获得实际的backlog值? 
        MSDN: There is no standard provision to obtain the actual backlog value.

      2. 如何结束套接字的监听状态? 
        首先,close掉是可以的。如果不close呢?最初猜想backlog为0,-1等特殊值可以达到此效果,结果失败。求解。

    4.accept

    SOCKET accept( SOCKET s, struct sockaddr FAR* addr, int FAR* addrlen );


    • 在没有listen的套接字上面。 
      返回:-1 
      错误号:10022(提供了一个无效的参数)

    • addr,输出参数,一般不填 
      单机测试,填上本机的IP和某个端口号。 
      结果:无法限制所接收的地址,执行完后addr中存放实际的地址。

    • addrlen 
      同bind

    • 结论

      1. 主套接字必须处于监听状态。
      2. 在地址字段填上任何值不能限制接受的连接。
      3. len参数要求所携带的值大于等于16。(AF_INET地址结构的长度)

    5.recv

    int recv( SOCKET s, char FAR* buf, int len, int flags ); 
    对于服务器,一般是ns = accept(s , &addr , &len) ;

    • s,一般是用上面accept正常返回的值

      1. 在没有accept的从套接字上操作。(上面的ns) 
        返回:-1 
        错误号:10038(在一个非套接字上尝试了一个操作)

      2. 在主套接字上操作。(上面的s) 
        返回:-1 
        错误号:10057(由于套接字没有连接并且 
        (当使用一个 sendto 调用发送数据报套接字时)没有提供地址, 
        发送或接收数据的请求没有被接受。 )

    • buf,要求指向一个有效的缓冲区 
      如果指向的无效的内存区域 
      返回: -1 
      错误号:10014(系统检测到在一个调用中尝试使用指针参数时的无效指针地址)

    • len,发送的字节数 
      len过长可能造成缓冲区溢出。 
      这个属于编程中的普遍考虑问题,不是socket函数特有。

    • flags,一般用0 
      设置MSG_PEEK标志后,接收但不移除数据。 
      (再次接收可得到相同的数据)

    • 结论

      1. 对服务器来说,必须传递成功accept之后返回的套接字。
      2. 缓冲区指针所指位置必须有效。
      3. 缓冲区长度参数不可超过实际准备的缓冲区长度。
      4. MSG_PEEK标志在接收的时候将保留数据。

    6.send

    int send( SOCKET s, const char FAR* buf, int len, int flags );

    • s,同recv

      1. 在没有accept的从套接字上操作。(上面的ns) 
        返回:-1 
        错误号:10038(在一个非套接字上尝试了一个操作)

      2. 在主套接字上操作。(上面的s) 
        返回:-1 
        错误号:10057(由于套接字没有连接并且 
        (当使用一个 sendto 调用发送数据报套接字时)没有提供地址, 
        发送或接收数据的请求没有被接受。 )

    • buf 
      必须指向有效缓冲区,同recv

    • len 
      必须和要发送的数据长度一致。

    • 结论 
      除flag可选项不同外,和recv一致。

    7.closesocket

    int closesocket( SOCKET s );

    • s

      1. 在申请套接字资源(调用socket)之前closesocket 
        返回: -1 
        错误号:10038(在一个非套接字上尝试了一个操作)

      2. 再已经closesocket的套接字上closesocket 
        返回: -1 
        错误号:10038(在一个非套接字上尝试了一个操作)

    • 结论 
      s必须是有效打开的套接字。 
      不得重复关闭。

    8.connect

    int connect( SOCKET s, const struct sockaddr FAR* name, int namelen );

    • 没有对端响应的情况 
      在没有运行服务器的情况下,connect是否会一直阻塞? 
      结果:等待一定时间后返回错误。 
      返回: -1 
      错误码:10061(由于目标机器积极拒绝,无法连接)


    • 没有使用过bind的套接字。 
      成功连接。 
      产生隐式绑定,相关应用的详细资料。

    • name 
      使用一些特殊的地址来测试。

      1. 使用远端点IP地址为INADDR_ANY测试。 
        返回:-1 
        错误号:10049(在其上下文中,该请求的地址无效)

      2. 使用远端点IP地址为10.1.1.255广播地址。 
        返回: -1 
        错误号:10060(由于连接方在一段时间后没有正确答复或连接的主机没有反应,连接尝试失败)

    • namelen 
      同bind等需要传递地址结构长度的函数

    • 结论

      1. 服务器必须启动listen。
      2. 可以不建立本地半相关,则进行隐式绑定。
      3. 客户不可以与INADDR_ANY主动相连,立即返回报错。
      4. 客户不可以与广播地址连接,会等待很久,返回失败。
    • 讨论 
      10060和10061两种错误不同。 
      其中10061解释为目标机器积极拒绝,返回错误很快(秒级)。 
      10060的情况,返回错误需要很长时间(几十秒级)。 
      此处值得深入研究,两种情况下的抓包应该不一样。

    9.recvfrom

    int recvfrom( SOCKET s, char FAR* buf, int len, int flags, struct sockaddr FAR* from, int FAR* fromlen );

    • 缺少半相关 
      在没有bind的套接字上面,直接recvfrom。 
      返回:-1 
      错误号:10022(提供了一个无效的参数)

    • 填写from结构 
      向其中填入非对端使用的地址或端口。 
      正常接收,并且from内置对端地址信息。

    • 结论 
      必须先进行本地半相关,指定端口,才能够接收。 
      无法通过recvfrom的地址结构限制接收的地址和端口。

    10.sendto

    int sendto( SOCKET s, const char FAR* buf, int len, int flags, const struct sockaddr FAR* to, int tolen );

    • 缺少半相关 
      在没有bind的套接字上面,直接sendto。 
      成功。返回发送的数据个数。

    • 不存在的对端实体

      1. sendto到一个不存在的实体(to结构) 
        返回:发送的字符数 
        错误号:无

      2. 紧接着调用recvfrom 
        结果:没有阻塞,直接返回 
        返回:-1 
        错误号:10054 (远程主机强迫关闭了一个现有的连接。)

    • 结论

      1. 可以在未本地半相关的情况下发送数据。由系统随机选择端口。

      2. 可以向不存在的远端点发送数据,本地仍然报告发送的字节数(不管有没有人接收)。

      3. 一般情况下,没有数据的时候recvfrom会阻塞。但是当给不存在的对端发送过数据后,会收到错误报告,紧接着的一次recvfrom会立即返回失败。(后面的仍然阻塞)

    • 思考

      1. SOCK_DGRAM类型的服务,无法为用户确保数据的正常交付。但是通过recvfrom返回的错误,可以对发送情况作出一定的判断。

      2. 这也启示在使用SOCK_DGRAM时候的编程框架要考虑下,当recvfrom错误的时候,判断一下错误号,再进一步处理。

    展开全文
  • Socket 功能 和 套接字三种类型

    千次阅读 2019-03-06 17:09:08
    6.2.2 Socket的功能 Socket的英文原意就是“孔”或“插座”,现在,作为BSD UNIX的...日常生活中常见的插座,有的是信号插座,有的是电源插座,有的可以接受信号(或能量),有的可以发送信号(或能量)。假如电话线与...

    转载自  http://blog.chinaunix.net/uid-22240661-id-1781638.html

     

     

    6.2.2  Socket的功能
        Socket的英文原意就是“孔”或“插座”,现在,作为BSD UNIX的进程通讯机制,取其后一种意义。日常生活中常见的插座,有的是信号插座,有的是电源插座,有的可以接受信号(或能量),有的可以发送信号(或能量)。假如电话线与电话机之间安放一个插座(相当于二者之间的接口,这一部分装置物理上是存在的)则Socket非常相似于电话插座。

        将电话系统与面向连接的Socket机制相比,有着惊人相似的地方。以一个国家级的电话网为例。电话的通话双方相当于相互通信的两个进程;通话双方所在的地区(享有一个全局唯一的区号)相当于一个网络,区号是它的网络地址;区内的一个单位的交换机相当于一台主机,主机分配给每个用户的局内号码相当于Socket号(下面将谈到)。

        任何用户在通话之前,首先要占有一部电话机,相当于申请一个Socket号;同时要知道对方的电话号码,相当于对方有一个 Socket。然后向对方拨号呼叫,相当于发出连接请求(假如对方不在同一区内,还要拨对方区号,相当于给出网络地址)。对方假如在场并空闲(相当于通信的另一主机开机且可以接受连接请求),拿起电话话筒,双方就可以正式通话,相当于连接成功。双方通话的过程,是向电话机发出信号和从电话机接受信号的过程,相当于向Socket发送数据和从Socket接受数据。通话结束后,一方挂起电话机,相当于关闭Socket,撤消连接。

        在电话系统中,一般用户只能感受到本地电话机和对方电话号码的存在,建立通话的过程、话音传输的过程以及整个电话系统的技术细节对它都是透明的,这也与Socket机制非常相似。Socket利用网间网通信设施实现进程通信,但它对通信设施的细节毫不关心,只要通信设施能提供足够的通信能力,它就满足了。

        至此,我们对Socket进行了直观的描述。抽象出来,Socket实质上提供了进程通信的端点。进程通信之前,双方首先必须各自创建一个端点,否则是没有办法建立联系并相互通信的。正如打电话之前,双方必须各自拥有一台电话机一样。

        每一个Socket都用一个半相关描述:
             {协议,本地地址,本地端口}
        一个完整的Socket则用一个相关描述:
             {协议,本地地址,本地端口,远程地址,远程端口}
        每一个Socket有一个本地的唯一Socket号,由操作系统分配。

        最重要的是,Socket是面向客户-服务器模型而设计的,针对客户和服务器程序提供不同的Socket系统调用。客户随机申请一个 Socket号(相当于一个想打电话的人可以在任何一台入网的电话上拨叫呼叫);服务器拥有全局公认的Socket,任何客户都可以向它发出连接请求和信息请求(相当于一个被呼叫的电话拥有一个呼叫方知道的电话号码)                          。
        Socket 利用客户-服务器模式巧妙的解决了进程之间建立通信连接的问题。服务器Socket为全局所公认非常重要。两个完全随机的用户进程之间,因为没有任何一方的Socket是固定的,就像打电话却不知道别人的电话号码,要通话是不可能的。
            

    6.2.3  套接字的三种类型
        套接字有三种类型:流式套接字(SOCK_STREAM),数据报套接字(SOCK_DGRAM)及原始套接字。
        1.流式套接字(SOCK_STREAM)
        流式的套接字可以提供可靠的、面向连接的通讯流。如果你通过流式套接字发送了顺序的数据:“1”“2”,那么数据到达远程时候的顺序也是“1”“2”。
        流式套接字可以做什么呢?你听说过Telnet应用程序吗?听过?哦,最常用的BBS服务,以及系统的远程登陆都是通过Telnet协议连接的。Telnet就是一个流式连接。你是否希望你在Telnet应用程序上输入的字符(或汉字)在到达远程应用程序的时候是以你输入的顺序到达的?答案应该是肯定的吧。还有WWW浏览器,它使用的HTTP协议也是通过流式套接字来获取网页的。事实上,如果你Telnet到一个Web Site的80端口上,然后输入“GET网页路径名”然后按两下回车(或者是两下 Ctrl+回车)然后你就得到了“网页路径名”所代表的网页!
        流式套接字是怎样保证这种应用层次上的数据传输质量呢?它使用了TCP(The Transmission Control Protocol)协议(可以参考RFC-793来得到TCP的细节)。TCP保证了你的数据传输是正确的,并且是顺序的。TCP是经常出现的TCP/IP中的前半部分。IP代表Internet Protocol(因特网协议,参考RFC-791)IP只处理网络路由。

        2.数据报套接字(SOCK_DGRAM)
        数据报套接字定义了一种无连接的服务,数据通过相互独立的报文进行传输,是无序的,并且不保证可靠,无差错。原始套接字允许对低层协议如IP或ICMP直接访问,主要用于新的网络协议实现的测试等。
        数据报套接字(Datagram Sockets)怎样呢?为什么它叫做“无连接”?应该怎样处理它们呢?为什么它们是不可靠的?好的,这里有一些事实:
        ·如果你发送了一个数据报,它可能不会到达。
        ·它可能会以不同的顺序到达。
        ·如果它到达了,它包含的数据中可能存在错误。
        数据报套接字也使用IP,但是它不使用TCP,它使用使用者数据报协议UDP(User Datagram Protocol可以参考RFC 768)
        为什么说它们是“无连接”的呢?因为它(UDP)不像流式套接字那样维护一个打开的连接,你只需要把数据打成一个包,把远程的IP贴上去,然后把这个包发送出去。这个过程是不需要建立连接的。UDP的应用例子有:tftp, bootp等。
         那么,数据包既然会丢失,怎样能保证程序能够正常工作呢?事实上,每个使用UDP的程序都要有自己的对数据进行确认的协议。比如,TFTP协议定义了对于每一个发送出去的数据包,远程在接受到之后都要回送一个数据包告诉本地程序:“我已经拿到了!(一个“ACK”包)。如果数据包发的送者在5秒内没有的得到回应,它就会重新发送这个数据包直到数据包接受者回送了“ACK”信号。这些知识对编写一个使用UDP协议的程序员来说是非常必要的。
        无连接服务器一般都是面向事务处理的,一个请求一个应答就完成了客户程序与服务程序之间的相互作用。面向连接服务器处理的请求往往比较复杂,不是一来一去的请求应答所能解决的,而且往往是并发服务器。

        套接字工作过程如下:服务器首先启动,通过调用socket()建立一个套接字,然后调用bind()将该套接字和本地网络地址联系在一起,再调用listen()使套接字做好侦听的准备,并规定它的请求队列的长度,之后就调用accept()来接收连接。客户在建立套接字后就可调用connect()和服务器建立连接。连接一旦建立,客户机和服务器之间就可以通过调用read()和write()来发送和接收数据。最后,待数据传送结束后,双方调用close()关闭套接字。
        3.原始套接字
        原始套接字主要用于一些协议的开发,可以进行比较底层的操作。它功能强大,但是没有上面介绍的两种套接字使用方便,一般的程序也涉及不到原始套接字。

    展开全文
  • socket通信数据类型

    2014-01-02 16:03:48
    在java中socket传输数据时,数据类型往往比较难选择。可能要考虑带宽、跨语言、版本的兼容等问题。比较常见的做法有两种:一是把对象包装成JSON字符串传输,二是采用java对象的序列化和反序列化。随着Google工具...
  • socket通讯常见模式分析(一) 最近由于工作需要,本人对socket通讯相关信息进行了相应归纳和整理,现将结果和大家分享一下。socket通讯在应用中主要分成两个基本类型:阻塞I/O通信模型、非阻塞I/O通信模型...
  • 用这个系列在记录自己的C++网络编程之路。 首先,网络编程必然离不开socketAPI函数,大多数 Socket API 都源于BSD...### 常用的socketAPI函数 函数名称 函数简单描述 附加说明 socket 创造某种类型的套...
  • Socket 类 构造函数和常见方法

    千次阅读 2017-01-24 14:16:23
     通过系统默认类型的 SocketImpl 创建未连接套接字 Socket(InetAddress address, int port)   创建一个流套接字并将其连接到指定 IP 地址指定端口号。 Socket(InetAddress host, int port, boolean
  • socket

    2015-08-07 15:10:53
    常见socket有3种类型 1)流式socket(SOCK_STREAM) 流试套接字提供可靠。面向连接通信流,;使用tcp协议,从而保证了数据传输正确性和顺序性 2)数据包socket(SOCK_DGRAM) 数据报套接字定义了一种无连接...
  • 在实际应用中,socket发送的数据并不是按照一行一行发送的,比如我们常见的报文,那么我们就不能要求每发送一次数据,都在增加一个“\n”标识,这是及其不专业的,在实际应用中,通过是采用数据长度+类型+数据的方式...
  • 在一个较低的水平,可以访问底层的操作系统,它可以实现面向连接和无连接协议的客户端和服务器支持基本的socket。 Ruby也具有程序库,提供更高级别的访问特定的应用程序级的网络协议,如FTP,HTTP等。 这篇教程介绍 ...
  • 作用于应用层,常用于客户访问被墙的网站,简称外网,常见的有vpn翻墙操作 Socket4 代理的是数据包的传递,只设计数据的加密、安全等,不设计到各种HTTP等协议区分 只支持...
  • 常见套接字类型

    千次阅读 2020-10-18 10:08:01
    DARPA Internet 地址(Internet套接字),这是最具代表性、最经典的、最常用的Socket,我们经常讲的套接字也就是Internet Scoket。根据数据传输方式,可以将Internet Socket分成几种,在使用socket()创建网络连接时...
  • 基于TCP/UDP的socket编程

    2019-08-25 15:10:19
    socket常见API //创建socket文件描述符(TCP/UDP, 客户端 + 服务器) int socket (int domain,int type,int protocol); //domain:AF_INET 代表是IPV4地址域 // AF_INET6 代表是IPV6地址域 //type:套接字...
  • 本文实例讲述了Python socket实现的简单...常见的连接类型 SOCK_STREAM:即TCP/IP。面向连接的套接字,通信之前必须建立可靠的连接。面向连接的套接字提供序列化的、可靠的和不重复的数据交付,而没有记录边界。 SOC
  • Python-socket的简单使用

    千次阅读 2017-06-19 13:45:22
    套接字(socket)是计算机网络数据结构,在任何类型的通信开始之前,网络应用程序必须创建套接字,可以将其比作电话的插孔,没有...常见的连接类型 SOCK_STREAM:即TCP/IP。面向连接的套接字,通信之前必须建立可靠的连接
  • Linux下基于TCP的预先派生子进程服务器的Socket编程,和家强,刘彦隆,描述了客户/服务器模型以及常见的服务器类型--基于TCP的并发服务器。在一个基于TCP回射服务器程序的基础上,结合实际Web应用中的多进
  • 用python实现一个socket echo程序 && tcp socket的几个关闭状态 ... 真实活 - 博客园ip地址和int类型的相互转换这是我最近看到一个面试题,还比较常见,于是用c, c++和python分别实现了 ip2int 和 in...
  • 套接字(socket)是计算机网络数据结构,在任何类型的通信开始之前,网络应用程序必须创建套接字,可以将其比作电话插孔,没有它将无法进行通信常用地址家族AF_UNIX:基于文件,实现同一主机不同进程之间通信AF_...
  • sys/types.h:数据类型定义sys/socket.h:提供socket函数及数据结构netinet/in.h:定义数据结构 sockaddr_inarpa/inet.h:提供...提供socket等待测试机制的函数、其他在网络程序中常见的头文件unistd.h:提供通用的文件
  • 使用socket套接字一些错误

    千次阅读 2018-05-19 09:09:06
    原文地址:使用socket函数一些常见错误1.socketSOCKET socket( int af, int type, int protocol );af:常为AF_INET 使用AF_ISO等其他地址族标识,而非AF_INET。 返回:-1。 错误:10047(使用了与请求协议不...
  • 网络插座Socket

    千次阅读 2014-03-13 09:14:16
    Unix提供了类似于文件操作的网络操作方式--Socket。...1、Socket类型,使用网络协议的类别,IPv4的类型为PF_INET 2、数据通信的类型,常见的数据报(SOCK_DGRAM)、数据流(SOCK_STREAM) 3、使用的网络协
  • Python Socket学习总结

    2020-07-24 11:21:28
    1、背景:Socket 应用最常见的类型就是 客户端/服务器 应用,服务器用来等待客户端的连接。 2、Socket API概览:主要用到的Socket API函数如下 socket() bind() listen() accept() connect() connect_ex() ...
  • socket编程中需要用到头文件

    千次阅读 2012-08-17 16:24:36
    sys/types.h:数据类型定义 sys/socket.h:提供socket函数及数据结构 netinet/in.h:定义数据结构sockaddr_in arpa/inet.h:提供IP地址转换函数 netdb.h:提供设置及获取域名的函数 ...其他在网络程序中常见的
  • sys/types.h:数据类型定义 sys/socket.h:提供socket函数及数据结构 netinet/in.h:定义数据结构sockaddr_in arpa/inet.h:提供IP地址转换函数 netdb.h:提供设置及获取域名函数 sys/ioctl.h:提供...
  • python3:udp-socket使用方法

    千次阅读 2019-02-19 22:40:30
    以下为socket定义的常见常量: socket 类型 描述 socket.AF_INET 用于服务器与服务器之间网络通信 socket.SOCK_STREAM 基于TCP流式socket通信 socket.SOCK_DGRAM 基于UDP数据报式socket通信...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 430
精华内容 172
关键字:

常见的socket类型