Linux socket-编程入门(TCP server 端)

2023-05-16

原文完整内容

通常,socket编程总是Client/Server形式的,因为有了telnet,先不考虑client的程序,先写一个支持TCP协议的server端,然后用telnet作为client验证我们的程序。

TCP server端的基本流程 
        想象你自己是个小大佬,坐办公室(什么样的黑社会做办公室啊?可能是讨债公司吧^^)你很土,只有一个小弟帮你接电话(因为你自己的号码是不敢对外公开的)。一次通讯的流程大概应该是这样的:小弟那里的总机电话响了;小弟接起电话;对方说是你女朋友A妹;小弟转达说,“老大,你马子电话”;你说,接过来;小弟把电话接给你;你和你女朋友聊天半小时;挂电话。 
       分析一下整个过程中的元素。你小弟(listenSock),你需要他来监听(listen)电话;你自己(communicationSock),实际上打电话进行交流的是你自己;你的电话号码(servAddr),否则你女朋友怎么能找到你?你女朋友的电话号码(clntAddr),这个比喻有点牵强,因为事实上你接起电话,不需要知道对方的号码也可以通话(虽然事实上你应该是知道的,你不会取消了来电显示功能吧^^),但是,难道你是只接女朋友电话从来不打过去的牛人吗?这个过程中的行为(成员函数):你小弟接电话并转接给你(isAccept());你自己的通话(handleEcho())(这个行为确实比较土,只会乌鸦学舌的echo,呵呵)。

 

UNIX中的一切事物都是文件(everything in Unix is a file!)

  这是UNIX的基本理念之一,也是一句很好的概括。比如,很多UNIX老鸟会举出个例子来,“你看,/dev/hdc是个文件,它实际上也是我的光盘……”UNIX中的文件可以是:网络连接(network connection),输入输出(FIFO),管道(a pipe),终端(terminal),硬盘上的实际文件,或者其它任何东东。

  3个已经打开的fd,0:标准输入(STDIN_FILENO);1:标准输出(STDOUT_FILENO);2:标准错误(STDERR_FILENO)。(以上宏定义在<unistd.h>中)一个最简单的使用fd的例子,就是使用<unistd.h>中的函数:write(1, "Hello, World!\n", 20);,在标准输出上显示“Hello, World!”。

  file和fd并非一定是一一对应的。当一个file被多个程序调用的时候,会生成相互独立的fd。这个概念可以类比于C++中的引用(eg: int& rTmp = tmp;)。

socket与file descriptor
        文件是应用程序与系统(包括特定硬件设备)之间的桥梁,而文件描述符就是应用程序使用这个“桥梁”的接口。在需要的时候,应用程序会向系统申请一个文件,然后将文件的描述符返回供程序使用。返回socket的文件通常被创建在/tmp或者/usr/tmp中。我们实际上不用关心这些文件,仅仅能够利用返回的socket描述符就可以了。 
      

收件人:全体女生。
地址:<一种地址描述方式>

        事实上,在socket的通用address描述结构sockaddr中, 正是用这样的方式来进行地址描述的:

struct  sockaddr 

    unsigned 
short  sa_family; 
    
char  sa_data[ 14 ]; 
};

 

sa_family可以认为是socket address family的缩写,也可能被简写成AF(Address Family),他就好像我们例子中那个“收件人:全体女生”一样,虽然事实上有很多AF的种类,但是我们这个教程中只用得上大名鼎鼎的internet家族AF_INET。另外的14字节是用来描述地址的。这是一种通用结构,事实上,当我们指定sa_family=AF_INET之后,sa_data的形式也就被固定了下来:最前端的2字节用于记录16位的端口,紧接着的4字节用于记录32位的IP地址,最后的8字节清空为零。这就是我们实际在构造sockaddr时候用到的结构sockaddr_in(意指socket address internet):

struct  sockaddr_in 

    unsigned 
short  sin_family; 
    unsigned 
short  sin_port; 
    
struct  in_addr sin_addr; 
    
char  sin_zero[ 8 ]; 
};

 

我想,sin_的意思,就是socket (address) internet吧,只不过把address省略掉了。sin_addr被定义成了一个结构,这个结构实际上就是:

