socket套接字

  1. 1套接字概念

Linux中的一种文件类型,伪文件,不占用存储空间,可进行IO操作,可间接看做文件描述符使用。

2套接字三种类型

套接字有三种类型:流式套接字(SOCK_STREAM),数据报套接字(SOCK_DGRAM) 及原始套接字。

  1. 流式套接字(SOCK_STREAM)
    流式的套接字可以提供可靠的、面向连接的通讯流。如果你通过流式套接字发送了顺 序的数据:“1”、“2”。那么数据到达远程时候的顺序也是“1”、“2”
  2. 数据报套接字(SOCK_DGRAM)
    数据报套接字定义了一种无连接的服务,数据通过相互独立的报文进行传输,是无序 的,并且不保证可靠,无差错。
  3. 原始套接字
    原始套接字允许对低层协议如 IP 或 ICMP 直接访问,主要用于新的网络协议实现的测试等。

 对流式套接字你所需要做的只是调用 send() 函数来发送数据。而对于数据报套接字,你需要自己加个信息头,然后调用 sendto() 函数把数据发送出去。

 3套接字的一些基本知识

3.1结构体

3.1.1struct sockaddr

这个结构用来存储套接字地址。

数据定义:

struct sockaddr {

unsigned short sa_family; /* 地址族协议, ipv4 */

char sa_data[14]; /* 14 bytes的协议地址 ,端口(2字节) + IP地址(4字节) + 填充(8字节)*/

};

sa_family 一般来说,都是 “AFINET”。

sa_data 包含了一些远程电脑的地址、端口和套接字的数目,它里面的数据是杂溶在一 切的。

为了处理 struct sockaddr, 程序员建立了另外一个相似的结构 struct sockaddr_in:
struct sockaddr_in (“in” 代表 “Internet”)

struct sockaddr_in {

        short int sin_family; /* 地址族协议: AF_INET */

        unsigned short int sin_port; /* 端口, 2字节-> 大端 */

        struct in_addr sin_addr; /* IP地址, 4字节 -> 大端 */

        unsigned char sin_zero[8]; /* 添0(和struct sockaddr一样大小)*/

};

这个结构提供了方便的手段来访问 socket address(struct sockaddr)结构中的每一个元素。注意 sin_zero[8] 是为了是两个结构在内存中具有相同的尺寸,使用 sockaddr_in 的时 候要把 sin_zero 全部设成零值(使用 bzero()或 memset()函数)。而且,有一点很重要,就是一个指向 struct sockaddr_in 的指针可以声明指向一个 sturct sockaddr 的结构。所以虽然 socket() 函数需要一个 structaddr * ,你也可以给他一个 sockaddr_in * 。注意在 struct sockaddr_in 中,sin_family 相当于在 struct sockaddr 中的 sa_family,需要设成“AF_INET”。 最后一定要保证 sin_port 和 sin_addr 必须是网络字节顺序

3.1.2struct in_addr

其定义如下:
/* 因特网地址 (a structure for historical reasons) */

struct in_addr

{

        unsigned long s_addr;

};

如果你声明了一个 “ ina ”作为一个 struct sockaddr_in 的结构,那么 “ina.sin_addr.s_addr”就是 4 个字节的 IP 地址(按网络字节顺序排放)。

3.2基本转换函数

3.2.1网络字节顺序

因为每一个机器内部对变量的字节存储顺序不同(有的系统是高位在前,底位在后, 而有的系统是底位在前,高位在后),而网络传输的数据大家是一定要统一顺序的。所以对与内部字节表示顺序和网络字节顺序不同的机器,就一定要对数据进行转换(比如 IP 地 址的表示,端口号的表示)。但是内部字节顺序和网络字节顺序相同的机器该怎么办呢? 是这样的:它们也要调用转换函数,但是真正转换还是不转换是由系统函数自己来决定的。

Little-Endian -> 主机字节序 (小端)

  • 数据的低位字节存储到内存的低地址位, 数据的高位字节存储到内存的高地址位
  • 我们使用的PC机,数据的存储默认使用的是小端

Big-Endian -> 网络字节序 (大端)

  • 数据的低位字节存储到内存的高地址位, 数据的高位字节存储到内存的低地址位
  • 套接字通信过程中操作的数据都是大端存储的,包括:接收/发送的数据、IP地址、端口。

