TCP/IP 网络程序设计——基础篇

预备知识

Socket

Socket 简介

  • 是一个编程接口
  • 是一种特殊的文件描述符(everything in Unix is a file)
  • 并不仅限于 TCP/IP 协议
  • 面向连接(Transmission Control Protocol - TCP/IP)
  • 无连接(User Datagram Protocol -UDP 和 Inter-network Packet Exchange - IP)

Socket 类型

  • 流式套接字(SOCK_STREAM
    • 提供了一个面向连接、可靠的数据传输服务,数据无差错、无重复的发送且按发送顺序接收。内设置流量控制,避免数据流淹没慢的接收方。数据被看作是字节流,无长度限制。
    • ==> TCP
  • 数据报套接字(SOCK_DGRAM
    • 提供无连接服务。数据包以独立数据包的形式被发送,不提供无差错保证,数据可能丢失或重复,顺序发送,可能乱序接收。
    • ==> UDP
  • 原始套接字(SOCK_RAW
    • 可以对较低层次协议如IP、ICMP直接访问。

IP 地址

IP 地址是 Internet 中主机的唯一标识

  • Internet 中的主机要与别的机器通信必须具有一个 IP 地址
  • IP 地址为 32 位(IPv4)或者 128 位(IPv6)
  • 每个数据包都必须携带目的 IP 地址和源 IP 地址,路由器依靠此信息为数据包选择路由

IPV4ip 地址的表示形式:点分十进制,例子:192.168.1.123

IP地址分类(最后都会转换为一个 32 位的无符号整数)

  • A 类 0000 0000 - 0111 1111 0.x.x.x - 127.x.x.x
  • B 类 1000 0000 - 1011 1111 128.x.x.x - 191.x.x.x
  • C 类 1100 0000 - 1101 1111 192.x.x.x - 223.x.x.x
  • D 类 1110 0000 - 1110 1111 224.x.x.x - 239.x.x.x 表示组播地址
  • E 类 1111 0000 - 1111 1111 240.x.x.x - 255.x.x.x 属于保留测试
  • 127.x.x.x 表示主机地址
  • 192.168.x.x 表示局域网 ip 地址
  • 192.168.1.x 为例
    • 192.168.1.0 表示网段、网络地址
    • 192.168.1.1 表示网关
    • 192.168.1.255 表示广播地址

子网掩码 表示主机的最大连接数

  • A类 255.0.0.0 2~24
  • B类 255.255.0.0 2~16
  • C类 255.255.255.0 2~8

IP 地址的转换

  • #include <arpa/inet.h>
  • inet_addr()
    • 将点分十进制 IP 地址转化为网络字节序的整型数据
    • in_addr_t inet_addr(const char *cp);
  • inet_ntoa()
    • 将网络字节序的整型数据转化为点分十进制 IP 地址
    • char *inet_ntoa(struct in_addr in);
  • 例子:inet_addr("192.168.1.123");

端口号

端口号(vi /etc/services 查看已经占用的端口号)

  • 为了区分一台主机接收到的数据包应该转交给哪个进程来进行处理,使用端口号来区别
  • TCP 端口号与 UDP 端口号独立
  • 端口号一般由 IANA (Internet Assigned Numbers Authority) 管理
    • 众所周知端口:11023(1255之间为众所周知端口,256~1023端口通常由 UNIX 系统占用)
    • 已登记端口:1024~49151
    • 动态或私有端口:49152~65535
    • 一般使用 6666 7777 8888 9999 10000 10001
  • 套接字和端口
  • 端到端通信数据包投递过程
  • 一个比喻

字节序

不同类型 CPU 的主机中,内存存储多字节整数序列有两种方法,称为主机字节序(HBO):

  • 小端序(little-endian) - 低序字节存储在低地址
    • 将低字节存储在起始地址,称为“Little-Endian”字节序,Intel、AMD 等采用的是这种方式;
  • 大端序(big-endian)- 高序字节存储在低地址
    • 将高字节存储在起始地址,称为“Big-Endian”字节序,由 ARM、Motorola 等所采用

如何测试主机字节序

  • 方法一:使用指针
int main(){
   
    int a = 0x12345678;
    char *pa = (char *)&a;
     
    printf("a = %#x\n", a);
    printf("*pa = %#x\n", *pa);
     
    if(*pa == (a | 0xff))
        puts("Small end storage\n");
    else
        puts("Big end storage\n");}
  • 方法二:使用 file 命令
$ file a.out
a.out: ELF 64-bit LSB  executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=ef89abc11bb84eb2fe2fdc11d51ccc6a8063cba1, not stripped
  • 方法三:使用共用体
#include <stdio.h>
 
union HBO{
   
    int a;
    char b;
};
 
int main(){
   
    union HBO myHBO;
    myHBO.a = 0x12345678;
     
    printf("%#x\n",myHBO.a);
    printf("%#x\n", myHBO.b);
     
    if(myHBO.b == (myHBO.a & 0xff))
        puts("Least Significant Byte");
    else
        puts("Most Significant Byte");}

网络中传输的数据必须按网络字节序,即大端字节序

字节序转换函数

  • #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);
  • 例子:
    • htons(9999);

系统调用

TCP 服务器、客户端

TCP 服务器端流程

  • 创建套接字 socket( )
  • 填充服务器网络信息结构体 sockaddr_in
  • 将套接字与服务器的网络信息结构体绑定 bind()
  • 将套接字设置为被动监听模式 listen()
  • 阻塞等待客户端的连接请求 accept()
  • 进行通信 recv()/send()

TCP 客户端流程

  • 创建套接字 socket()
  • 填充服务器网络信息结构体 sockaddr_in
  • 发送客户端的连接请求 connect()
  • 进行通信 send()/recv()

网络编程相关 API

Socket

#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>

int socket(int domain, int type, int protocol)

功能:创建一个套接字,返回一个文件描述符

参数:

  • domain:通信域,协议族
    • AF_UNIX 本地通信
    • AF_INET ipv4 网络协议
    • AF_INET6 ipv6 网络协议
    • AF_PACKET 底层的协议
  • type:类型
    • SOCK_STREAM 流式套接字 tcp
    • SOCK_DGRAM 数据报套接字 UDP
    • SOCK_RAW 底层的通信
  • protocol:一般为 0

返回值:

  • 成功:文件描述符
  • 失败:-1

例子:

int sockfd;
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
   
    perror("fail to socket");
    //return -1;
    exit(1);
}
地址相关的数据结构

通用的(一般不用)

struct sockaddr {
   
    sa_family_t sa_family;  // 2 个字节
    char        sa_data[14]; // 14 个字节
}

一般使用的:sockaddr_in
#include <netinet/in.h>
struct sockaddr_in{

  • __SOCKADDR_COMMON (sin_);
    • #define __SOCKADDR_COMMON(sa_prefix) sa_family_t sa_prefix##family
    • 在函数宏里面,##<
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值