struct in_addr
{
    unsigned long s_addr;
};

in_addr显然是internet address了,s_addr是什么意思呢?说实话我没猜出值得肯定的答案,也许就是socket address的意思吧,尽管跟更广义的sockaddr结构意思有所重复了。哎,这些都是历史原因,也许我是没有精力去考究了。

原文介绍Linux的实现

 

看看源码实现:

ContractedBlock.gif ExpandedBlockStart.gif Code
//Filename: TcpServerClass.hpp 
#ifndef TCPSERVERCLASS_HPP_INCLUDED 
#define TCPSERVERCLASS_HPP_INCLUDED 
#include 
<unistd.h> 
#include 
<iostream> 
#include 
<sys/socket.h> 
#include 
<arpa/inet.h> 
class TcpServer 

private
int listenSock; 
int communicationSock; 
sockaddr_in servAddr; 
sockaddr_in clntAddr; 
public
    TcpServer(
int listen_port); 
bool isAccept(); 
void handleEcho(); 
}; 
#endif // TCPSERVERCLASS_HPP_INCLUDED

 

TcpServer::TcpServer(
int listen_port)  // 创建了listen socket(监听嵌套字) 

if ( (listenSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0 ) { 
throw "socket() failed"
    } 
    memset(
&servAddr, 0sizeof(servAddr)); 
    servAddr.sin_family 
= AF_INET; 
    servAddr.sin_addr.s_addr 
= htonl(INADDR_ANY); 
    servAddr.sin_port 
= htons(listen_port); 
if ( bind(listenSock, (sockaddr*)&servAddr, sizeof(servAddr)) < 0 ) { 
throw "bind() failed"
    } 
if ( listen(listenSock, 10< 0 ) { 
throw "listen() failed"
    } 
}

bool TcpServer::isAccept() 

    unsigned 
int clntAddrLen = sizeof(clntAddr); 
if ( (communicationSock = accept(listenSock, (sockaddr*)&clntAddr, &clntAddrLen)) < 0 ) { 
return false
    } 
else { 
        std::cout 
<< "Client(IP: " << inet_ntoa(clntAddr.sin_addr) << ") connected.\n"
return true
    } 
}

void TcpServer::handleEcho() 

const int BUFFERSIZE = 32
char buffer[BUFFERSIZE]; 
int recvMsgSize; 
bool goon = true
while ( goon == true ) { 
if ( (recvMsgSize = recv(communicationSock, buffer, BUFFERSIZE, 0)) < 0 ) { 
throw "recv() failed"
        } 
else if ( recvMsgSize == 0 ) { 
            goon 
= false
        } 
else { 
if ( send(communicationSock, buffer, recvMsgSize, 0!= recvMsgSize ) { 
throw "send() failed"
            } 
        } 
    } 
    close(communicationSock); 
}

//Filename: main.cpp 
//Tcp Server C++ style, single work 
#include <iostream> 
#include 
"TcpServerClass.hpp" 
int echo_server(int argc, char* argv[]); 
int main(int argc, char* argv[]) 

int mainRtn = 0
try { 
        mainRtn 
= echo_server(argc, argv); 
    } 
catch ( const char* s ) { 
        perror(s); 
        exit(EXIT_FAILURE); 
    } 
return mainRtn; 

int echo_server(int argc, char* argv[]) 

int port; 
if ( argc == 2 ) { 
        port 
= atoi(argv[1]); 
    } 
else { 
        port 
= 5000
    } 
    TcpServer myServ(port); 
while ( true ) { 
if ( myServ.isAccept() == true ) { 
            myServ.handleEcho(); 
        } 
    } 
return 0
}

 

        我们前面说到了网络分层:链路——网络——传输——应用。数据从应用程序里诞生,传送到互联网上每一层都会进行一次封装:
Data>>Application>>TCP/UDP>>IP>>OS(Driver, Kernel & Physical Address)
我们用socket重点描述的是协议,包括网络协议(IP)和传输协议(TCP/UDP)。
sockaddr重点描述的是地址,包括IP地址和TCP/UDP端口。

