基于UNIX的网络编程(理论篇:设计迭代服务器)

UNIX网络编程——并发服务器(多进程) 1、TCP客户/服务器(回射服务器)完整简单示例: 阅读详情

这里写图片描述
注意:read的返回值为0说明读的字节数,如果为EOF则为0.

之前腾讯一面的时候,面试官看见了我的最近一篇博客《TCP和UDP详解》感觉我的理论还行。但是,问我有没实际做过TCP/UDP的项目。结果我就只能说我在大一做过的一个没有搭在服务器上面的下载器,感觉有点丢人。于是,自己马上下来做了准备一个基于UNIX的网络编程的小项目。准备分为两个理论篇和一个实践篇。
现在马上进入我们的第一个理论篇:设计迭代服务器
在这篇博客中,你将看到以下内容:
这里写图片描述

好的,好戏开始:

UNIX I/O

一.UNIX的I/O系统是怎么回事?

计算机专业的都知道,我们的计算机都是基于冯诺伊曼的架构(当然还是哈佛架构总线分开)。那么,对于冯诺伊曼架构的计算机I/O就成了很重要的一环。在我们的UNIX系统的I/O是怎么样的呢?对于I/O(输入、输出),UNIX系统做了一个非常优美的抽象,UNIX系统将所有的I/O设备,正如网络、磁盘和终端,都抽象为了我们的文件。包括我们写程序中用到的prinf,scanf,sprintf,sscanf都是我们的文件。
针对于每一个程序,内核都会自动分配出三个文件:stdin,stdout,stderr(标准输入、标准输出、标准错误流),文件描述符分别为0,1,2.
这里提一下,UNIX是怎么进行文件区分的,下面直接来放一张UNIX系统的文件头结构体:
这里写图片描述
看起来有点多,我们挑我们常用的几个来说
1.mode_t st_mode. 说明文件的具体类型,是可读的,还是可写的,还是可执行的。
我们可以利用我们的宏指令来看文件的类型

宏指令描述
S_ISREG()这是一个普通文件吗?
S_ISDIR()这是一个目录文件吗?
S_ISSOCK()这是一个网络套接字吗?

其他的相关信息:st_atime、st_mtime、st_ctime可以用来做文件的mac或者hash来进行传输,来保证文件在发送的时候没有被修改,这又涉及到安全方面的知识了,这又是另一个故事了,对信息安全感兴趣的可以看我的这篇博文:http://blog.csdn.net/github_33873969/article/details/79008664
这里还是上一段代码如何查询和处理一个文件的st_mode位吧

#include<unistd.h>
#include<sys/stat.h>

int main(int argc, char **argv)
{
    struct stat stat;
    char *type, *readok;

    stat(argv[1],&stat);
    /* Determine file type */
    if(S_ISREG(stat.st_mode))
        type="regular";
    else if(S_ISDIR(stat.st_mode))
        type="directory";
    else
        type="other";
    /* Check read access */
    if((stat.st_mode & S_IRUSR))
        readok="yes";
    else
        readok="no";

    printf("type: %s, read: %s\n",type,readok);
    exit(0);
}

接下来,我还是想说一下针对每一个进程而言,文件是如何管理的,映射到操作系统内核文件又是怎样的?
还是先上图:
这里写图片描述
从头开始:
1.对于每一个进程而言,都会独立维护一个描述符表,这个描述符表就是我刚刚所说的对于进程而言,区分不同的文件而准备的(比如:stdin、stdout、stderr)
这里说明一个打开对应文件的函数吧。

#include <sys/types.h>
#inlcude <sys/stat.h>
#include <fcntl.h>
int open(char *filename,int flags,mode_t mode);   //返回:若成功则为新文件描述符,若出错为-1.

flags说明我们的进程将以什么样的方式访问这个文件,O_RDONLY:只读、O_WRONLY:只写、O_RDWR:可读可写。
接下来说明一下mode,这个参数,这个参数指定了我们对文件的访问权限,我们可以通过调用umask函数来设置。
还是直接上表吧,这个表中说明了各种权限。

宏指令描述
S_IRUSR使用者(拥有者)能够读这个文件
S_IWUSR使用者(拥有者)能够写这个文件
S_IXUSR使用者(拥有者)能够执行这个文件
S_IRGRP拥有者所在组的成员能够读这个文件
S_IWGRP拥有者所在组的成员能够写这个文件
S_IXGRP拥有者所在组的成员能够执行这个文件
S_IROTH其他人(任何人)能够读这个文件
S_IWOTH其他人(任何人)能够写这个文件
S_IXOTH其他人(任何人)能够执行这个文件