3.2.2有关转化函数

  • htons()——“Host to Network Short”主机字节顺序转换为网络字节顺序(对无符号 短型进行操作 4 bytes)
  • htonl()——“Host to Network Long” 主机字节顺序转换为网络字节顺序(对无符 号长型进行操作 8 bytes)
  • ntohs()——“Network to Host Short “ 网络字节顺序转换为主机字节顺序(对无符 号短型进行操作 4 bytes)
  • ntohl()——“Network to Host Long “ 网络字节顺序转换为主机字节顺序(对无符 号长型进行操作 8 bytes)

在你把数据发送到 Internet 之前,一定要把它的字节顺序从主机字节顺序转换到网络字节顺序!

 sin_addr 和 sin_port 是从 IP 和 UDP 协议层取出来的数据,而在 IP 和 UDP 协议层,是直接和网络相关的,所以,它们必须使用网络字节顺序。然而, sin_family 域 只是内核用来判断 struct sockaddr_in 是存储的什么类型的数据,并且, sin_family 永远也 不会被发送到网络上,所以可以使用主机字节顺序来存储。

3.3.3IP地址转换

这一组函数只能处理ipv4的ip地址

// 点分十进制IP -> 大端整形
in_addr_t inet_addr (const char *cp);

// 大端整形 -> 点分十进制IP
char* inet_ntoa(struct in_addr in);

inet_addr():,它能够把一个用数字和点表 示 IP 地址的字符串转换成一个无符号长整型
ina.sin_addr.s_addr = inet_addr(“166.111.69.52”);

  • net_addr() 返回的地址已经是网络字节顺序了。

net_ntoa():的 IP 地址打印出来(按照 数字.数字.数 字.数字的格式)
printf(“%s”, inet_ntoa(ina.sin_addr));

  • inet_ntoa() 使用 struct in_addr 作为一个参数,不是一个长整型值。
  • inet_ntoa() 返回一个字符指针,它指向一个定义在函数 inet_ntoa() 中的 static 类型字符串。所 以每次你调用 inet_ntoa(),都会改变最后一次调用 inet_ntoa() 函数时得到的结果。

// 主机字节序的IP地址转换为网络字节序
// 主机字节序的IP地址是字符串, 网络字节序IP地址是整形
int inet_pton(int af, const char *src, void *dst);

参数:
        af: 地址族(IP地址的家族包括ipv4和ipv6)协议
                AF_INET: ipv4格式的ip地址
                AF_INET6: ipv6格式的ip地址
        src: 传入参数, 对应要转换的点分十进制的ip地址: 192.168.1.100
        dst: 传出参数, 函数调用完成, 转换得到的大端整形IP被写入到这块内存中
返回值:成功返回1,失败返回0或者-1

#include <arpa/inet.h>
// 将大端的整形数, 转换为小端的点分十进制的IP地址        
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);

参数:

        af: 地址族协议
                AF_INET: ipv4格式的ip地址
                AF_INET6: ipv6格式的ip地址
        src: 传入参数, 这个指针指向的内存中存储了大端的整形IP地址
        dst: 传出参数, 存储转换得到的小端的点分十进制的IP地址
        size: 修饰dst参数的, 标记dst指向的内存中最多可以存储多少个字节
返回值:

        成功: 指针指向第三个参数对应的内存地址, 通过返回值也可以直接取出转换得到的IP字符串
        失败: NULL

4.基本套接字调用

  1. socket()                        //创建一个套接字
  2. bind()                           // 将文件描述符和本地的IP与端口进行绑定
  3. connect()
  4. listen()
  5. accept()
  6. send()
  7. recv()
  8. sendto()
  9. recvfrom()
  10. close()
  11. shutdown()
  12. setsockopt()
  13. getsockopt()
  14. getpeername()
  15. getsockname()
  16. gethostbyname()
  17. gethostbyaddr()
  18. getprotobyname() 
  19. fcntl()

4.1socket函数

定义:

#includ<sys/type.s>

#include<sys/socket.h>

int socketint domain , int type , int protocol);

 参数:
        domain: 使用的地址族协议
                AF_INET: 使用IPv4格式的ip地址
                AF_INET6: 使用IPv4格式的ip地址
        type:告诉内核这个 socket 是什么类型
                SOCK_STREAM: 使用流式的传输协议
                SOCK_DGRAM: 使用报式(报文)的传输协议
        protocol: 一般写0即可, 使用默认的协议
                SOCK_STREAM: 流式传输默认使用的是tcp
                SOCK_DGRAM: 报式传输默认使用的udp
        返回值:
                成功: 可用于套接字通信的文件描述符
                失败: -1