socket()函数


    我们从TcpServer::TcpServer()函数可以看到,socket和sockaddr的产生是可以相互独立的。socket()的函数原型是:

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

在Linux中的实现为:

#include 
< sys / socket.h >  
/*  Create a new socket of type TYPE in domain DOMAIN, using 
   protocol PROTOCOL.  If PROTOCOL is zero, one is chosen automatically. 
   Returns a file descriptor for the new socket, or -1 for errors.  
*/  
extern   int  socket ( int  __domain,  int  __type,  int  __protocol) __THROW;

 

第一个参数是协议簇(Linux里面叫作域,意思一样的),还是那句话,我们这篇教程用到的就仅仅是一个PF_INET(protocol family : internet),很多时候你会发现人们也经常在这里赋值为AF_INET,事实上,当前,AF_INET就是PF_INET的一个#define,但是,写成PF_INET从语义上会更加严谨。这也就是TCP/IP协议簇中的IP协议(Internet Protocol),网络层的协议。
后面两个参数定义传输层的协议。
第二个参数是传输层协议类型,我们教程里用到的宏,只有两个:SOCK_STREAM(数据流格式)和SOCK_DGRAM(数据报格式);(具体是什么我们以后讨论)
第三个参数是具体的传输层协议。当赋值为0的时候,系统会根据传输层协议类型自动匹配和选择。事实上,当前,匹配SOCK_STREAM的就是TCP协议;而匹配SOCK_DGRAM就是UDP协议。所以,我们指定了第二个参数,第三个就可以简单的设置为0。不过,为了严谨,我们最好还是把具体协议写出来,比如,我们的例子中的TCP协议的宏名称:IPPROTO_TCP。

 

数据的“地址”


        从数据封装的模型,我们可以看到数据是怎么从应用程序传递到互联网的。我们说过,数据的传送是通过socket进行的。但是socket只描述了协议类型。要让数据正确的传送到某个地方,必须添加那个地方的sockaddr地址;同样,要能接受网络上的数据,必须有自己的sockaddr地址。
        可见,在网络上传送的数据包,是socket和sockaddr共同“染指”的结果。他们共同封装和指定了一个数据包的网络协议(IP)和IP地址,传输协议(TCP/UDP)和端口号。

 

网络字节和本机字节的相互转换


        sockaddr结构中的IP地址(sin_addr.s_addr)和端口号(sin_port)将被封装到网络上传送的数据包中,所以,它的结构形式需要保证是网络字节形式。我们这里用到的函数是htons()和htonl(),这些缩写的意思是:

h: host,主机(本机) 
n: network,网络 
to: to转换 
s: 
short ,16位(2字节,常用于端口号) 
l: 
long , 32位(4字节,常用于IP地址) 
“反过来”的函数也是存在的ntohs()和ntohl()。

 

socket和sockaddr的创建是可以相互独立的


        首先通过socket()系统调用创建了listenSock,然后通过为结构体赋值的方法具体定义了服务器端的sockaddr。这里需要补充的是说明宏定义INADDR_ANY。这里的意思是使用本机所有可用的IP地址。当然,如果你机器绑定了多个IP地址,你也可以指定使用哪一个。

socket与本机sockaddr的绑定


        有时候绑定是系统的任务,特别是当你不需要知道自己的IP地址和所使用的端口号的时候。但是,我们现在是建立服务器,你必须告诉客户端你的连接信息:IP和Port。所以,我们需要指明IP和Port,然后进行绑定。

int bind(int socket, struct sockaddr* localAddress, unsigned int addressLength);

作为C++的程序员,也许你会觉得这个函数很不友好,它似乎更应该写成:

int bind_cpp_style(int socket, const sockaddr& localAddress);

我们需要通过函数原型指明两点:
1、我们仅仅使用sockaddr结构的数据,但并不会对原有的数据进行修改;
2、我们使用的是完整的结构体,而不仅仅是这个结构体的指针。(很显然光用指针是无法说明结构体大小的)
幸运的是,在Linux的实现中,这个函数已经被写为:

#include <sys/socket.h>
/* Give the socket FD the local address ADDR (which is LEN bytes long).  */
extern int bind (int __fd, __CONST_SOCKADDR_ARG __addr, socklen_t __len)
     __THROW;

