Linux-Network-Programming

Sample codes for Linux network programming based on socket and I/O multiplexing in C++11, revealing the essence of computer network communication through packet capture.

View on GitHub

基本 TCP 套接字 API

socket(2)

#include <sys/types.h>
#include <sys/socket.h>

int socket(int domain, int type, int protocal);
int sock = socket(AF_INET, SOCK_STREAM, 0);

bind(2)

#include <sys/types.h>
#include <sys/socket.h>

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
sockaddr_in address; // #include <netinet/in.h>
address.sin_family = AF_INET;
address.sin_port = htons(12345);
address.sin_addr.s_addr = htonl(INADDR_ANY);
bzero(&(address.sin_zero), 8);
bind(sock, reinterpret_cast<sockaddr*>(&address), sizeof(address));
struct sockaddr_in {
    short            sin_family;   // 2 bytes e.g. AF_INET, AF_INET6  
    unsigned short   sin_port;     // 2 bytes e.g. htons(3490)  
    struct in_addr   sin_addr;     // 4 bytes see struct in_addr, below  
    char             sin_zero[8];  // 8 bytes zero this if you want to  
};

struct in_addr {
    unsigned long s_addr;          // 4 bytes load with inet_pton()  
};

// sockaddr 和 sockaddr_in 大小都是 16 字节,只不过 sockaddr_in 把 14 个字节的 sa_data 拆开了
// sin_zero 用于填充字节,保证 sockaddr_in 和 sockaddr 一样大
struct sockaddr {
    unsigned short    sa_family;    // 2 bytes address family, AF_xxx  
    char              sa_data[14];  // 14 bytes of protocol address  
};

// sockaddr 是给系统用的,程序员应该用 sockaddr_in
// 通常用类型、IP 地址、端口填充 sockaddr_in 后,转换成 sockaddr 作为参数传递给调用函数

listen(2)

#include <sys/types.h>
#include <sys/socket.h>

int listen(int sockfd, int backlog);
listen(sock, 5);

accept(2)

#include <sys/types.h>
#include <sys/socket.h>

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
sockaddr_in client_address;
socklen_t len = sizeof(client_address);
int client_fd =
    accept(sock, reinterpret_cast<sockaddr*>(&client_address), &len);
if (connectFd != -1) {
  std::cout << "ip: " << inet_ntoa(client_address.sin_addr) << '\n';
  std::cout << "port: " << ntohs(client_address.sin_port) << '\n';
  close(client_fd);
}

connect(2)

#include <sys/types.h>
#include <sys/socket.h>

int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen);
sockaddr_in server;
bzero(&server, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(12345);
server.sin_addr.s_addr = inet_addr("192.168.211.129");
connect(sock, reinterpret_cast<sockaddr*>(&server), sizeof(server));

close(2)

#include <unistd.h>

int close(int sockfd);
close(sock);

字符处理函数

#include <strings.h>

void bzero(void *s, size_t n);
void bcopy(const void *src, void *dest, size_t n);
int bcmp(const void *s1, const void *s2, size_t n); // 相等返回 0,否则返回非 0
#include <arpa/inet.h>

 // 主机字节序转网络字节序
unit16_t htons(unit16_t hostshort); // 一般用于端口号
uint32_t htonl(uint32_t hostlong); // 一般用于 IP 地址

 // 网络字节序转主机字节序
uint16_t ntohs(uint16_t netshort); // 一般用于端口号
uint32_t ntohl(uint32_t netlong); // 一般用于 IP 地址
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

// 将 cp 转换为 32 位网络字节序二进制值并存储在 inp 中。字符串有效返回 1,否则返回 0
int inet_aton(const char *cp, struct in_addr *inp);

// 将点分十进制字符串 cp 转为 IPv4 地址,字符串有效则返回 32 位二进制网络字节序的IPv4地址
// 否则为INADDR_NONE,通常是-1,即32位均为1,即255.255.255.255,这也意味着不能处理255.255.255.255
// 现在inet_addr已被废弃
in_addr_t inet_addr(const char *cp);

// 返回指向点分十进制字符串的指针,返回值所指向的字符串驻留在静态内存中,因此该函数是不可重入的
char *inet_ntoa(struct in_addr in);
#include <arpa/inet.h>

// 成功返回1,输入不是有效的表达式返回0,出错返回-1
int inet_pton(int af, const char *src, void *dst);

// 成功返回指向结果的指针,出错返回NULL
const char* inet_ntop(int af, const void *src, char *dst, socklen_t size);

// 在<netinet/in.h>中有如下定义,可用作size
// 如果size太小,不足以容纳表达式结果,则返回一个空指针,并置errno为ENOSPC
#define INET_ADDRSTRLEN 16
#define INET6_ADDRSTRLEN 46
const char* ip = "192.168.211.129";
inet_pton(AF_INET, ip, &foo.sin_addr); // foo.sin_add.s_addr = inet_addr(ip);

char s[INET_ADDRSTRLEN];
const char* p = inet_ntop(AF_INET, &foo.sin_addr, s, sizeof(s)); // p = inet_ntoa(foo.sin_addr);

fork(2)

#include <sys/types.h>
#include <unistd.h>

pid_t fork(void);
int i = 0;
pid_t pid = fork();

if (pid == -1) {
  std::cerr << "errno: " << errno << '\n';
} else if (pid == 0) {
  std::cout << "child id: " << getpid() << '\n';
  std::cout << "parent id:" << getppid() << '\n';
  ++i;
} else {
  std::cout << "child id: " << pid << '\n';
  std::cout << "parent id: " << getpid() << '\n';
}

std::cout << i;  // 在子进程中为 1,在父进程中为 0