函数的返回值只是简单的返回一个你以后可以使用的套接字描述符,通过这个文件描述符可以操作内核中的某一块内存,网络通信是基于这个文件描述符来完成的。

4.2bind函数

bind()函数可以帮助你指定一个套接字使用的端口。

定义

// 将文件描述符和本地的IP与端口进行绑定   

#includ<sys/type.s>

#include<sys/socket.h>

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

  • 参数:
    • sockfd: 监听的文件描述符, 通过socket()调用得到的返回值
    • addr: 传入参数, 要绑定的IP和端口信息需要初始化到这个结构体中,是一个指向 struct sockaddr 的指针,包含有关你的地址的信息:名称、 端口和 IP 地址。,IP和端口要转换为网络字节序
    • addrlen: 参数addr指向的内存大小, sizeof(struct sockaddr)
  • 返回值:成功返回0,失败返回-1

有时候你并不一定要调用 bind()来建立网络连接。比如你只是想连接到一个远程主机上面进行通讯,你并不在乎你究竟是用的自己机器上的哪个端口进行通讯(比如 Telnet),那么你可以简单的直接调用 connect()函数,connect()将自动寻找出本地机器上的一个未使用的端口,然后调用 bind()来将其 socket 绑定到那个端口上。

4.3connect函数

#includ<sys/type.s>

#include<sys/socket.h>

// 成功连接服务器之后, 客户端会自动随机绑定一个端口
// 服务器端调用accept()的函数, 第二个参数存储的就是客户端的IP和端口信息
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

  • 参数:
    • sockfd :套接字文件描述符,由 socket()函数返回的
    • serv_addr 是一个存储远程计算机的 IP 地址和端口信息的结构。
    • addrlen 应该是 sizeof(struct sockaddr)。
  • 返回值:连接成功返回0,连接失败返回-1

一定要检测 connect()的返回值:如果发生了错误(比如无法连接到远程主 机,或是远程主机的指定端口无法进行连接等)它将会返回错误值 –1 。 全局变量 errno 将会存储错误代码。

在面向连接的协议的程序中,服务器执行以下函数:

  • 调用 socket()函数创建一个套接字。 
  • 调用 bind()函数把自己绑定在一个地址上。
  • 调用 listen()函数侦听连接。
  • 调用 accept()函数接受所有引入的请求。
  • 调用 recv()函数获取引入的信息然后调用 send()回答。

4.4listen函数

listen()函数是等待别人连接,进行系统侦听请求的函数。当有人连接你的时候,你有两步需要做:通过 listen()函数等待连接请求,然后使用 accept()函数来处理。(accept()函数 在下面介绍)。

#include<sys/socket.h>

// 给监听的套接字设置监听
int listen(int sockfd, int backlog);

  • 参数:
    • sockfd: 文件描述符, 可以通过调用socket()得到,在监听之前必须要绑定 bind()
    • backlog: 同时能处理的最大连接要求,最大值为128
  • 返回值:函数调用成功返回0,调用失败返回 -1

如果你想在一个端口上接受外来的连接请求的话,那么函数的调用顺序为:

socket() ;

bind() ;

listen() ;

/* 在这里调用 accept()函数 */

4.5accept函数

// 等待并接受客户端的连接请求, 建立新的连接, 会得到一个新的文件描述符(通信的)        
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

  • 参数:
    • sockfd: 监听的文件描述符
    • addr: 传出参数, 里边存储了建立连接的客户端的地址信息
    • addrlen: 传入传出参数,用于存储addr指向的内存大小
  • 返回值:函数调用成功,得到一个文件描述符, 用于和建立连接的这个客户端通信,调用失败返回 -1

这个函数是一个阻塞函数,当没有新的客户端连接请求的时候,该函数阻塞;当检测到有新的客户端连接请求时,阻塞解除,新连接就建立了,得到的返回值也是一个文件描述符,基于这个文件描述符就可以和客户端通信了。

4.6send、recv、read、write函数

// 接收数据
ssize_t read(int sockfd, void *buf, size_t size);
ssize_t recv(int sockfd, void *buf, size_t size, int flags);

  • 参数:
    • sockfd: 用于通信的文件描述符, accept() 函数的返回值
    • buf: 指向一块有效内存, 用于存储接收的数据
    • size: 参数buf指向的内存的容量
    • flags: 特殊的属性, 一般不使用, 指定为 0
  • 返回值:
    • 大于0:实际接收的字节数
    • 等于0:对方断开了连接
    • -1:接收数据失败了