注意哦,这些文件权限,关系到我们的网络编程中的动态文件(可执行的文件)还是静态文件(html、jpg)图片的发送有关哦。
2.打开文件表
打开文件表位于内核中(所有进程共享),存储着所有的打开文件的信息。存储着文件位置和引用计数,关于这个引用计数可是又一定的技术故事的啦,引用计数技术在这里简单来说,就是有哪些进程打开了这个文件,引用了这个问题,一旦进程销毁后,引用计数就会减一。直到引用计数为0,则该文件被销毁。
Reference:https://www.zhihu.com/question/21539353
这种引用计数的方法也被引用在java的垃圾回收以及C++对象中。关于java的垃圾回收,我这里还是简单的聊一下吧:
2.1.引用计数。大家都知道java是一门面向对象的语言,针对于每一个对象都有的一个引用计数,谁引用了这个对象,这个对象的引用计数+1。引用结束就-1。直到对应的引用计数为0,说明该对象被销毁,这样的垃圾内存需要被回收。但是这样的回收方式,是基于局部的引用的情况,是局部最优解,可能产生无法回收的情况,如下面的例子:由于不方便转载,这个例子还是去这个网站上看比较好:https://www.zhihu.com/question/21539353
2.2.不可达图回收:如果把每一个对象看到一个结点,相互引用的关系看作是结点与结点相连关系,就可以构成一张图,那么那些图中达不到(不可达)的结点就是垃圾结点(垃圾对象),需要进行回收。所以,我们只要从GC开始对所有结点进行bfs,达不到的结点就是垃圾结点。
题外话说多了,回到我们的正题:
3.v-node表
这个才是我们在硬盘里面的真正文件,所有进程共享。所以会出现多个文件表指向同一个v-node(利用I/O重定向dup2来实现文件描述符指向同一个,这个之后会马上 讲),如果是父子进行(fork)出来的会共享同一个文件表,如下图所示:
这里写图片描述

二.如何编写健壮的I/O函数

这里举一个非常形象的例子来说明我们的普通的I/O函数,read,write有什么缺陷。假设我们要拷贝1个G的文件,我们如果用read和write一个个字节的拷贝,我们将会重复的陷入(trap into)系统调用,这会大量消耗我们无用的性能。我们基于这样的缺陷,设计了一个带有缓冲区的Rio_read和Rio_write,它的基本设计思路是调用rio_read要求读n个字节时,读缓冲区内有rp->rio_cnt个未读字节。如果缓冲区为空,那么会通过调用read再填满它。(原则其实就是多读数据进来)。
写也是同理。

这里还是直接上代码吧:

/*
 * rio_writen - robustly write n bytes (unbuffered)
 */
/* $begin rio_writen */
ssize_t rio_writen(int fd, void *usrbuf, size_t n) 
{
    size_t nleft = n;
    ssize_t nwritten;
    char *bufp = usrbuf;

    while (nleft > 0) {
    if ((nwritten = write(fd, bufp, nleft)) <= 0) {
        if (errno == EINTR)  /* Interrupted by sig handler return */
        nwritten = 0;    /* and call write() again */
        else
        return -1;       /* errno set by write() */
    }
    nleft -= nwritten;
    bufp += nwritten;
    }
    return n;
}
/* $end rio_writen */


/* 
 * rio_read - This is a wrapper for the Unix read() function that
 *    transfers min(n, rio_cnt) bytes from an internal buffer to a user
 *    buffer, where n is the number of bytes requested by the user and
 *    rio_cnt is the number of unread bytes in the internal buffer. On
 *    entry, rio_read() refills the internal buffer via a call to
 *    read() if the internal buffer is empty.
 */