看到亲切的const,我们就知道这个指针带入是没有“副作用”的。

 

监听:listen()


        stream流模型形式上是一种“持续性”的连接,这就是要求信息的流动是“可来可去”的。也就是说,stream流的socket除了绑定本机的sockaddr,还应该拥有对方sockaddr的信息。在listen()中,这“对方的sockaddr”就可以不是某一个特定的sockaddr。实际上,listen socket的目的是准备被动的接受来自“所有”sockaddr的请求。所以,listen()反而就不能指定某个特定的sockaddr。

int listen(int socket, int queueLimit);

其中第二个参数是等待队列的限制,一般设置在5-20。Linux中实现为:

#include <sys/socket.h>
/* Prepare to accept connections on socket FD.
   N connection requests will be queued before further requests are refused.
   Returns 0 on success, -1 for errors.  */
extern int listen (int __fd, int __n) __THROW;

完成了这一步,回到我们的例子,就像是让你小弟在电话机前做好了接电话的准备工作。需要再次强调的是,这些行为仅仅是改变了socket的状态,实际上我想强调的是,为什么这些函数不会造成block(阻塞)的原因。(block的概念以后再解释)

 

创建“通讯 ”嵌套字

 

        这里的“通讯”加上了引号,是因为实际上所有的socket都有通讯的功能,只是在我们的例子中,之前那个socket只负责listen,而这个socket负责接受信息并echo回去。
我们现看看这个函数:

bool  TcpServer::isAccept() 

    unsigned 
int  clntAddrLen  =   sizeof (clntAddr); 
if  ( (communicationSock  =  accept(listenSock, (sockaddr * ) & clntAddr,  & clntAddrLen))  <   0  ) { 
return   false
    } 
else  { 
        std::cout 
<<   " Client(IP:  "   <<  inet_ntoa(clntAddr.sin_addr)  <<   " ) connected.\n "
return   true
    } 
}

用accept()创建新的socket
        在我们的例子中,communicationSock实际上是用函数accept()创建的。

int accept(int socket, struct sockaddr* clientAddress, unsigned int* addressLength);

在Linux中的实现为:

/* Await a connection on socket FD.
   When a connection arrives, open a new socket to communicate with it,
   set *ADDR (which is *ADDR_LEN bytes long) to the address of the connecting
   peer and *ADDR_LEN to the address's actual length, and return the
   new socket's descriptor, or -1 for errors.
   This function is a cancellation point and therefore not marked with
   __THROW.  */
extern int accept (int __fd, __SOCKADDR_ARG __addr,
           socklen_t *__restrict __addr_len);

这个函数实际上起着构造socket作用的仅仅只有第一个参数(另外还有一个不在这个函数内表现出来的因素,后面会讨论到),后面两个指针都有副作用,在socket创建后,会将客户端sockaddr的数据以及结构体的大小传回。
        当程序调用accept()的时候,程序有可能就停下来等accept()的结果。这就是我们前一小节说到的block(阻塞)。这如同我们调用std::cin的时候系统会等待输入直到回车一样。accept()是一个有可能引起block的函数。请注意我说的是“有可能”,这是因为accept()的block与否实际上决定与第一个参数socket的属性。这个文件描述符如果是block的,accept()就block,否则就不block。默认情况下,socket的属性是“可读可写”,并且,是阻塞的。所以,我们不修改socket属性的时候,accept()是阻塞的。

 
accept()的另一面connect()


        accept()只是在server端被动的等待,它所响应的,是client端connect()函数:

int connect(int socket, struct sockaddr* foreignAddress, unsigned int addressLength);

虽然我们这里不打算详细说明这个client端的函数,但是我们可以看出来,这个函数与之前我们介绍的bind()有几分相似,特别在Linux的实现中:

/* Open a connection on socket FD to peer at ADDR (which LEN bytes long).
   For connectionless socket types, just set the default address to send to
   and the only address from which to accept transmissions.
   Return 0 on success, -1 for errors.
   This function is a cancellation point and therefore not marked with
   __THROW.  */
