目录
0. Socket 编程预备知识
0.1 源IP和目的IP
IP 在网络中, 用来标识主机的唯一性。
网络传输的过程,就是将数据从源IP上的对应进程发到目的IP的对应进程,例如使用qq从qq服务器上获得数据。
0.2 端口号
端口号(port)是传输层协议的内容
• 端口号是一个 2 字节 16 位的整数;
• 端口号用来标识一个进程, 告诉操作系统, 当前的这个数据要交给哪一个进程来处理;
• IP 地址 + 端口号能够标识网络上的某一台主机的某一个进程;
• 一个端口号只能被一个进程占用。
端口号范围划分
• 0 - 1023: 知名端口号, HTTP, FTP, SSH 等这些广为使用的应用层协议, 他们的
端口号都是固定的。
• 1024 - 65535: 操作系统动态分配的端口号. 客户端程序的端口号, 就是由操作
系统从这个范围分配的。
另外, 一个进程可以绑定多个端口号,但是一个端口号不能被多个进程绑定;
• 进程 ID 属于系统概念, 技术上也具有唯一性, 确实可以用来标识唯一的一个进
程, 但是这样做, 会让系统进程管理和网络强耦合, 实际设计的时候, 并没有选择这
样做。
源端口号和目的端口号
传输层协议(TCP 和 UDP)的数据段中有两个端口号, 分别叫做源端口号和目的端口号.
就是在描述 "数据是谁发的, 要发给谁"。
理解 socket
• 综上, IP 地址用来标识互联网中唯一的一台主机, port 用来标识该主机上唯一的
一个网络进程
• IP+Port 就能表示互联网中唯一的一个进程
• 所以, 通信的时候, 本质是两个互联网进程代表人来进行通信, {srcIp,
srcPort, dstIp, dstPort}这样的 4 元组就能标识互联网中唯二的两个进程
• 所以, 网络通信的本质, 也是进程间通信
• 我们把 ip+port 叫做套接字 socket
补充:传输层属于内核,要通过网络协议栈进行通信, 必定调用的是传输层提供的系统调用, 来进行网络通信的。
1. UDP协议
1.1 UDP协议特点
1.传输层协议。
2.无连接。
3.不可靠传输。
3.面向数据报。
1.2 网络字节序
内存中的数据存储有大端和小端之分, 磁盘文件中的数据存储也有大端小端之分, 网络数据流同样有大端小端之分. 那么如何定义网络数据流的地址呢?
• 发送主机通常将发送缓冲区中的数据按内存地址从低到高的顺序发出;
• 接收主机把从网络上接到的字节依次保存在接收缓冲区中,也是按内存地址从低到高的顺序保存;
• 因此,网络数据流的地址应这样规定:先发出的数据是低地址,后发出的数据是高地址.
• TCP/IP 协议规定,网络数据流应采用大端字节序,即低地址高字节.
• 不管这台主机是大端机还是小端机, 都会按照这个 TCP/IP 规定的网络字节序来发送/接收数据;
• 如果当前发送主机是小端, 就需要先将数据转成大端; 否则就忽略, 直接发送即可;
于是就有了以下库函数来做数据从主机序列到网络序列的转化:
#include <arpa/inet.h>
uint16_t htons(uint16_t hostshort);
uint32_t htonl(uint32_t hostlong);
uint16_t ntohs(uint16_t netshort);
uint32_t ntohl(uint32_t netlong);
• 这些函数名很好记,h 表示 host,n 表示 network,l 表示 32 位长整数,s 表示 16 位
短整数。
• 例如 htonl 表示将 32 位的长整数从主机字节序转换为网络字节序,例如将 IP 地
址转换后准备发送。
• 如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回;
• 如果主机是大端字节序,这些函数不做转换,将参数原封不动地返回。
1.2 socket编程接口
由于UDP是面向数据报的无连接协议,所以数据传输时要使用的接口有:
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
int bind(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
size_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
接下来逐一介绍:
1. socket()
函数
socket()
函数用于创建一个套接字(socket),这是网络通信的基本端点。通过套接字,应用程序可以发送和接收数据。
原型:
int socket(int domain, int type, int protocol);
-
domain
:指定协议族(地址族),常见的值有:AF_INET
:IPv4 网络协议。AF_INET6
:IPv6 网络协议。AF_UNIX
:本地通信协议(仅适用于 UNIX 系统)。
-
type
:指定套接字类型,常见的值有:SOCK_STREAM
:流套接字,通常用于 TCP(面向连接的协议)。SOCK_DGRAM
:数据报套接字,通常用于 UDP(无连接协议)。
-
protocol
:指定协议,通常为0
,表示自动选择合适的协议。一般与type
配合使用,如:0
:自动选择协议。IPPROTO_TCP
:用于 TCP。IPPROTO_UDP
:用于 UDP。
返回值:
- 成功:返回一个非负整数(套接字描述符