/* $begin rio_read */
static ssize_t rio_read(rio_t *rp, char *usrbuf, size_t n)
{
    int cnt;

    while (rp->rio_cnt <= 0) {  /* Refill if buf is empty */
    rp->rio_cnt = read(rp->rio_fd, rp->rio_buf, 
               sizeof(rp->rio_buf));
    if (rp->rio_cnt < 0) {
        if (errno != EINTR) /* Interrupted by sig handler return */
        return -1;
    }
    else if (rp->rio_cnt == 0)  /* EOF */
        return 0;
    else 
        rp->rio_bufptr = rp->rio_buf; /* Reset buffer ptr */
    }

    /* Copy min(n, rp->rio_cnt) bytes from internal buf to user buf */
    cnt = n;          
    if (rp->rio_cnt < n)   
    cnt = rp->rio_cnt;
    memcpy(usrbuf, rp->rio_bufptr, cnt);
    rp->rio_bufptr += cnt;
    rp->rio_cnt -= cnt;
    return cnt;
}
/* $end rio_read */

/*
 * rio_readinitb - Associate a descriptor with a read buffer and reset buffer
 */
/* $begin rio_readinitb */
void rio_readinitb(rio_t *rp, int fd) 
{
    rp->rio_fd = fd;  
    rp->rio_cnt = 0;  
    rp->rio_bufptr = rp->rio_buf;
}
/* $end rio_readinitb */

/*
 * rio_readnb - Robustly read n bytes (buffered)
 */
/* $begin rio_readnb */
ssize_t rio_readnb(rio_t *rp, void *usrbuf, size_t n) 
{
    size_t nleft = n;
    ssize_t nread;
    char *bufp = usrbuf;

    while (nleft > 0) {
    if ((nread = rio_read(rp, bufp, nleft)) < 0) 
            return -1;          /* errno set by read() */ 
    else if (nread == 0)
        break;              /* EOF */
    nleft -= nread;
    bufp += nread;
    }
    return (n - nleft);         /* return >= 0 */
}
/* $end rio_readnb */

/* 
 * rio_readlineb - robustly read a text line (buffered)
 */
/* $begin rio_readlineb */
ssize_t rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen) 
{
    int n, rc;
    char c, *bufp = usrbuf;

    for (n = 1; n < maxlen; n++) { 
        if ((rc = rio_read(rp, &c, 1)) == 1) {
        *bufp++ = c;
        if (c == '\n') {
                n++;
            break;
            }
    } else if (rc == 0) {
        if (n == 1)
        return 0; /* EOF, no data read */
        else
        break;    /* EOF, some data was read */
    } else
        return -1;    /* Error */
    }
    *bufp = 0;
    return n-1;
}

三.I/O重定向

这个就更简单了,有了我们上面懂得UNIX系统中对文件中的抽象。我们利用下面的函数

#include <unitstd.h>
int dup2(int oldfd, int newfd);   //返回:若成功则为非负的描述符,若出错则为-1.

假设我们调用dup2(3,1),即使让3也指向和文件表1中的同一个v-node,一是标准输出,那么3的输出会直接被定向到标准输出。同理,重定向到网络套接字描述符,重定向到标准输入,那么文件3的输出就变成了网络套接字的输入或者标准输入,这就是重定向I/O以及“一切都是文件”的魅力,刚刚的过程我用下面的图表示出来:
这里写图片描述

网络编程

这部分我将尽量简化网络例如TCP/IP协议以及HTTP的理论部分,着眼于代码以及相关函数部分

一.客户端与服务端的基本模式

这里写图片描述
这里我将说明一个最简单的模型,并且我们的第一个服务器也是基于这个模式。
1.客户端发送情况。2.服务器去请求资源。((1).可能是磁盘中的静态资源,html/css/jpg文件。(2)也可能是动态文件(需要fork出一个进程去execve该程序)).3.之后就将服务器的相关资源发送。4.客户端进行响应。中间的TCP的可靠传输的实现,HTTPS和SSL的加密等内容,可以看我的其他博客:http://blog.csdn.net/github_33873969/article/details/79422188
这里就不细说了。

二.UNIX有哪些函数与我们的客户端与服务端相关

说了这么多,终于开始调函数了。受首先是我们的客户端,可以想象我们发送一个消息,需要一个对应服务器主机的IP地址,了解到需要发送服从的协议。代码如下:

#include <sys/types.h>
#include <sys/socket.h>

//domain表示使用的网络(常用的是AF_INET),type常为SOCK_STREAM表示因特网连接的一个结点,
int socket(int domain, int type, int protocol);

现在只是连接起一个套接字,但是,还不能用于读写。

#include <sys/types.h>
#include <sys/socket.h>
int connect(int socketfd,struct sockaddr *serv_addr,int addslen)