extern int connect (int __fd, __CONST_SOCKADDR_ARG __addr, socklen_t __len);

connect() 也使用了const的sockaddr,只不过是远程电脑上的而非bind()的本机。
        accept()在server端表面上是通过listen socket创建了新的socket,实际上,这种行为是在接受对方客户机程序中connect()函数的请求后发生的。综合起看,被创建的新socket实际上包含了listen socket的信息以及客户端connect()请求中所包含的信息——客户端的sockaddr地址。
新socket与sockaddr的关系
        accept()创建的新socket(我们例子中的communicationSock,这里我们简单用newSock来带指)首先包含了listen socket的信息,所以,newSock具有本机sockaddr的信息;其次,因为它响应于client端connect()函数的请求,所以,它还包含了clinet端sockaddr的信息。
        我们说过,stream流形式的TCP协议实际上是建立起一个“可来可去”的通道。用于listen的通道,远程机的目标地址是不确定的;但是newSock却是有指定的本机地址和远程机地址,所以,这个socket,才是我们真正用于TCP“通讯”的socket。


inet_ntoa()

#include <arpa/inet.h>
/* Convert Internet number in IN to ASCII representation.  The return value
   is a pointer to an internal array containing the string.  */
extern char *inet_ntoa (struct in_addr __in) __THROW;

       将IP地址,由in_addr结构转换为可读的ASCII形式的固定用法。

 

TCP通讯模型


        TCP的Server/Client模型类似这样: ServApp——ServSock——Internet——ClntSock——ClntApp
当然,我们这里的socket指的就是用于“通讯”的socket。TCP的server端至少有两个socket,一个用于监听,一个用于通讯;TCP的server端可以只有一个socket,这个socket同时“插”在server的两个socket上。当然,插上listen socket的目的只是为了创建communication socket,创建完备后,listen是可以关闭的。但是,如果这样,其他的client就无法再连接上server了。
        我们这个模型,是client的socket插在server的communication socket上的示意。这两个socket,都拥有完整的本地地址信息以及远程计算机地址信息,所以,这两个socket以及之间的网络实际上形成了一条形式上“封闭”的管道。数据包只要从一端进来,就能知道出去的目的地,反之亦然。这正是TCP协议,数据流形式抽象化以及实现。因为不再需要指明“出处”和“去向”,对这样的socket(实际上是S/C上的socket对)的操作,就如同对本地文件描述符的操作一样。但是,尽管我们可以使用read()和write(),但是,为了完美的控制,我们最好使用recv()和send()。


recv()和send()

int  send( int  socket,  const   void *  msg, unsigned  int  msgLength,  int  flags); 
int  recv( int  socket,  void *  rcvBuffer, unsigned  int  bufferLength,  int  flags);

在Linux中的实现为:

#include 
< sys / socket.h >  
/*  Send N bytes of BUF to socket FD.  Returns the number sent or -1. 
   This function is a cancellation point and therefore not marked with 
   __THROW.  
*/  
extern  ssize_t send ( int  __fd, __const  void   * __buf, size_t __n,  int  __flags); 
/*  Read N bytes into BUF from socket FD. 
   Returns the number read or -1 for errors. 
   This function is a cancellation point and therefore not marked with 
   __THROW.  
*/  
extern  ssize_t recv ( int  __fd,  void   * __buf, size_t __n,  int  __flags);

 

这两个函数的第一个参数是用于“通讯”的socket,第二个参数是发送或者接收数据的起始点指针,第三个参数是数据长度,第四个参数是控制符号(默认属性设置为0就可以了)。失败时候传回-1,否则传回实际发送或者接收数据的大小,返回0往往意味着连接断开了。


处理echo行为

void  TcpServer::handleEcho() 

const   int  BUFFERSIZE  =   32
char  buffer[BUFFERSIZE]; 
int  recvMsgSize; 
bool  goon  =   true
while  ( goon  ==   true  ) { 
if  ( (recvMsgSize  =  recv(communicationSock, buffer, BUFFERSIZE,  0 ))  <   0  ) { 
throw   " recv() failed "
        } 
else   if  ( recvMsgSize  ==   0  ) { 
            goon 
=   false
        } 
else  { 
if  ( send(communicationSock, buffer, recvMsgSize,  0 !=  recvMsgSize ) { 
throw   " send() failed "
            } 
        } 
    } 
    close(communicationSock); 
}

 

