网络编程套接字(一)

网络编程套接字(一)

一、基础知识

  • 1. 理解源IP地址和目的IP地址

  • 在IP数据包头部中,有两个IP地址,分别叫源IP地址和目的IP地址。这两个很好理解,见名知义。

  • 思考: 我们光有IP地址就可以完成通信了嘛? 想象一下发qq消息的例子, 有了IP地址能够把消息发送到对方的机器上,但是还需要有一个其他的标识来区分出, 这个数据要给哪个程序进行解析。

  • 2. 认识端口号

  • 端口号是传输层协议内容
    a. 端口号是一个2字节的16为整数
    b.端口号是用来标识一个进程,告诉操作系统,当前这个数据要交给那个进程去处理
    c.IP地址 + 端口号 能够唯一标识网络上的某一台主机的某一个进程
    d.一个端口号只能被一个进程占用

  • 3. 理解端口号和进程ID

  • 一个进程可以绑定多个端口号,但是一个端口号只能被一个进程绑定

  • 一个网络应用程序对应一个端口

  • 一个端口对应一至多个进程;

  • 一个进程对应一至多个线程

  • 4. 理解源端口号和目的端口号

  • 在传输层协议(TCP/UDP)的数据段中有两个端口号,分别叫源端口号和目的端口号

  • 源端口就是指本地端口,源端口就是本机程序用来发送数据的端口。

  • 目的端口就是远程端口,目的端口就是对方主机用哪个端口接收

  • 5. 初识TCP协议

  • 传输层协议

  • 有链接(相当于打电话)

  • 可靠传输(这里的可靠只是相对而言,“可靠“具体是指发送/接收数据的成功与否自己知道,并不是100%传输成功)

  • 面向字节流

  • 6. 初识UDP协议

  • 传输层协议

  • 无连接(如消息,短信)

  • 不可靠传输(“不可靠“具体是指发送/接收数据的成功与否自己不知道,并不是说传输数据一定是不可靠的)

  • 面向数据报

  • 7. 网络字节序(大端序

  • 我们已经知道,内存中的多字节数据相对于内存地址有大端和小端之分,
    磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分, 网络数据流同样有大端小端之分. 那么如何定义网络数据流的地址呢?

  • 发送主机通常将发送缓存区中的数据按照内存地址从低到高的顺序发出

  • 接收主机通常把从网络上接收到的字节依次保存在接受缓存区中,也就是按内存地址从低到高的顺序保存的

  • 因此,网络数据流的地址应该这样规定:先发出的数据是低地址,后发出的数据是高地址

  • TCP/IP协议规定,网络数据采用 大端字节序,即先低地址后高地址

  • 不论主机是大端机还是小端机,都会按造这个TCP/IP规定的网络字节序来发送/接受数据

  • 如果当前发送主机是小端机,就需要先将数据转为大端;否则直接忽略这一步

  • 8.网络字节序和主机字节序的转换

  • 目的:为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络字节序和主机字节序的转换

#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
  • h代编host(主机),n代表网络(network),l代表32位长整数,s表示16位短整数
  • 例如htonl表示将32位的长整数从主机字节序转换为网络字节序,例如将IP地址转换后准备发送
  • 如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回;
  • 如果主机是大端字节序,这些 函数不做转换,将参数原封不动地返回

二、sock编程接口

  • 1.socket函数—>创建socket
int socket(int domain, int type, int protocol);
  • 作用:创建一个socket文件描述符(相当于整个socket编程的操作句柄),也就是文件描述符表的下表,根据前面所说,可以用一个端口号关联一个进程,在这里就是拿端口号和一个socket绑定在一起
  • 参数:
参数作用
domaindomain 为地址族(Address Family),也就是 IP 地址类型,常用的有 AF_INETAF_INET6。AF_INET 表示 IPv4 地址,例如 127.0.0.1;AF_INET6 表示 IPv6 地址,例如 1030::C9B4:FF12:48AA:1A2B。
typetype 为数据传输方式/套接字类型,常用的有 SOCK_STREAM(流格式套接字/面向连接的套接字) 和 SOCK_DGRAM(数据报套接字/无连接的套接字)
protocolprotocol 表示传输协议,常用的有 IPPROTO_TCP 和 IPPTOTO_UDP,分别表示 TCP 传输协议和 UDP 传输协议。如果操作系统可以根据前面两个参数成功推演出协议类型,那么此处可以直接为0
  • 有了地址类型和数据传输方式,还不足以决定采用哪种协议吗?为什么还需要第三个参数呢?

  • 正如大家所想,一般情况下有了 domain 和 type 两个参数就可以创建套接字了,操作系统会自动推演出协议类型

  • 除非遇到这样的情况:有两种不同的协议支持同一种地址类型和数据传输类型。如果我们不指明使用哪种协议,操作系统是没办法自动推演的。

  • 例如

  • 如果使用 SOCK_STREAM 传输数据,那么满足这两个条件的协议只有 TCP,因此可以这样来调用 socket() 函数:

int tcp_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);  //IPPROTO_TCP表示TCP协议
  • 这种套接字称为 TCP 套接字。
  • 如果使用 SOCK_DGRAM 传输方式,那么满足这两个条件的协议只有 UDP,因此可以这样来调用 socket() 函数:
