友情提醒:可以利用上面的目录,选择感兴趣的部分。
前面3个小节,主要介绍了socket是什么,网络模型,IP地址等基本的知识,接下来介绍socket编程中,最常使用到的几个系统函数(syscall)和相关的数据结构,为linux socket编程入门做好准备。
int
struct addrinfo {
int ai_flags; // AI_PASSIVE, AI_CANONNAME, etc.
int ai_family; // AF_INET, AF_INET6, AF_UNSPEC
int ai_socktype; // SOCK_STREAM, SOCK_DGRAM
int ai_protocol; // use 0 for "any"
size_t ai_addrlen; // size of ai_addr in bytes
struct sockaddr *ai_addr; // struct sockaddr_in or _in6
char *ai_canonname; // full canonical hostname
struct addrinfo *ai_next; // linked list, next node
};
其中:
struct sockaddr {
unsigned short sa_family; // address family, AF_xxx
char sa_data[14]; // 14 bytes of protocol address
};
其中:
struct sockaddr_in {
short int sin_family; // Address family, AF_INET
unsigned short int sin_port; // Port number
struct in_addr sin_addr; // Internet address
unsigned char sin_zero[8]; // Same size as struct sockaddr
};
其中的struct in_addr 结构为:
struct in_addr {
uint32_t s_addr; // that's a 32-bit int (4 bytes)
};
其中:
struct sockaddr_in6 {
u_int16_t sin6_family; // address family, AF_INET6
u_int16_t sin6_port; // port number, Network Byte Order
u_int32_t sin6_flowinfo; // IPv6 flow information
struct in6_addr sin6_addr; // IPv6 address
u_int32_t sin6_scope_id; // Scope ID
};
struct in6_addr {
unsigned char s6_addr[16]; // IPv6 address
};
注:对于socket入门,可以暂时忽略 sin6_scope_id。
struct sockaddr_storage {
sa_family_t ss_family; // address family
// all this is padding, implementation specific, ignore it:
char __ss_pad1[_SS_PAD1SIZE];
int64_t __ss_align;
char __ss_pad2[_SS_PAD2SIZE];
};
根据ss_family为AF_INET 还是 AF_INET6,把sockaddr_storage强制转换为sockaddr_in 或者 sockaddr_in6。sockaddr_storage的用法,在下一篇代码分析里面可以看到。
struct sockaddr_in sa; // IPv4
struct sockaddr_in6 sa6; // IPv6
inet_pton(AF_INET, "10.12.110.57", &(sa.sin_addr)); // IPv4
inet_pton(AF_INET6, "2001:db8:63b3:1::3490", &(sa6.sin6_addr)); // IPv6
注意:这个函数失败的话,会返回-1或者0,记得要检查返回值,确保返回值是大于0的。
// IPv4:
char ip4[INET_ADDRSTRLEN]; // space to hold the IPv4 string
struct sockaddr_in sa; // pretend this is loaded with something
inet_ntop(AF_INET, &(sa.sin_addr), ip4, INET_ADDRSTRLEN);
printf("The IPv4 address is: %s\n", ip4);
// IPv6:
char ip6[INET6_ADDRSTRLEN]; // space to hold the IPv6 string
struct sockaddr_in6 sa6; // pretend this is loaded with something
inet_ntop(AF_INET6, &(sa6.sin6_addr), ip6, INET6_ADDRSTRLEN);
printf("The address is: %s\n", ip6);
注意:INET_ADDRSTRLEN 是一个预定义的宏,代表IPv4字符串长度。INET6_ADDRSTRLEN则代表IPv6的长度。
#include
#include
#include
int getaddrinfo(const char *node, // e.g. "www.example.com" or IP
const char *service, // e.g. "http" or port number
const struct addrinfo *hints,
struct addrinfo **res);
其中:
int status;
struct addrinfo hints;
struct addrinfo *servinfo; // 这个是输出参数
memset(&hints, 0, sizeof hints); // 很重要,一定要先将structure清0
hints.ai_family = AF_UNSPEC; // 这个参数表明不关心是IPv4还是IPv6,由系统自动选择。
hints.ai_socktype = SOCK_STREAM; // 采用TCP的socket
hints.ai_flags = AI_PASSIVE; // 由系统自动填充IP地址。
if ((status = getaddrinfo(NULL, "3490", &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(status));
exit(1);
}
// servinfo 现在指向的是一个链表,对应着一个或多个IP地址。
// ...
freeaddrinfo(servinfo); // 最后记得要把servinfo 释放掉。
#include
#include
int socket(int domain, int type, int protocol);
其中:
int s;
struct addrinfo hints, *res;
//...
getaddrinfo("www.example.com", "http", &hints, &res);
s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
#include
#include
int bind(int sockfd, struct sockaddr *my_addr, int addrlen);
其中:
struct addrinfo hints, *res;
int sockfd;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; // use IPv4 or IPv6, whichever
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; // fill in my IP for me
getaddrinfo(NULL, "3490", &hints, &res);
// make a socket:
sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
// bind it to the port we passed in to getaddrinfo():
bind(sockfd, res->ai_addr, res->ai_addrlen);
#include
#include
int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);
其中:
struct addrinfo hints, *res;
int sockfd;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
getaddrinfo("www.example.com", "3490", &hints, &res);
// make a socket:
sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
// connect!
connect(sockfd, res->ai_addr, res->ai_addrlen);
#include
#include
int listen(int sockfd, int backlog);
getaddrinfo();
socket();
bind();
listen();
/* accept() goes here */
#include
#include
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
其中:
#include
#include
#include
#include
#define MYPORT "3490" // the port users will be connecting to
#define BACKLOG 10 // how many pending connections queue will hold
int main(void)
{
struct sockaddr_storage their_addr;
socklen_t addr_size;
struct addrinfo hints, *res;
int sockfd, new_fd;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; // use IPv4 or IPv6, whichever
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; // fill in my IP for me
getaddrinfo(NULL, MYPORT, &hints, &res);
// make a socket, bind it, and listen on it:
sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
bind(sockfd, res->ai_addr, res->ai_addrlen);
listen(sockfd, BACKLOG);
// now accept an incoming connection:
addr_size = sizeof their_addr;
new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &addr_size);
// ready to communicate on socket descriptor new_fd!
.
.
.
int send(int sockfd, const void *msg, int len, int flags);
其中:
int recv(int sockfd, void *buf, int len, int flags);
其中:
char *msg = "Hello World!";
int len, bytes_sent;
...
len = strlen(msg);
bytes_sent = send(sockfd, msg, len, 0);
.
.
int sendto(int sockfd, const void *msg, int len, unsigned int flags, const struct sockaddr *to, socklen_t tolen);
其中:
int recvfrom(int sockfd, void *buf, int len, unsigned int flags, struct sockaddr *from, int *fromlen);
其中:
int close(int sockfd);
其中:
...
close(fd);