本小节最后要讲的函数是close(),它包含在<unistd.h>中

#include <unistd.h>
/* Close the file descriptor FD.
   This function is a cancellation point and therefore not marked with
   __THROW.  */
extern int close (int __fd);

这个函数用于关闭一个文件描述符,自然,也就可以用于关闭socket。

默认的监听端口是5000。我们可以通过
$telnet 127.0.0.1 5000
验证在本机运行的echo server程序。

转载于:https://www.cnblogs.com/dubingsky/archive/2009/07/11/1521441.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux socket-编程入门(TCP server 端) 的相关文章

  • cmake 常用命令

    1 使用日期 获取时间 string TIMESTAMP DATE TIME 34 y m d H M 34 获取日期 string TIMESTAMP DATE VERSION 34 m d 34 转载于 https www cnblog
  • QQ2008 msg.db,user.db读取

    Saturday November 27 2010 msg db读取 下载 user db读取 下载 转载于 https www cnblogs com ycdx2001 archive 2010 11 27 1889498 html
  • MongoDB——Mac环境搭建

    1 下载 官网地址 xff1a https www mongodb com 2 解压并配置 解压到 usr local 配置Path xff0c vim打开 bash profile添加export PATH 61 PATH usr loc
  • Django模型

    模型是你的数据的唯一的 权威的信息源 它包含你所储存数据的必要字段和行为 通常 xff0c 每个模型对应数据库中唯一的一张表 1 基础 每个模型都是django db models Model 的一个Python 子类 模型的每个属性都表示
  • 速度之王 — LZ4压缩算法(二)

    LZ4 Extremely Fast Compression algorithm 项目 xff1a http code google com p lz4 作者 xff1a Yann Collet 本文作者 xff1a zhangskd 64
  • dpkg

    dpkg error dpkg status database is locked by another process 无法获得锁 var lib apt lists lock open ubuntu升级错误或强制中断后容易爆出上面两个错
  • html5中加一个链接,HTML5教程—链接的添加方式_HTML5教程_链接添加_HTML5运用_课课家...

    HTML5的强大功能有很多 xff0c 在图像的修改中 xff0c 我们可见其强大 xff0c 然而其中有一个功能仍能可以运用于广告中的 xff0c 因为在广告主的需求中 xff0c 有很多情况下需要在动画中添加一些外部链接 而这份文档就在
  • Django--初始化

    1 Django介绍 它是一个WEB框架 Django 大而全tornado flask 小而精 2 Django安装 https www djangoproject com download 3 创建django程序 手动创建 file
  • ubuntu更换源后报错:W: GPG error: (转载)

    From xff1a http www njava com njava 626 html 更换163源后 xff0c 更新源时出现错误 apt get update W GPG error http extras ubuntu com pr
  • 魔咒词典

    题目描述 哈利波特在魔法学校的必修课之一就是学习魔咒 据说魔法世界有100000种不同的魔咒 xff0c 哈利很难全部记住 xff0c 但是为了对抗强敌 xff0c 他必须在危急时刻能够调用任何一个需要的魔咒 xff0c 所以他需要你的帮助
  • 禁用计算机上的所有鼠标加速,鼠标加速,小编告诉你鼠标加速怎么关

    我们在使用电脑的时候经常都会需要使用到鼠标 xff0c 所以对于鼠标的相关知识我们应该要了解的多一些 所以今天小编就来给你们讲讲鼠标加速要怎么关 xff0c 感兴趣的小伙伴们就接着看下去吧 小伙伴们 xff0c 小编今天来给你们说说关于电脑
  • 位运算之左移右移运算之详解

    先看如下一段左移右移的代码及其结果 xff1a 代码 include 34 stdio h 34 char leftshift char i int n if n lt 0 return 1 return i lt lt n
  • linux安装debian桌面,在Debian 10 Buster上安装Cinnamon桌面环境的方法

    在本文中 xff0c 我们将介绍在Debian 10 Buster 操作系统上安装Cinnamon桌面环境的方法 安装Debian 10 Buster之后 xff0c 可能需要将桌面环境更改为你喜欢的桌面环境 xff0c 默认安装搭载Gno
  • MongoDB——更新文档详解

    更新文档 span class token comment 语法 span db span class token punctuation span collection span class token punctuation span
  • CCF-CSP题解 201609-3 炉石传说

    模拟 注意随从的编号在 summon 和 attack 随从死亡时都可能改变 code include lt bits stdc 43 43 h gt using namespace std struct tNode int attack
  • USACO Network of Schools(学校网络) ---强连通分量

    描述 一些学校的校园网连接在一个计算机网络上 学校之间存在软件支援协议 每个学校都有它应支援的学校名单 xff08 学校a支援学校b xff0c 并不表示学校b一定支援学校a xff09 当某校获得一个新软件时 xff0c 无论是直接得到的
  • iOS开发-使用Storyboard进行界面跳转及传值

    前言 xff1a 苹果官方是推荐我们将所有的UI都使用Storyboard去搭建 xff0c Storyboard也是一个很成熟的工具了 使用Storyboard去搭建所有界面 xff0c 我们可以很迅捷地搭建出复杂的界面 xff0c 也就
  • iOS 8 自适应 Cell

    在使用 table view 的时侯经常会遇到这样的需求 xff1a table view 的 cell 中的内容是动态的 xff0c 导致在开发的时候不知道一个 cell 的高度具体是多少 xff0c 所以需要提供一个计算 cell 高度
  • Linux命令之切换用户

    一 从 user 用户切换到 root 用户 不管是用图形模式登录 Ubuntu xff0c 还是命令行模式登录 xff0c 我们会发现缺省的用户是 user xff0c 但是当我们需要执行一些具有 root 权限的操作 如修还系统文件 时
  • CNN对位移、尺度和旋转不变性的讨论

    CNN得益于全局共享权值和pool操作 xff0c 具有平移不变性 对于尺度不变性 xff0c 是没有或者说具有一定的不变性 xff08 尺度变化不大 xff09 xff0c 实验中小目标的检测是难点 xff0c 需要采用FPN或者其他的方

