#include
unsigned long int htonl(unsigned long int hostlong);
unsigned short int htons(unsigned short int hostshort);
unsigned long int ntohl(unsigned long int netlong);
unsigned short int ntohs(unsigned short int netlong);
#include
struct sockaddr{
sa_family_t sa_family; //地址族类型
char sa_data[14]; //存放socket地址值
}
#include
struct sockaddr_storage{
sa_family_t sa_family; //地址族类型
unsigned long int __ss_align;
char __ss_padding[128-sizeof(__ss_align)];
}
这个结构体是内存对齐的,也是就是[128-sizeof(__ss_align)]
的作用。编译器创建一个变量时候地址并不是随意取得,而是某一个数的倍数,这样就导致了有一部分内存是用不到的,而上述过程会把申请到的内存刚好用完,这就是内存对齐。
#include
struct sockaddr_un {
sa_family_t sin_family; //地址族
char sun_path[108]; //文件路径名
}
struct sockaddr_in {
sa_family_t sin_family; //地址族
u_int16_t sin_port; //端口号,网络字节序
struct in_addr sin_addr; //IPv4地址结构体
}
struct in_addr {
u_int32_t s_addr; //IPv4地址,用网络字节序表示
}
struct sockaddr_in6 {
sa_family_t sin6_family; //地址族
u_int16_t sin6_port; //端口号,网络字节序
u_int32_t sint6_flowinfo; //流信息,应设置为0
struct in6_addr sin6_addr; //IPv6地址结构体
u_int32_t sin6_scope_id //scope ID, 处于实验阶段
}
struct in6_addr {
unsigned char sa_addr[16]; //IPv6地址,用网络字节序表示
}
强制转换
为通用的socket地址类型sockaddr。因为所有socket编程接口使用的地址参数类型都是sockaddr.#include
in_addr_t inet_addr(const char* strptr); //使用点十分进制字符串表示的IPv4地址转化为网络字节序表示的IPv4地址,失败返回INADDR_NONE
int inet_aton(const char* cp, struct in_addr* inp); //功能与inet_addr类似,但是会把结果存储在inp中,成功返回1,失败返回0
char * inet_ntoa(srtcut in_addr in); //把网络字节序的IPv4地址转化为用点分十进制表示的字符串。
#include
int inet_pton(in af, const char* src, void* dst); //字符串表示的IP转化为网络字节序表示的IP,成功返回1,失败返回0
const char* inet_ntop(int af, const void* src, char* dst, socklen_t cnt); //网络字节序IP到字符串表示的IP,成功返回目标存储单元的地址,失败返回NULL
INET_ADDRSTRLEN
用于IPv4,INET6_ADDRSTRLEN
用于IPv6#include
#include
int socket(int domain, int type, int protocol); //成功返回文件描述符,失败返回-1。
SOCK_NONBLOCK
和SOCK_CLOEXEC
参数相与(逻辑与)作为一个新参数,SOCK_NONBLOCK代表把新创建的socket设置为非阻塞,SOCK_CLOEXEC代表调用fork创建子进程时在子进程关闭该socket。#include
#include
int bind(int sockfd, const struct sockaddr* my_addr, socklen_t addrlen); //成功返回0,失败返回-1。并设置error
#include
int listen(int sockfd, int backlog);
5
。//研究socket监听中block参数的影响
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
static bool stop = false;
// sigterm信号处理函数,触发时结束主程序中的循环
static void handle_term(int sig) {
stop = true;
}
int main(int argc, char* argv[]) {
signal(SIGTERM, handle_term);
if (argc <= 3) {
std::cout << "usage: " << argv[0] <<" ip_address port_number backlog" << std::endl;
return 1;
}
const char* ip = argv[1];
int port = atoi(argv[2]);
int backlog = atoi(argv[3]);
//创建socket
int sockfd = socket(PF_INET, SOCK_STREAM, 0);
assert(sockfd >= 0);
//创建IPv4地址
struct sockaddr_in address;
bzero(&address, sizeof(address));
address.sin_family = AF_INET;
address.sin_port = htons(port);
assert(inet_aton(ip, &address.sin_addr) == 1);
//命名socket
assert(bind(sockfd, (struct sockaddr*)&address, sizeof(address)) != -1);
//监听
assert(listen(sockfd, backlog) != -1);
//循环等待
while(!stop){
sleep(1);
}
//关闭socket
close(sockfd);
return 0;
}
#include
#include
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); //成功返回一个新连接的socket,失败返回-1。
sockfd是执行过listen系统调用的监听sockfd
addr用来获取被接受连接的远端socket地址
addrlen指定该socket地址长度
accpet只是负责从监听队列中取出连接,不论连接处于何种状态
#include
#include
int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen); //成功返回0,失败返回-1。设置error
#include
int close(int fd);
close并不是立刻关闭连接,而是将fd的引用计数-1,当fd的引用计数为0时,才是真正关闭连接。
多进程中,一次fork系统调用默认将父进程打开的socket引用计数+1。因此必须对父进程和子进程的socket执行close调用才能将连接真正关闭。
如果必须立刻终止连接,而不是将引用计数-1,那就使用shutdown
#include
int shutdown(int sockfd, in howto);
#include
#include
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
#include
#include
#include
#include
#include
#include
//创建IPv4
class createSockfd{
struct sockaddr_in address;
public:
int sockfd;
struct sockaddr_in client;
socklen_t client_addrlength;
int connfd; //accept接受到的socketfd
public:
createSockfd(const char *ip, int port){
sockfd = socket(PF_INET, SOCK_STREAM, 0);
assert(sockfd >= 0);
bzero(&address, sizeof(address));
address.sin_family = AF_INET;
address.sin_port = htons(port);
assert(inet_aton(ip, &address.sin_addr) == 1);
client_addrlength = sizeof(client);
}
~createSockfd(){
close(sockfd);
close(connfd);
}
int bindSockfd();
int connectSockfd();
int listenfd(int backlog = 5);
int acceptfd();
};
//命名socket
int createSockfd::bindSockfd(){
return bind(sockfd, (struct sockaddr*)&address, sizeof(address));
}
//连接socket
int createSockfd::connectSockfd(){
return connect(sockfd, (struct sockaddr*)&address, sizeof(address));
}
//监听socket
int createSockfd::listenfd(int backlog){
return listen(sockfd, backlog);
}
int createSockfd::acceptfd(){
connfd = accept(sockfd, (struct sockaddr*)&client, &client_addrlength);
return connfd;
}
客户端,用于发送报文,这里一共发送了三个报文,“123” “abc” “123”,其中“abc”报文被认为是带外数据。
#include "../create_sockfd.h"
#include
#include
int main(int argc, char *argv[]){
if (argc <=2){
std::cout << "usage: " << basename(argv[0]) << " ip_address port_number" << std::endl;
return 1;
}
createSockfd sockfd(argv[1], atoi(argv[2]));
if (sockfd.connectSockfd() < 0){
std::cout << "connect faild." << std::endl;
} else {
const char* oob_data = "abc";
const char* normal_data = "123";
send(sockfd.sockfd, normal_data, strlen(normal_data), 0);
send(sockfd.sockfd, oob_data, strlen(oob_data), MSG_OOB);
send(sockfd.sockfd, normal_data, strlen(normal_data), 0);
}
return 0;
}
服务器端报文,用于接收数据
#include "../create_sockfd.h"
#include "iostream"
#include
#include
#define BUF_SIZE 1024
int main(int argc, char *argv[]){
if(argc <= 2){
std::cout << "usage: " << basename(argv[0]) << " ip_address port_number" << std::endl;
return 1;
}
createSockfd socketfd(argv[1], atoi(argv[2]));
socketfd.bindSockfd();
socketfd.listenfd(5);
socketfd.acceptfd();
if(socketfd.connfd < 0){
std::cout << "error : " << errno << std::endl;
} else {
char buffer[BUF_SIZE];
memset(buffer, '\0', BUF_SIZE);
int ret = recv(socketfd.connfd, buffer, BUF_SIZE - 1, 0);
std::cout << "got " << ret << " bytes of normal data '" << buffer << "'" << std::endl;
memset(buffer, '\0', BUF_SIZE);
ret = recv(socketfd.connfd, buffer, BUF_SIZE - 1, MSG_OOB);
std::cout << "got " << ret << " bytes of odd data '" << buffer << "'" << std::endl;
memset(buffer, '\0', BUF_SIZE);
ret = recv(socketfd.connfd, buffer, BUF_SIZE - 1, 0);
std::cout << "got " << ret << " bytes of normal data '" << buffer << "'" << std::endl;
}
return 0;
}
接收到
got 1 bytes of odd data 'c'
got 5 bytes of normal data '123ab'
got 3 bytes of normal data '123'
#include
#include
ssize_t recvfrom(int sockfd, void* buf, size_t len, int flags, struct sockaddr* src_adr, socklen_t* addrlen);
ssize_ sendto(int sockfd, const void* buf, size_t len, int flags, const struct sockaddr* dest_addr, socklen_t addrlen);
recvfrom读取sockfd上的数据,buf和len分别是指定缓冲区的位置和大小。因为UDF不连接,所以每一次都得传递地址,addrlen是地址的长度。
sendto往sockfd写数据,buf和len是指定缓冲区的位置和大小,dest_addr是指定地址的位置,addrlen是指定地址的大小。
recvfrom和sendto也可以用于面向连接的socket,只需要把后边的参数和职位NULL,因为此时的地址已经知道了。
#include
ssize_t recvmsg(int sockfd, struct msghdr* msg, int flags);
ssize_t sendmsg(int sockfd, struct msghdr* msg, int flags);
struct msghdr {
void* msg_name; //socket地址
socklen_t msg_namelen; //socket地址长度
struct iovec* msg_iov; //分散的内存块
int msg_iovlen; //分散的内存块数量
void* msg_control; //指向辅助数据的起始位置
socklen_t msg_controllen; //辅助数据的大小
int msg_flagsl //辅助函数中的flags参数,会在调用中更新。
};
struct iovec{
void *iov_base; //内存块起始地址
size_t iov_len; //内存块长度
};
#include
int sockatmark(int sockfd);
#inlcude <sys/socket.h>
//成功返回0,失败返回-1。
int getsocketname(int sockfd, struct sockaddr* address, socklen_t* address_len); //获取本端,存储在address中
int getpeername(int sockfd, struct sockaddr* address, socklen_t* address_len); //获取远端
#include
//成功返回0,失败返回-1
//读取socket文件描述符
int getsockopt(int sockfd, int level, int option_name, void* option_value, socklen_t* restrict option_len);
//设置socket文件描述符
int setsockopt(int sockfd, int level, int option_name, const void* option_value, socklen_t option_len);
...
int recvbuf = 50;
int len = sizeof(recvbuf);
setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &recvbuf, sizeof(recvbuf));
getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &recvbuf, (socklen_t*)&len);
std::cout << "Tcp recevie buffer size after setting is " << recvbuf << std::endl;
...
#incldue <sys/socket.h>
struct linger {
int l_onoffl; //开启(非0),关闭(0)
int l_inger; //滞留时间
}
略
#include
struct hostent* gethostbyname(const char* name);
struct hostent* gethostbyaddr(const void* addr, size_t len, int type);
struct hostent {
char* h_name; //主机名称
char** h_aliases; //主机别名列表
int h_addrtype; //地址类型
int h_length; //地址长度
char** h_addr_list; //网络字节序,主机IP地址列表。
};
#include
struct servent* getservbyname(const char* name, const char* proto);
struct servent* getservbyport(int port, const char* proto);
struct servent{
char* s_name; //服务名称
char** s_aliases; //服务别名
int s_port; //端口号
char* s_proto; //服务类型,通常是tcp或udp
};
略
略