如果连接没有断开,接收端接收不到数据,接收数据的函数会阻塞等待数据到达,数据到达后函数解除阻塞,开始接收数据。当发送端断开连接,接收端无法接收到任何数据,但是这时候就不会阻塞了,函数直接返回0。

// 发送数据的函数
ssize_t write(int fd, const void *buf, size_t len);
ssize_t send(int fd, const void *buf, size_t len, int flags);

  • 参数:
    • fd: 通信的文件描述符, accept() 函数的返回值
    • buf: 传入参数, 要发送的字符串
    • len: 要发送的字符串的长度
    • flags: 特殊的属性, 一般不使用, 指定为 0
  • 返回值:
    • 大于0:实际发送的字节数,和参数len是相等的
    • -1:发送数据失败了

4.7sendto() 和 recvfrom() 函数

这两个函数是进行无连接的 UDP 通讯时使用的。

//发送数据

int sendto(int sockfd, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen);

  •  参数:
    • sockfd 是代表你与远程程序连接的套接字描述符。
    • msg 是一个指针,指向你想发送的信息的地址。
    • len 是你想发送信息的长度。
    • flags 发送标记。一般都设为 0
    • to 是一个指向 struct sockaddr 结构的指针,里面包含了远程主机的 IP 地址和端口 数据。
    • tolen 只是指出了 struct sockaddr 在内存中的大小 sizeof(struct sockaddr)。
  • 返回值:
    • 大于0:实际发送的字节数,和参数len是相等的
    • -1:发送数据失败了

//接收数据

int recvfrom(int sockfd, void *buf, int len, unsigned int flags ,struct sockaddr *from, int *fromlen);

  • 参数:
    • sockfd 是你要读取数据的套接字描述符。
    • buf 是一个指针,指向你能存储数据的内存缓存区域。 
    • len 是缓存区的最大尺寸。
    • flags 是 recv() 函数的一个标志,一般都为 0
    • from 是一个本地指针,指向一个 struct sockaddr 的结构(里面存有源 IP 地址和端 口数)。
    • fromlen 是一个指向一个 int 型数据的指针,它的大小应该是 sizeof(struct sockaddr).当函数返回的时候,formlen 指向的数据是 form 指向的 struct sockaddr 的实际大小.
  • 返回值:
    • 大于0:实际接收的字节数
    • 等于0:对方断开了连接
    • -1:接收数据失败了

4.8close()和 shutdown()函数

//关闭套接字描述符所表示的连接

close(sockfd);

套接字将不会在允许进行读操作和写操作。任何有关对套接字描述 符进行读和写的操作都会接收到一个错误。

如果你想对网络套接字的关闭进行进一步的操作的话,你可以使用函数 shutdown()。 它允许你进行单向的关闭操作,或是全部禁止掉。

int shutdown(int sockfd, int how);

  • 参数:
    • sockfd 是一个你所想关闭的套接字描述符。
    • how 可以取下面的值。
      • 0 表示不允许以后数据的接收操作;
      • 1 表示不允许以后数据的发送操作;
      • 2 表示和 close()一样,不允许以后的任何操作(包括接收,发送数据)
  • 返回值:
    • 等于0:操作成功
    • 等于-1:操作失败

4.9setsockopt() 和 getsockopt() 函数

Linux 所提供的 socket 库含有一个错误(bug)。此错误表现为你不能为一个套接字重新启用同一个端口号,即使在你正常关闭该套接字以后。

Linux 内核在一个绑定套接字的进程结束后从不把端口标记为未用。在大多数 Linux/UNIX 系统中,端口可以被一个进程重复使用,甚至可以被其它进程使用。

在 Linux 中绕开这个问题的办法是,当套接字已经打开但尚未有连接的时候用 setsockopt()系统调用在其上设定选项(options)。setsockopt()调用设置选项而 getsockopt() 从给定的套接字取得选项。

int getsockopt(int sockfd, int level, int name, char *value, int *optlen);