int udp_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);  //IPPROTO_UDP表示UDP协议
  • 这种套接字称为 UDP 套接字。
  • 上面两种情况都只有一种协议满足条件,可以将 protocol 的值设为 0,系统会自动推演出应该使用什么协议,如下所示:
int tcp_socket = socket(AF_INET, SOCK_STREAM, 0);  //创建TCP套接字
int udp_socket = socket(AF_INET, SOCK_DGRAM, 0);  //创建UDP套接字
  • 2.bind函数—>绑定端口信息
int bind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen);
  • 功能:bind为套接字sockfd指定本地地址my_addr
  • 参数
参数功能
sockfdsocket创建出的套接字,也就是文件描述符
my_addr该参数是一个指向结构sockaddr的指针,sockaddr封装了地址的类型、IP地址、端口号,用的时候需要把这些成员变量附上值,也就是绑定的过程,绑定本地主机的IP端口号信息
addrlen这个参数是第二个参数的大小,直接sizeof即可
  • 3.listen函数—>开始监听
int listen(int s, int backlog)
  • 功能:调用listen使其能够自动接收到来的连接并且为连接队列指定一个长度限制.
  • listen调用仅适用于 SOCK_STREAM 或者SOCK_SEQPACKET 类型的套接字(因为前面已经创建号套接字和绑定好端口号相关信息,但是别人是无法访问到我的
  • 通过listen就可以和我链接到,但是处于链接队列的所有请求都是处于倾听状态)
  • 参数:
参数功能
ssocket套接字
backlog指定完成连接队列的最大长度.如果一个连接请求到达时未完成连接队列已满,那么客户端将接收到错误
  • 4.accept函数—>接受请求
int accept(int s, struct sockaddr *addr, socklen_t *addrlen);
  • 功能:它从未完成连接队列中取出第一个连接请求,创建一个和参数s属性相同的连接套接字,并为这个套接字分配一个文件描述符,然后以这个描述符返回.新创建的描述符不再处于倾听状态,原套接字s不受此调用的影响.
  • 注意任意一个文件描述符标志(任何可以被fcntl以参数F_SETFL设置的值,比如非阻塞式或者异步状态)不会被 accept.
  • 参数
参数功能
s套接字
addr是一个指向结构sockaddr的指针,是输出型参数,这个结构体以连接实体地址填充.
addrlen是一个实时参数: 它的大小应该能够足以容纳参数 addr所指向的结构体;在函数返回时此参数将以字节数表示出返回地址的 实际长度.若 addr 使用NULL作为参数,addrlen将也被置为NULL
  • 5.connect函数—>建立链接
int connect(int sockfd, const struct sockaddr *addr,
                   socklen_t addrlen);
  • 功能:connect()系统调用将文件描述符sockfd引用的套接字连接到addr指定的地址
  • 参数
参数功能
sockfd套接字
addr该参数是一个指向结构sockaddr的指针,sockaddr封装了地址的类型、IP地址、端口号,用的时候需要把这些成员变量附上值
addrlensizeof(addr)
  • 6.recv函数
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
  • 功能:接收远端主机传来的数据,并把数据存到由参数buf 指向的内存空间
  • 参数
参数功能
sockfdsockfd为前面accept的返回值.即new_fd,也就是新的套接字
buf存储接收到的数据的缓冲区
一般为0
  • 7.recvfrom函数
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                        struct sockaddr *src_addr, socklen_t *addrlen);

  • 参数
参数作用
src_addr输出型参数,数据发送者地址,函数调用后该地址结构被填充
addrlensizeof(src_addr)
  • 8.send函数
int send(int s, const void *msg, size_t len, int flags);
  • 功能:发送数据给指定的远端主机
  • 参数
参数功能
ss为前面accept的返回值.即new_fd
msgmsg一般为常量字符串,也就是服务器返回给客户端的内容
len发送数据的长度
flags通常为0
  • 9.sendto函数
int sendto(int s, const void *msg, size_t len, int flags, 
						const struct sockaddr *to, socklen_t tolen);
  • 参数
参数作用
to指向接收数据的主机地址信息的结构体(sockaddr_in需类型转换)
tolensizeof(to)
相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页