emmmm,这个参数的含义我觉得直接看参数名都能看懂,值得注意的是,当connect在执行过程中,函数处于阻塞状态,意思就是说其他套接字不能进行连接状态,直到连接上或者返回报错信息,IP在sockaddr这里结构体中。
我们直接将socket函数和connect函数封装成一个函数。
如下所示:

/* $begin open_clientfd */
int open_clientfd(char *hostname, int port) 
{
    int clientfd;
    struct hostent *hp;
    struct sockaddr_in serveraddr;

    if ((clientfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    return -1; /* Check errno for cause of error */

    /* Fill in the server's IP address and port */
    if ((hp = gethostbyname(hostname)) == NULL)
    return -2; /* Check h_errno for cause of error */
    bzero((char *) &serveraddr, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    bcopy((char *)hp->h_addr_list[0], 
      (char *)&serveraddr.sin_addr.s_addr, hp->h_length);
    serveraddr.sin_port = htons(port);

    /* Establish a connection with the server */
    if (connect(clientfd, (SA *) &serveraddr, sizeof(serveraddr)) < 0)
    return -1;
    return clientfd;
}

对于服务器

1.第一个仍然是我们的socket函数,这个不多说了,创建一个套接字描述符。
2.bind函数。

#include <sys/socket.h>
int bind(int socketfd,struct sockaddr *my_addr,int addrlen);

即让操作系统内核将对应的socketfd绑定到我们的my_addr结构题中。
3.listen函数

#include <sys/socket.h>
int listen(int sockfd,int backlog);

这个函数是想主动的套接字转化为监听套接字(服务器专用),这个函数可是有的说呀,这个函数足以和我们的TCP握手协议连接起来,还是先图:
Reference:https://www.cnblogs.com/chris-cp/p/4022262.html
这里写图片描述
我们都知道TCP有着三次握手和四次挥手协议。但这些协议老实说都在操作系统底层实现了。但是,其中的函数还是会影响到我们的listen函数,我们的listen函数维护着两个队列:
3.1.已完成连接队列,负责排队已经完成三次握手的客户端socket。即是已经是ESTABLISHED状态的连接。
3.2.未完成连接队列:这个更好理解了,就是还处于三次握手的连接(连接处于SYN_RCVD状态)。
这里要注意,listen函数负责三次握手的连接,但是accept函数却不负责三次握手的连接。这个还要注意一点,accept函数只首发处于已连接状态的套接字,所以当以完成连接队列为空的时候,accept会处于阻塞状态。知道已完成连接队列中有已经完成握手的套接字存在。
然后已完成连接队列+未完成连接队列=我们的参数backlog。
4.accept函数

#include <sys/socket.h>
int accept(int listenfd,struct sockaddr *addr,int *addrlen)

这时候,我们输入的listenfd监听描述符就返回出了连接描述符,记住listenfd监听描述符针对的一个服务器,而通过accept创建的连接描述符是针对服务器针对一个客户端的连接。具体的连接过程如下图所示:
这里写图片描述
图已经很清楚哒。
同时我们也将socket和listen函数封装成一个函数

/* $begin open_listenfd */
int open_listenfd(int port) 
{
    int listenfd, optval=1;
    struct sockaddr_in serveraddr;

    /* Create a socket descriptor */
    if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    return -1;

    /* Eliminates "Address already in use" error from bind */
    if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, 
           (const void *)&optval , sizeof(int)) < 0)
    return -1;

    /* Listenfd will be an endpoint for all requests to port
       on any IP address for this host */
    bzero((char *) &serveraddr, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET; 
    serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); 
    serveraddr.sin_port = htons((unsigned short)port); 
    if (bind(listenfd, (SA *)&serveraddr, sizeof(serveraddr)) < 0)
    return -1;

    /* Make it a listening socket ready to accept connection requests */
    if (listen(listenfd, LISTENQ) < 0)
    return -1;
    return listenfd;
}

设计我们的第一个迭代服务器

代码有点多,我就直接下地址啦:https://github.com/HBKO/tiny-server

unix网络编程----迭代式echo服务器与客户端(只支持一个客户端通信) <br />    分别用gcc编译两个代码为可执行程序client和server之后,在一个终端里运行./server启动服务器,在另一个终端运行./client 127.0.0.1后,就可以在client终端里面输入任意字符然后回车,输入的字符会发送到服务器,然后被服务器返回来,在client终端里显示,起到"echo"的效果。此代码可以作为网络编程基础设置的参考。<br />    参考资料:<<UNIX Network Programming Volumn1:The Sockets Networki 阅读详情

相关推荐

UNIX 网络编程 30 种服务器模型对比:从迭代到预线程,性能与复杂度分析

本文深入对比了UNIX网络编程中的30种服务器模型,从迭代式到预线程架构,详细分析了性能与复杂度。通过基准测试数据和技术解析,帮助开发者根据实际场景选择最优架构,提升网络服务的吞吐量和响应时间,特别适合需要高性能网络编程的开发者参考。

328

Unix项目编写Web服务器

UnixC项目

weixin_41560737的博客 1016

Linux中stat函数和stat命令使用详解

stat函数和stat命令 linux文件里的【inode = index node】解释:要理解inode必须了解磁盘和【目录项】,inode实际是连接【目录项】和磁盘的中间物质。 图里的大圈代表硬件的磁盘,里面的小圈代表某个文件存储在磁盘上了。 【inode = index node】的node(承载node信息的结构体是:stat,stat的定义在后面 )里面有: 文件大小 文件的最后修改时间 文件的所属用户 文件的权限 硬链接计数(ls -l 显示出来的数字) 块位置:指定文件存储在磁盘的具体位置。 下图中的hello是个普通文件,hello.hard是hell

Unix/Linux编程:多协议服务器(TCP/UDP)-----DAYTIME

这里介绍了如果构造一个单线程服务器,使用异步IO以便在多个连接上提供表面上的并发性。本文将扩展这个概念,展示一个单线程服务器如何可以适用于多个传输协议。 多协议服务器的动机 在大多数情况下,一个给定的服务器处理针对一个服务的请求,这些请求是通过一个传输协议发来的。比如,一个提供DAYTIME服务的计算机系统往往是要运行两个服务器,一个服务器处理来自UDP的请求,而另一个服务器则处理来自TCP的请求。 为每个协议使用一组单独服务器的主要优点是便于控制:系统管理员可以通过控制系统所运行的服务器来很容易的控制.

OceanStar的博客 951

Unix网络编程小结——简单的服务器实现

1.前言 要实现一个简易的服务器,需要几个步骤来与客户端建立连接,并接收客户端的数据进行处理。 上图是实现TCP客户/服务器程序需要使用到的基本套接字函数。 本以实现服务器端为主。 2.sokcet() 首先要调用socket函数得到一个监听套接字文件描述符作为起始,用于后续使用bind()和listen() 使用时需要包含头文件“#include <sys/socket.h>”。 server.cpp #include <sys/socket.h> int main(int a

weixin_44431550的博客 1276

unix网络编程各种TCP客户-服务器程序设计实例附环境搭建和编译方法(一)

前言:在以前的UNIX网络编程系列中我们介绍了有关网络编程理论知识,本文将在其基础上总结《UNIX网络编程》上的9种不同的TCP客户程序设计例子,希望能给大家带来帮助。本文只介绍实例,相关的理论知识请参考《unix网络编程》或者本博文的http://blog.csdn.net/ts173383201/article/category/1213821系列; 在出代码之前,可能有很多同学遇到《UN...

weixin_30249203的博客 266

操作系统介绍1---UNIX系统的主要功能

然而,与任何操作系统一样,重要的是让 Unix 系统保持最新的安全补丁,并遵循系统强化和安全的最佳实践。总的来说,Rsync 是一个强大而高效的实用程序,广泛用于 Unix 和类 Unix 系统上的文件传输和同步。登月舱:控制登陆月球的航天器登月舱的软件是用汇编语言编写的,运行在使用 Unix 操作系统的 AGC(阿波罗制导计算机)的特殊修改版本上。然而,重要的是要注意 Linux 与 Unix 相比有其自身的一系列优势,例如更灵活和可定制的内核、更广泛的硬件支持以及更大的开源社区。

u010839117的博客 3662

UNIX网络编程】客户/服务器程序设计范式

前言: 这本UNIX网络编程到这里也就结束了,回想一下,确实自己后一段的学习有放松了,昨天和同学交流,他说他每天十二点睡,早上4.30起床,学习到8.30再去实验室做老师的活,想来我这一部书第一遍就学习了两个月,是在是自愧不如,他是专硕,今年九月就要找工作了,相信他的前途一定是一片光明的。我自己也绝对不能再犹豫,一定做出成绩来! 1.基本设计模式分类: a.迭代服务器(iterative s

yw8355507的专栏 703

unix网络编程

google20的博客 670

UNIX 网络编程-卷1》阅读笔记31: 全书总结 - 从入门到设计哲学

从阻塞到非阻塞,从串行到并发是性能优化的演进主线。“协议无关”是编写健壮网络程序的原则,使用和套接字结构封装函数(如SA家族)。理解内核行为(如三次握手队列、TIME_WAIT、缓冲区)是解决疑难杂症的根本。五种I/O模型和并发服务器模式的对比是本书的压舱石,反复揣摩各自取舍。源码是金:Stevens的unp.hwrap函数包装了错误处理,让代码清晰简洁,实际开发中可借鉴这种风格。本文为个人学习笔记,仅用于知识分享。如有错误,欢迎指正。❤️ 如果觉得有用,请给个赞支持一下作者!

1320

UNIX网络编程(1):fork创建进程、实现并发服务器及其问题

已有的事,后必再有;已行的事,后必再行。 UNIX网络编程,一往无前!冲啊! 进入正题… 首先来了解一下正常的迭代服务器服务器正常处理一个客户,如果多个客户连接差不多同时达到,系统内核最大数目的限制下把他们排入队列(相当于一个管道,有先后次序),然后每个返回一个accept函数。 // 迭代服务器,对于每个客户他都迭代执行一次 while(1) {} || for ( ; ; ) 如果程序运行速度需要较多时间服务每个客户,那么我们必须以某种方式重叠对各个用户的服务,即并发服务器。 并发服务器开始辽!

遇事不决,问春风 582

unix网络编程_Unix 网络编程

传输层部分知识点TIME_WAIT状态MSL: maximum segment lifetime任何TCP的实现都需要为MSL选择一个合适的值, RFC的建议值是2分钟。分组可能出现迷途,若迷途分组在MSL中找到路, 造成重复,TCP必须修复TIME_WAIT存在的理由:可靠的实现全双工的连接和终止考虑最终ACK丢失的情况,允许老的重复分组在网络中消逝TCP的化生身现象, 因为TIME_WAIT的...

weixin_39626613的博客 271

txiki.js网络编程实战:TCP、UDP和Unix套接字完整指南

🚀 想要在JavaScript中实现高性能网络通信吗?txiki.js这个轻量级JavaScript运行时为你提供了完整的网络编程能力!作为一个小巧但功能强大的JavaScript运行时,txiki.js支持TCP、UDP和Unix套接字等多种网络协议,让开发者能够轻松构建各种网络应用。 ## 🔥 为什么选择txiki.js进行网络编程? txiki.js是一个极简的JavaScript运

gitblog_00558的博客 1103

Unix网络编程基础概念

一般认为,Web服务器程序是一个长时间运行的程序(即所谓的守护程序,daemon),它只在响应来自网络的请求时才发送网络消息。 一般网络应用,总是由Client发起Request,这样能够简化协议和程序本身。较为复杂的网络应用,由服务器向客户发起请求消息,这称为异步回调(asy

huaye502的专栏 1733

UNIX网络编程卷1 服务器程序设计范式0 迭代服务器

1.迭代 TCP 服务器总是在完全处理某个客户的请求后才转向下一个客户。 2.从进程控制角度看迭代服务器是最快的,因为它不执行进程控制。 /* include serv00 */ #include "unp.h" int main(int argc, char **argv) { int listenfd, connfd; void sig_int(int), web

1037

UNIX 网络编程-卷1》阅读笔记05: 基本 TCP 套接字编程

第四章是UNP全书的真正编程起点。前一章学习了套接字地址结构和字节序转换这些基础构件,本章将这些概念落到实处——开始编写真正的TCP客户/服务器程序。本章的使命是完整介绍编写一个TCP客户/服务器程序所需要的全部基本套接字函数,包括socket、connect、bind、listen、accept、close和fork等。学完本章,你将能够独立编写一个简单的TCP回射(echo)客户/服务器程序。

3045
上一篇: 1123. Is It a Complete AVL Tree(AVL树旋转、判断完全二叉树)
下一篇: C++对象内存模型详解(基于GDB)
写代码的柯长
博客等级 码龄11年 84粉丝 52原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值