您现在的位置:首页 > 教案模板 > 正文

linux socket函数参数_linux socket函数详解_socket()函数(3)

2019-07-23 06:06 网络整理 教案网

0,否则返回-1,相应的标准错误写入错误符errno。返回写入数据存放到currwritesize变量中,所后将已经写入的部分加到步长变量中继续再判断是否全部写入len长度数据,该方法成功返回0,否则返回-1。按照handbook中关于freebsd引导过程的描述,fdisk 的 b参数是表示在硬盘的mbr中写入boot0(512字节),而bsdlabel中的b参数则表示在该主分区中写入该主分区的引导记录(并非主引导记录),即boot1(512字节,写在主分区的第一个扇区)和boot2(大概8k左右,写在分区的某个特定区域,并非文件系统中)。

其它的我就不一一介绍这几对I/O函数了,具体参见man文档或者baidu、Google,下面的例子中将使用到send/recv。

(9)关闭套接字(close)

函数:int close(int fd);

在服务器与客户端建立连接之后,会进行一些读写操作,完成了读写操作就要关闭相应的socket描述字,好比操作完打开的文件要调用fclose关闭打开的文件。

如果fd[1]的引用计数减少至0,即没有写端进程向管道中写,则fd[0]上的read操作将会读取到eof标志,返回0。套接字的文件表表项中的引用计数,直到父子进程的connfd都关闭了,到客户端的连接才会终止。因为套接字的文件表表项中的引用计数,直到父子进程的 connfd 都关闭了,到客户端的连接才会终止。

式并启动hc传输,直至完成传输.整个usbsubsystem只有该驱动直接操作硬件寄存器.该层也支持多种不同的host controller driver,比如uhci,ohci等.。可见okfn函数是必不可少的,当netfilter被启用时,它用于完成接收的数据包后的后续操作,如果不启用netfilter做数据包过滤,则所有的数据包都被接受,直接调用该函数做后续操作。它允许应用程序在执行一个可能不安全或敏感的操作前确定该操作是什么,以及是否是在允许执行该操作的安全上下文中执行它。

int shutdown(int sockfd,int how);

sockfd是需要关闭的socket的描述符。参数how允许为shutdown操作选择以下几种方式:

socket()函数_linux socket函数参数_linux socket函数详解

0-------不允许继续接收数据

1-------不允许继续发送数据

2-------不允许继续发送和接收数据

均为允许则调用close()。shutdown在操作成功时返回0,在出现错误时返回-1并置相应errno。

2、Socket中的TCP握手过程

(1)三次握手建立连接

我们知道tcp建立连接要进行“三次握手”,即交换三个分组。大致流程如下:

* 客户端向服务器发送一个SYN J,尝试连接服务器

* 服务器向客户端响应一个SYN K,并对SYN J进行确认ACK J+1,表示服务器可用,可以建立连接了

* 客户端再向服务器发一个确认ACK K+1,连接建立成功

这三次握手发生在socket的那几个函数中呢?请看下图:

http://images.cnblogs.com/cnblogs_com/skynet/201012/201012122157467258.png

图1 建立TCP连接的三次握手过程

从图中可以看出,当客户端调用connect时,触发了连接请求,向服务器发送了SYN J包,这时connect进入阻塞状态;服务器监听到连接请求,即收到SYN J包,调用accept函数接收请求,向客户端发送SYN K ,ACK J+1,这时accept进入阻塞状态;客户端收到服务器的SYN K ,ACK J+1之后,这时connect返回,并对SYN K进行确认;服务器收到ACK K+1时,accept返回,至此三次握手完毕,连接建立。

总结:客户端的connect在三次握手的第二次返回,而服务器端的accept在三次握手的第三次返回。

(2)四次握手释放连接

socket中,通过四次握手关闭TCP连接的过程,请看下图:

http://images.cnblogs.com/cnblogs_com/skynet/201012/201012122157487616.png

图2 关闭TCP连接的四次握手过程

图示过程如下:

* 某个应用进程首先调用close主动关闭连接,这时TCP发送一个FIN M;

* 另一端接收到FIN M之后,执行被动关闭,对这个FIN进行确认。它的接收也作为文件结束符传递给应用进程,因为FIN的接收意味着应用进程在相应的连接上再也接收不到额外数据;

* 一段时间之后,接收到文件结束符的应用进程调用close关闭它的socket。这导致它的TCP也发送一个FIN N;

* 接收到这个FIN的源发送端TCP对它进行确认。

这样每个方向上都有一个FIN和ACK。

产生四次握手的原因是由于tcp的半关闭造成的,既然一个tcp连接是全双工的,那么每个方向必须单独的进行关闭,原则就是当一方完成它的数据发送过程后就能发送一个fin来终止这个方向连接,当一端收到一个fin,他必须通知应用层另一端已经终止了哪个方向的数据传送。这个原则是当一方完成它的数据发送任务后就能发送一个fin来终止这个方向 的连接。这原则是当一方完成它的数据发送任务后就能发送一个fin来终止这个方向的连接。

图3 TCP关闭的四次握手过程

过程如下:

1)客户端A发送一个FIN,用来关闭客户A到服务器B的数据传送(报文段4)。

