/**
* g++ chapter5/5.1_byteorder.cpp -o out.app && ./out.app
*
* 代码5-1 判断机器字节序
*/
#include
using namespace std;
// 字节序转换函数:
#include
unsigned long int htonl(unsigned long int hostlong);
unsigned short int htons(unsigned short int hostlong);
unsigned long int ntohl(unsigned long int netlong);
unsigned short int ntohs(unsigned short int netshort);
int main()
{
union {
short value;
char union_bytes[sizeof(short)];
} test;
test.value = 0x0102;
if ((test.union_bytes[0] == 1) && (test.union_bytes[1] == 2))
{
cout << "Big Endian" << endl;
}
else if ((test.union_bytes[0] == 2) && (test.union_bytes[1] == 1))
{
cout << "Little Endian" << endl; //实际输出
}
else
{
cout << "Unknown..." << endl;
}
return 0;
}
#include
#include
// domain: IPv4设置为PF_INET,IPv6设置为PF_INET6
// type: TCP为SOCK_STREAM,UDP为SOCK_DGRAM
// protocol: 给定前两个参数时选择一个具体协议,通常是唯一的(前两个参数已经完全决定),默认设为0
// 调用成功时返回socket文件描述符,失败返回-1并设置errno
int socket(int domain, int type, int protocol);
#include
#include
// 成功返回0,失败返回-1并设置errno。最常见的两种errno:
// EACCESS:被绑定的是受保护地址(普通用户绑定端口号0~1023)
// EADDRINUSE: 被绑定的地址正在使用中。
int bind(int sockfd,const struct sockaddr* my_addr,socklen_t addrlen);
#include
#include
// backlog: 监听队列的最大长度,完全连接状态的socket的上限
int listen(int sockfd,int backlog);
/**
* g++ chapter5/5.4_testlisten.cpp -o testlisten.app && ./testlisten.app 127.0.0.1 12345 3
*
* telnet 127.0.0.1 12345 # 多次执行
*
* netstat -nt | grep 12345
*
* 代码 5-3 backlog参数
*/
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
static bool stop = false;
static void handle_term(int sig)
{
stop = true;
}
int main(int argc, char *argv[])
{
signal(SIGTERM, handle_term);
if (argc <= 3)
{
cout << "Usage: " << argv[0] << " IP port backlog" << endl;
return 1;
}
const char *ip = argv[1];
int port = atoi(argv[2]);
int backlog = atoi(argv[3]);
int sock = socket(PF_INET, SOCK_STREAM, 0);
assert(sock > 0);
// 创建一个IPv4 socket地址
struct sockaddr_in address;
bzero(&address, sizeof(address));
address.sin_family = AF_INET;
inet_pton(AF_INET, ip, &address.sin_addr);
address.sin_port = htons(port);
// 命名socket
int ret = bind(sock, (struct sockaddr *)&address, sizeof(address));
assert(ret != -1);
// 开始监听
cout << "backlog : " << backlog << endl;
ret = listen(sock, backlog);
assert(ret != -1);
while (!stop)
{
sleep(1);
}
//关闭socket
close(sock);
return 0;
}
/**
* g++ chapter5/5.5_testaccept.cpp -o testaccept.app && ./testaccept.app 127.0.0.1 12345
*
* telnet 127.0.0.1 12345
*
* netstat -nt | grep 12345
*
* 代码 5-5 接受一个异常连接
*/
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
int main(int argc, char *argv[])
{
if (argc < 3)
{
cout << "Usage: " << argv[0] << " IP port" << endl;
return 1;
}
const char *ip = argv[1];
int port = atoi(argv[2]);
int sock = socket(PF_INET, SOCK_STREAM, 0);
assert(sock > 0);
// 创建一个IPv4 socket地址
struct sockaddr_in address;
bzero(&address, sizeof(address));
address.sin_family = AF_INET;
inet_pton(AF_INET, ip, &address.sin_addr);
address.sin_port = htons(port);
// 命名socket
int ret = bind(sock, (struct sockaddr *)&address, sizeof(address));
assert(ret != -1);
// 开始监听
ret = listen(sock, 3);
assert(ret != -1);
// 暂停,等待客户端操作结束
sleep(20);
struct sockaddr_in client;
socklen_t client_addrlength = sizeof(client);
int conn_fd = accept(sock, (struct sockaddr *)&client, &client_addrlength);
if (conn_fd < 0)
{
cout << "Error:" << errno << endl;
}
else
{ //连接成功,打印客户端端口号和IP
char remote[INET_ADDRSTRLEN];
printf("Connected IP: %s ,Port: %d\n", inet_ntop(AF_INET, &client.sin_addr, remote, INET_ADDRSTRLEN), ntohs(client.sin_port));
close(conn_fd);
}
//关闭socket
close(sock);
return 0;
}
#include
#include
// 成功返回0,失败返回-1并设置errno
int connect(int sockfd,const struct sockaddr* serv_addr,socklen_t addrlen);
#include
// close是将fd的引用计数减一,当fd的引用计数为0时才会真正关闭;在多进程程序中,一次fork默认会将父进程打开的socket的引用计数加一,因此父子进程都要关闭该socket。
int close(int fd);
// 如果要立即终止连接,而不是引用计数减一,使用shutdown。
// howto: SHUT_RD,只关闭读,接收缓冲区数据全部丢弃;SHUT_WR,关闭写,程序不能继续写,缓冲区数据会发送完,此时处于半连接状态;SHUT_RDWR,同时关闭读写。
#include
int shutdown(int sockfd,int howto);
#include
#include
// TCP读写
// recv返回实际长度,可能小于期望长度;返回0:对方已关闭;返回-1:出错
ssize_t recv(int sockfd,void* buf,size_t len,int flags);
//返回实际发送数据
ssize_t send(int sockfd,const void* buff,size_t len,int flags);
// UDP读写
// UDP是无连接的,读写都需要指定对方地址
ssize_t recvfrom(int sockfd,void* buf,size_t len,int flags,struct sockaddr* src_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);
// 通用数据读写
ssize_t recvmsg(int sockfd, struct msghdr* msg, int flags);
ssize_t sendmsg(int sockfd, struct msghdr* msg, int flags);
#include
//检测下一个被读到的数据是否是带外数据
int sockatmark(int cockfd);
#include
// 获取本地socket地址和对方socket地址
int getsockname(int sockfd,struct sockaddr* address, socklen_t* address_len);
int getpeername(int sockfd,struct sockaddr* address, socklen_t* address_len);
#include
int getsockopt(int sockfd,int level, int option_name, void* option_value,socklen_t* restrict option_len);
int setsockopt(int sockfd,int level, int option_name, const void* option_value,socklen_t option_len);
#include
// 创建一个单向管道,实现进程间通信
// fd[1]只能写,fd[0]只能读
int pipe(int fd[2]);
#include
#include
// 创建一个双向管道,domain只能是AF_UNIX
int socketpair(int domain,int type,int protocol, int fd[2]);
/**
* g++ chapter6/6.2_cgi.cpp -o cgi.app && ./cgi.app 127.0.0.1 12345
*
* telnet 127.0.0.1 12345
*
* 代码 6-1 CGI服务器原理
*/
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
int main(int argc, char *argv[])
{
if (argc < 3)
{
cout << "Usage: " << argv[0] << " IP port" << endl;
return 1;
}
const char *ip = argv[1];
int port = atoi(argv[2]);
int sock = socket(PF_INET, SOCK_STREAM, 0);
assert(sock > 0);
// 创建一个IPv4 socket地址
struct sockaddr_in address;
bzero(&address, sizeof(address));
address.sin_family = AF_INET;
inet_pton(AF_INET, ip, &address.sin_addr);
address.sin_port = htons(port);
// 命名socket
int ret = bind(sock, (struct sockaddr *)&address, sizeof(address));
assert(ret != -1);
// 开始监听
ret = listen(sock, 3);
assert(ret != -1);
struct sockaddr_in client;
socklen_t client_addrlength = sizeof(client);
int conn_fd = accept(sock, (struct sockaddr *)&client, &client_addrlength);
if (conn_fd < 0)
{
cout << "Error:" << errno << endl;
}
else
{ //连接成功
close(STDOUT_FILENO);
// dup创建新的文件描述符,它与原有文件描述符指向相同的文件
// 总是返回系统中最小的可用文件描述符,所以返回值实际是1,即STDOUT_FILENO
// dup2函数返回第一个不小于file_descriptor_two的整数值
int new_fd = dup(conn_fd);
cout << "fd=" << new_fd << endl;
cout << "hello!" << endl;
close(conn_fd);
}
//关闭socket
close(sock);
return 0;
}
#include
// 分散读写,一次读写多个内存块
ssize_t readv(int fd,const struct iovec* vector,int count);
ssize_t writev(int fd,const struct iovec* vector,int count);
#include
// 直接在两个文件描述符之间传递数据(完全在内核中操作),
// 避免了内核与用户缓冲区之间的数据拷贝,效率很高,称为零拷贝。
// in_fd必须指向真实文件,out_fd必须指向socket,
// offset指定从哪个位置开始拷贝,count指定传输数据量
ssize_t sendfile(int out_fd, int in_fd,off_t* offset,size_t count);
在两个文件描述符之间移动数据,也是零拷贝操作。
在两个管道文件描述符之间复制数据,也是零拷贝操作,但不消耗数据。
提供了对文件描述符的各种控制操作。在网络编程中,通常用来讲一个文件描述符设置为非阻塞的
#include
void openlog(const char* ident,int logopt,int facility);
int setlogmask(int maskpri);//设置日志级别,用于日志过滤
void syslog(int priority,const char* message,...);
void closelog();
/**
* g++ chapter7/7.1_test_uid.cpp -o test_uid.app
*
* sudo chown root:root test_uid.app
* #设置目标文件的set-user-id标志,使得运行程序的用户拥有该程序有效用户的权限
* sudo chmod +s test_uid.app
*
* ./test_uid.app
*
* 代码 7-1 测试进程的UID和EUID
*/
#include
#include
using namespace std;
int main()
{
uid_t uid = getuid();
uid_t euid = geteuid();
cout << "UID: " << uid << " EUID: " << euid << endl;
return 0;
}
#include
// 如果buf空间不够,则返回NULL;如果buf为NULL,则返回内部malloc的内存,要自己释放
char* getcwd(char* buf,size_t size);
int chdir(const char* path);
// 切换根目录,只有特权进程能切换根目录
int chroot(const char* path);