int setsockopt(int sockfd, int level, int name, char *value, int *optlen);

  • 参数:
    • sockfd 必须是一个已打开的套接字。
    • level是函数所使用的协议标准。
      • IPPROTO_TCP(TCP)
      • IPPROTO_IP (IP)
      • SOL_SOCKET(套接字标准)
    • name 选项在套接字说明书中(man page)有详细说明。
      • SO_BROADCAST 是否允许发送广播信息
      • SO_REUSEADDR 是否允许重复使用本地地址
      • SO_SNDBUF 获取发送缓冲区长度
      •  SO_RCVBUF 获取接收缓冲区长度
      • SO_RCVTIMEO 获取接收超时时间
      • SO_SNDTIMEO 获取发送超时时间
    • value 指向为 getsockopt()函数所获取的值,setsockopt()函数所设置的值的地址。
    • optlen 指针指向一个整数,该整数包含参数以字节计算的长度。
  • 返回值:
    • 等于0:成功
    • 等于1 :失败

4.10getpeername()函数

这个函数可以取得一个已经连接上的套接字的远程信息(比如 IP 地址和端口)。

int getpeername(int sockfd, struct sockaddr *addr, int *addrlen);

  • 参数:
    • sockfd 是你想取得远程信息的那个套接字描述符。
    • addr 是一个指向 struct sockaddr (或是 struct sockaddr_in)的指针。
    • addrlen 是一个指向 int 的指针,应该赋于 sizeof(struct sockaddr)的大小。
  • 返回值:
    • 等于0 :成功
    • 等于-1:错误

4.11gethostname()函数

gethostname()函数可以取得本地主机的信息。

它返回正在执行它的计算机的名字。返回的这个名字可以被 gethostbyname()函数使用, 由此可以得到本地主机的 IP 地址。

int gethostname(char *hostname, size_t size);

  • 参数:
    • hostname 是一个指向字符数组的指针,当函数返回的时候,它里面的数据就是本 地的主机的名字。
    • size 是 hostname 指向的数组的长度。
  • 返回值:
    • 等于0:成功
    • 等于-1;失败

5.DNS操作

5.1理解DNS

你应该知道 DNS 吧?DNS 是“Domain Name Service”(域名服务)的缩写。有了它, 你就可以通过一个可读性非常强的因特网名字得到这个名字所代表的 IP 地址。转换为 IP 地址后,你就可以使用标准的套接字函数(bind(),connect(),sendto(),或是其他任何需 要使用的函数)。

5.2和DNS有关的函数和结构

DNS 是怎样工作的呢?你可以使用 gethostbyname()函数。

struct hostent *gethostbyname(const char *name);

正如你所看见的,它返回了一个指向 struct hostent 的指针.Struct hostent 是这样定义的:

struct hostent

{

        char *h_name;                        //主机的正式名称

        char **h_aliases;                     //以NULL结尾的数组,里面存储了主机的备用名称。

        int h_addrtype;                        //是返回地址的类型,一般来说是“AF_INET”

        int h_length;                            //是地址的字节长度。

        char **h_addr_list;                  //t 是一个以 0 结尾的数组,存储了主机的网络地址。

};

#define h_addr h_addr_list[0]        //数组的第一个成员.