2)服务器B收到这个FIN,它发回一个ACK,确认序号为收到的序号加1(报文段5)。和SYN一样,一个FIN将占用一个序号。

3)服务器B关闭与客户端A的连接,发送一个FIN给客户端A(报文段6)。

4)客户端A发回ACK报文确认,并将确认序号设置为收到序号加1(报文段7)。

对TCP连接建立与关闭的一些疑问解释如下。

1)为什么建立连接协议是三次握手,而关闭连接却是四次握手呢?

但未必你所有的数据都全部发送给对方了,所以你可以未必会马上会关闭socket,也即你可能还需要发送一些数据给对方之后,再发送fin报文给对方来表示你同意现在可以关闭连接了,所以它这里的ack报文和fin报文多数情况下都是分开发送的.。但未必你所有的数据都全部发送给对方了,所以你可以未 必会马上会关闭socket,也即你可能还需要发送一些数据给对方之后,再发送fin报文给对方来表示你同意现在可以关闭连接了,所以它这里的ack报文 和fin报文多数情况下都是分开发送的。但未必你所有的数据都全部发送给对方了,所以你可以未必会马上会关闭socket,也即你可能还需要发送一些数据给对方之后,再发送fin报文给对方来表示你同意现在可以关闭连接了,所以它这里的ack报文和fin报文多数情况下都是分开发送的。

2)为什么TIME_WAIT状态还需要等2MSL后才能返回到CLOSED状态?

但是因为我们必须要假想网络是不可靠的,你无法保证你最后发送的ack报文会一定被对方收到,因此对方处于 last_ack状态下的socket可能会因为超时未收到ack报文,而重发fin报文,所以这个time_wait状态的作用就是用来重发可能丢失的 ack报文。而每当接收到tcp连接对端发来的ack确认了报文的成功发送时,写缓存就会减少,这是因为tcp的可靠性决定的,发出去报文后由于担心报文丢失而不会销毁它,可能会由重发定时器来重发报文。例如,客户机a向服务器b发送tcp连接请求,第一个连接请求报文在网络的某个节点长时间滞留,a超时后认为报文丢失,于是再重传一次连接请求,b收到后建立连接。

3、一个TCP回显服务器例子

下面是一个简单地回显服务器例子,使用TCP。它发送数据和接收回完全相同的数据。事实上,很多机器出于调试目的而运行“回显服务器”。

linux socket函数详解_socket()函数_linux socket函数参数

服务器端server.c:一直监听本机的8000号端口,如果收到连接请求,将接收请求并接收客户端发来的消息,然后向客户端返回消息。

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#define DEFAULT_PORT 8000
#define MAXPENDING 10  /* Max connection requests */
#define BUFFSIZE 1024
void Die(char *mess){ perror(mess); exit(1); }
void HandleClient(int sock){
    char buffer[BUFFSIZE];
    int received=-1;
    /* 接收消息 */
    if((received=recv(sock, buffer, BUFFSIZE, 0))<0){
        Die("Failed to receive initial bytes from client");
    }
    /* 发送字节,并在循环中检查是否有更多进来的数据 */
    while(received>0){
        /* 把接收的数据发送回去 */
        if(send(sock, buffer, received, 0)!=received){
            Die("Failed to send bytes to client");
        }
        /* 检查更多进来的数据 */
        if((received=recv(sock, buffer, BUFFSIZE, 0))<0){
            Die("Failed to receive additional bytes from client");
        }
    }
    close(sock);
}
int main(int argc, char** argv)  
{  
    int    socket_fd, connect_fd;
    struct sockaddr_in     servaddr, clientaddr;
    char    buff[4096];
    int     n;  
    //创建TCP Socket
    if( (socket_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ){  
        Die("Failed to create socket");
    }  
    //构造地址族结构
    memset(&servaddr, 0, sizeof(servaddr));  
    servaddr.sin_family = AF_INET;  
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);   //IP地址设置成INADDR_ANY,让系统自动获取本机的IP地址
    servaddr.sin_port = htons(DEFAULT_PORT);        //设置的端口为DEFAULT_PORT
  
    //将地址族绑定到套接字上
    if( bind(socket_fd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1){
        Die("Failed to bind the server socket");
    }  
    //监听是否有客户端连接
    if(listen(socket_fd, MAXPENDING) == -1){
        Die("Failed to listen on server socket");
    }  
    printf("======waiting for client's request======\n");
    /* 一直运行,直到取消 */
    while(1){
        //阻塞直到有客户端连接,不然多浪费CPU资源
        unsigned int clientlen=sizeof(clientaddr);
        if((connect_fd = accept(socket_fd, (struct sockaddr*)&clientaddr, &clientlen)) == -1){
            printf("accept socket error: %s(errno: %d)",strerror(errno),errno);
            continue;
        }
        fprintf(stdout, "Client connected: %s\n", inet_ntoa(clientaddr.sin_addr));
        //处理客户连接
        HandleClient(connect_fd);
    } 
}
TCP服务器基本流程:创建socket ---> 构造地址族结构 ---> 绑定地址族 ---> 监听客户端连接 ---> 在主循环中不断接受客户端连接 --> 接收并处理客户端消息。