随机推荐

  • AtCoder Regular Contest 069 D 思维,模拟 E 模拟,贪心

    AtCoder Regular Contest 069 D Menagerie 题意 xff1a n只动物从1到n围成一个圈 xff0c 每只动物要么是羊要么是狼 每只动物会说出一个字母 xff0c 说 39 o 39 表示它两边动物种类相
  • git stash解决代码merge出错

    最近在使用git提交代码时 xff0c 遇到一个问题 xff0c 就是我修改了几个文件的代码 xff0c 然后又想把自己代码库里面的代码更新到最新版本 xff0c 然后不出所料 xff0c 代码冲突了 xff01 作为一个喜欢解决问题的程序
  • 锐捷网络交换机的配置命令集

    转自 xff1a http hi baidu com bjgbd 交换机 gt Enable 进入特权模式 Exit 返回上一级操作模式 End 返回到特权模式 write memory 或copy running config start
  • MongoDB——配置文件详解

    配置文件 MongoDB常用配置参数可以由config配置文件统一设置实现 Mongod的配置文件是mongod yaml Mongos的配置文件为mongos yaml MongoDB在2 6版本开始 xff0c 配置文件 采用YAML格
  • 计算机远程桌面自动关闭,windows10重启之后自动关闭远程桌面是怎么回事

    首先 xff0c 请大家打开Win 10系统桌面主页面 xff0c 在桌面页面中点击开始菜单 xff0c 在弹出的开始菜单中点击选择 设置 选项 xff0c 进入Win10系统设置页面 在Win10系统设置页面中 xff0c 点击选择 更新
  • openwrt issues

    1 ssh 没密码不让登录 解决方法 xff1a 使用串口登录上 passwd root 设置密码后 etc init d dropbear restart 发现option BannerFile 39 etc banner 39 被屏蔽掉
  • 图片样本集

    图像识别训练样本集 ImageNet ImageNet是一个计算机视觉系统识别项目 xff0c 是目前世界上图像识别最大的数据库 是美国斯坦福的计算机科学家李飞飞模拟人类的识别系统建立的 能够从图片识别物体 目前已经包含14197122张图
  • ubuntu16.10安装网易云音乐

    首先去官网 xff08 https music 163 com download xff09 下载安装包 xff1a netease cloud music 1 1 0 amd64 ubuntu deb 下载好以后 xff0c 执行安装命令
  • 安装 protoc 的各种坑

    首先下载 protoc 2 6 1 https github com google protobuf releases download v2 6 1 protobuf 2 6 1 tar gz https pkgs org downloa
  • 在mysql中创建视图需要使用什么语句_mysql如何创建视图?创建语句是什么?

    在mysql中 xff0c 可以使用 CREATE VIEW 语句来创建视图 xff0c 语法格式为 CREATE VIEW AS xff1b 其中 不能包含FROM子句中的子查询 xff0c 不能引用预处理语句参数 推荐教程 xff1a
  • 正则表达式学习

    1 正则表达式规则 1 1 普通字符 字母 数字 汉字 下划线 以及后边章节中没有特殊定义的标点符号 xff0c 都是 34 普通字符 34 表达式中的普通字符 xff0c 在匹配一个 字符串的时候 xff0c 匹配与之相同的一个字符 举例
  • 在Windows、Mac和 Linux系统中安装Python与 PyCharm

    工欲善其事 必先利其器 xff0c 本文介绍 Python环境的安装和 Python的集成开发环境 IDE PyCharn的安装 一 Python安装 Windows Mac和 Linux 当前主流的 Python版本为2x和3x 由于 P
  • Linux入门-安装篇(Debian 服务器版)

    本文描述了安装一个Linux xff08 Debian xff09 服务器的全过程 xff0c 并附加了一些必要的参考文章 xff0c 通篇以Debian为基础 xff0c 以建立一个服务器 xff08 非桌面环境 xff09 为主要目的
  • MongoDB——写出错机制

    写出错机制 MongoDB的写操作命令默认是没有任何出错返回值的 xff0c 这减少了写操作的等待时间 xff0c 也就是说 xff0c 不管有没有写入到磁盘或者有没有遇到错误 xff0c 它都不会报错 这在大量插 入类似GPS定位坐标信息
  • git入门1

    2016 04 23 19 25 02 参考 xff1a http blog jobbole com 78960 Git bash中运行git log之后怎样终止这个命令 xff0c 按q 再按回车键 清屏 clear 查询用户名 git
  • mysql查看库表的大小_MySQL查看数据库大小和表大小的方法

    MySQL查看数据库大小和表大小的方法 其实很简单 xff0c 就不多说了 xff0c 基本上看得懂代码都明白 查看MySQL数据库大小 SELECT table schema Database Name sum data length 4
  • 按键控制led灯亮灭c语言实验报告,嵌入式STM32学习笔记之按键查询方式控制led灯的亮灭...

    其实接触STM32已经快半年了 xff0c 端断续续的也学习了一下的STM32的各个模块的基本功能 xff0c 刚开始的时候也是看比人的写的代码 xff0c 看懂了然后再去修改 xff0c 能够在自己的平台上实现基本的功能 xff0c 也就
  • iOS 开发比较实用的框架总结(上)

    iOS开发比较实用的框架总结 下拉刷新类型的框架 EGOTableViewPullRefresh https github com enormego EGOTableViewPullRefresh 最早的下拉刷新控件 SVPullToRef
  • Linux使用远程X Server显示图形

    背景 通常我们不希望在服务器上安装图形界面 xff0c 但有时候有些程序需要图形界面 xff0c 比如安装oracle的时候 此时 xff0c 可以配置让Linux使用远程的X Server进行图形界面显示 首先要明确的是Linux X W
  • Linux socket-编程入门(TCP server 端)

    原文完整内容 通常 xff0c socket编程总是Client Server形式的 xff0c 因为有了telnet xff0c 先不考虑client的程序 xff0c 先写一个支持TCP协议的server端 xff0c 然后用telne