gethostbyname() 返回的指针指向结构 struct hostent ,如果发生错误,它将会返回 NULL (但是 errno 并不代表错误代码,h_errno 中存储的才识错误代码。

6.通信流程

6.1服务器端通信流程

  1. 创建用于监听的套接字, 这个套接字是一个文件描述符        socket()
  2. 将得到的监听的文件描述符和本地的IP 端口进行绑定          bind()
  3. 设置监听(成功之后开始监听, 监听的是客户端的连接)          listen()
  4. 等待并接受客户端的连接请求, 建立新的连接, 会得到一个新的文件描述符(通信的),没有新连接请求就阻塞                                                                      accept()
  5. 通信,读写操作默认都是阻塞的                                              read():write
  6. 断开连接, 关闭套接字                                                              close()
  • 在tcp的服务器端, 有两类文件描述符
    • 监听的文件描述符
      • 只需要有一个
      • 不负责和客户端通信, 负责检测客户端的连接请求, 检测到之后调用accept就可以建立新的连接
    • 通信的文件描述符
      • 负责和建立连接的客户端通信
      • 如果有N个客户端和服务器建立了新的连接, 通信的文件描述符就有N个,每个客户端和服务器都对应一个通信的文件描述符
// server.c
#include <iostream>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
int main(){
    //1.创建监听的套接字
    int lfd=socket(AF_INET,SOCK_DGRAM,0);
    if(lfd==-1){
        perror("socket创建失败");
        exit(0);
    }
    //2.将socket()返回值和本地的IP端口绑定到一起
    struct sockaddr_in addr;//保存本机信息
    addr.sin_family=AF_INET;
    addr.sin_port=htons(10000);      //大端端口
    // INADDR_ANY代表本机的所有IP, 假设有三个网卡就有三个IP地址
    // 这个宏可以代表任意一个IP地址
    // 这个宏一般用于本地的绑定操作
    addr.sin_addr.s_addr=INADDR_ANY;    // 这个宏的值为0 == 0.0.0.0
    //inet_pton(AF_INET,"192.168.18.20",&addr.sin_addr.s_addr);
    int ret=bind(lfd,(struct sockaddr*)&addr,sizeof(addr));
    if(ret==-1){
        perror("bind failed");
        exit(0);
    }
    //3.设置监听
    ret=listen(lfd,128);
    if(ret==-1){
        perror("listen failed");
        exit(0);
    }
    //4.阻塞并等待客户端的连接
    struct sockaddr_in cliaddr;//保存客户端信息
    socklen_t clilen=sizeof(cliaddr);
    int cfd=accept(lfd,(struct sockaddr*)&cliaddr,&clilen);
    if(cfd==-1){
        perror("accept failed");
        exit(0);
    }
    //打印客户端信息
    char ip[24]={0};
    std::cout<<"客户端的ip地址"<<inet_ntop(AF_INET,&cliaddr.sin_addr.s_addr,ip,sizeof(ip))
    <<", 端口号:"<<ntohs(cliaddr.sin_port)<<std::endl;
    //5.和客户端通信
    while(true){
        //接收数据
        char buf[1024];
        memset(buf,0,sizeof(buf));
        int len=read(cfd,&buf,sizeof(buf));
        if(len>0){
            std::cout<<"客户端说"<<buf<<std::endl;
            write(cfd,&buf,len);
        }else if(len==0){
             std::cout<<"对方断开连接"<<std::endl;
             break;
        }else{
             std::cout<<"read failed"<<std::endl;
             break;
        }

    }
    close(cfd);
    close(lfd);

    return 0;


}

6.2客户端通信流程

  1. 创建一个通信的套接字                                            //socket()
  2. 连接服务器, 需要知道服务器绑定的IP和端口          //connect()
  3. 通信                                                                         //read()/write()
  4. 断开连接, 关闭文件描述符(套接字)                         //close()
#include <iostream>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>


int main(){
    //1.创建通信的套接字
    int fd=socket(AF_INET, SOCK_STREAM, 0);
    if(fd==-1){
        perror("socket failed");
        exit(0);
    }
    //2.连接服务器
    struct sockaddr_in addr;
    addr.sin_family=AF_INET;
    addr.sin_port=htons(10000);
    inet_pton(AF_INET,"192.168.18.20",&addr.sin_addr.s_addr);

    int ret=connect(fd,(struct sockaddr*)&addr,sizeof(addr));
    if(ret == -1)
    {
        perror("connect failed");
        exit(0);
    }

    //3.和服务端通信
    int number=0;
    while(true){
        //发送数据
        char buf[1024]={0};
        std::cout<<"你好,服务器,这是我们第"<<number<<"次见面"<<std::endl;
        write(fd,buf,sizeof(buf));

        memset(buf,0,sizeof(buf));
        int len=read(fd,buf,sizeof(buf));
        if(len>0){
            std::cout<<"服务器回复:"<<buf<<std::endl;
        }else if(len==0){
             std::cout<<"服务器断开连接"<<std::endl;
        }else{
            perror("read failed");
            break;
        }
        sleep(1);
    }
    close(fd);

    return 0;
}

7.保留端口

7.1简介

大多数网络应用程序使用两个协议:传输控制协议(TCP)和用户数据包协议(UDP)。 他们都使用一个端口号以识别应用程序。端口号为主机上所运行之程序所用,这样就可以通过号码象名字一样来跟踪每个应用程序。端口号让操作系统更容易的知道有多少个应用 程序在使用系统,以及哪些服务有效。

端口号被从 1 开始分配。通常端口号超出 255 的部分被本地主机保留为私有用途。1 到 255 之间的号码被用于远程应用程序所请求的进程和网络服务。每个网络通信循环地进出主计算机的 TCP 应用层。它被两个所连接的号码唯一地识别。这两个号码合起来叫做套接字,组成套接字的这两个号码就是机器的 IP 地址TCP 软件所使用的端口号

7.2保留端口

系统留有 1024 个保留端口。这些端口是留给系统使用的,在系统中,只有具有Root 权利的人才可以使用 1024 以下的端口(包括 1024)。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值