sockets 可以说是 standard Unix file descriptors。 read() write() 也可以,但是send() and recv() 会有更好的控制。A file descriptor is simply an integer associated with an open file. But (and here’s the catch), that file can be a network connection, a FIFO, a pipe, a terminal, a real on-the-disk file, or just about anything else. Everything in Unix is a file!
两种 Internet Sockets
Stream sockets 就是用的 TCP (the transimission control protocol)。IP 代表的是 Internet Protocol。 Datagram sockets 用 UDP (User Datagram protocol)。 tftp (trivial file transfer protocol, a little brother to FTP), dhcpcd (a DHCP client), multiplayer games, streaming audio, video conferencing, etc.
该函数将IP地址转换为 sockaddr_in 结构。 “pton” stands for “presentation to network”—you can call it “printable to network” if that’s easier to remember. 以前这个功能的函数叫 inet_addr 或者是 inet_aton, 但是用不了 ipv6 了。
inet_ntop() “ntop” means “network to presentation”
1 2 3 4
char ip4[INET_ADDRSTRLEN]; // space to hold the IPv4 string structsockaddr_insa;// pretend this is loaded with something inet_ntop(AF_INET, &(sa.sin_addr), ip4, INET_ADDRSTRLEN); printf("The IPv4 address is: %s\n", ip4);
getaddrinfo() 可以得到IP地址。
socket()
1 2 3
#include<sys/types.h> #include<sys/socket.h> intsocket(int domain, int type, int protocol);
参数可以硬编码,(domain is PF_INET or PF_INET6, type is SOCK_STREAM or SOCK_DGRAM, and protocol can be set to 0 to choose the proper protocol for the given type 也可以用getprotobyname() 传”TCP” “UDP”
1 2 3 4 5 6 7 8 9 10
int s; structaddrinfohints, *res; // do the lookup // [pretend we already filled out the "hints" struct] getaddrinfo("www.example.com", "http", &hints, &res); // [again, you should do error-checking on getaddrinfo(), and walk // the "res" linked list looking for valid entries instead of just // assuming the first one is good (like many of these examples do.) // See the section on client/server for real examples.] s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
socket 调用成功则返回 a socket descriptor, 失败返回-1。 全局变量 errno 将会被设置。
#include<sys/types.h> #include<sys/socket.h> intbind(int sockfd, struct sockaddr *my_addr, int addrlen);
sockfd 就是 a socket descriptor, my_addr 需要指向 struct sockaddr,addrlen 是地址的字节数的长度
1 2 3 4 5 6 7 8 9 10 11 12
structaddrinfohints, *res; int sockfd; // first, load up address structs with getaddrinfo(): 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);
如果已经被绑定了
1 2 3 4 5
// lose the pesky "Address already in use" error message if (setsockopt(listener,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof yes) == -1) { perror("setsockopt"); exit(1); }
#include<sys/types.h> #include<sys/socket.h> intconnect(int sockfd, struct sockaddr *serv_addr, int addrlen);
sockfd is our friendly neighborhood socket file descriptor, as returned by the socket() call, serv_addr is a struct sockaddr containing the destination port and IP address, and addrlen is the length in bytes of the server address structure.
建立一个socket 连接:
1 2 3 4 5 6 7 8 9 10 11 12
structaddrinfohints, *res; int sockfd; // first, load up address structs with getaddrinfo(): 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);
#define MYPORT "3490"// the port users will be connecting to #define BACKLOG 10 // how many pending connections queue will hold
intmain(void) { structsockaddr_storagetheir_addr; socklen_t addr_size; structaddrinfohints, *res; int sockfd, new_fd;
// !! don't forget your error checking for these calls !!
// first, load up address structs with getaddrinfo():
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
The function getpeername() will tell you who is at the other end of a connected stream socket
1 2
#include<sys/socket.h> intgetpeername(int sockfd, struct sockaddr *addr, int *addrlen);
sockfd 已经连接的sfd, addr 指向 sockaddr 的指针, addrlen -> sizeof *addr. inet_ntop(), getnameinfo(), or gethostbyaddr() 可以得到更多信息. The function returns -1 on error and sets errno accordingly.
gethostname()
It returns the name of the computer that your program is running on
hostname is a pointer to an array of chars that will contain the hostname upon the function’s return, and size is the length in bytes of the hostname array. The function returns 0 on successful completion, and -1 on error, setting errno as usual.
Client-Server Background
there will only be one server on a machine, and that server will handle multiple clients using fork(). The basic routine is: server will wait for a connection, accept() it, and fork() a child process to handle it. This is what our sample server does in the next section.
/* ** server.c -- a stream socket server demo */ #include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<errno.h> #include<string.h> #include<sys/types.h> #include<sys/socket.h> #include<netinet/in.h> #include<netdb.h> #include<arpa/inet.h> #include<sys/wait.h> #include<signal.h> #define PORT "3490"// the port users will be connecting to #define BACKLOG 10 // how many pending connections queue will hold
voidsigchld_handler(int s){ // waitpid() might overwrite errno, so we save and restore it: int saved_errno = errno; while (waitpid(-1, NULL, WNOHANG) > 0); errno = saved_errno; } // get sockaddr, IPv4 or IPv6: void * get_in_addr(struct sockaddr * sa){ if (sa -> sa_family == AF_INET) { return &(((struct sockaddr_in * ) sa) -> sin_addr); } return &(((struct sockaddr_in6 * ) sa) -> sin6_addr); } intmain(void){ int sockfd, new_fd; // listen on sock_fd, new connection on new_fd structaddrinfohints, * servinfo, * p; structsockaddr_storagetheir_addr;// connector's address information socklen_t sin_size; structsigactionsa; int yes = 1; char s[INET6_ADDRSTRLEN]; int rv; memset( & hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; // use my IP if ((rv = getaddrinfo(NULL, PORT, & hints, & servinfo)) != 0) { fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv)); return1; } // loop through all the results and bind to the first we can for (p = servinfo; p != NULL; p = p -> ai_next) { if ((sockfd = socket(p -> ai_family, p -> ai_socktype, p -> ai_protocol)) == -1) { perror("server: socket"); continue; } if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, & yes, sizeof(int)) == -1) { perror("setsockopt"); exit(1); } if (bind(sockfd, p -> ai_addr, p -> ai_addrlen) == -1) { close(sockfd); perror("server: bind"); continue; } break; } freeaddrinfo(servinfo); // all done with this structure if (p == NULL) { fprintf(stderr, "server: failed to bind\n"); exit(1); } if (listen(sockfd, BACKLOG) == -1) { perror("listen"); exit(1); } sa.sa_handler = sigchld_handler; // reap all dead processes sigemptyset( & sa.sa_mask); sa.sa_flags = SA_RESTART; if (sigaction(SIGCHLD, & sa, NULL) == -1) { perror("sigaction"); exit(1); } printf("server: waiting for connections...\n"); while (1) { // main accept() loop sin_size = sizeof their_addr; new_fd = accept(sockfd, (struct sockaddr * ) & their_addr, & sin_size); if (new_fd == -1) { perror("accept"); continue; } inet_ntop(their_addr.ss_family, get_in_addr((struct sockaddr * ) & their_addr), s, sizeof s); printf("server: got connection from %s\n", s); if (!fork()) { // this is the child process close(sockfd); // child doesn't need the listener if (send(new_fd, "Hello, world!", 13, 0) == -1) perror("send"); close(new_fd); exit(0); } close(new_fd); // parent doesn't need this } return0; }
intmain(void) { int sockfd; structaddrinfohints, *servinfo, *p; int rv; int numbytes; structsockaddr_storagetheir_addr; char buf[MAXBUFLEN]; socklen_t addr_len; char s[INET6_ADDRSTRLEN];
memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; // set to AF_INET to force IPv4 hints.ai_socktype = SOCK_DGRAM; hints.ai_flags = AI_PASSIVE; // use my IP
// loop through all the results and bind to the first we can for(p = servinfo; p != NULL; p = p->ai_next) { if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) { perror("listener: socket"); continue; }
// loop through all the results and make a socket for(p = servinfo; p != NULL; p = p->ai_next) { if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) { perror("talker: socket"); continue; }
break; }
if (p == NULL) { fprintf(stderr, "talker: failed to create socket\n"); return2; }
FD_SET(int fd, fd_set set); Add fd to the set. FD_CLR(int fd, fd_set set); Remove fd from the set. FD_ISSET(int fd, fd_set set); Return true if fd is in the set. FD_ZERO(fd_set set); Clear all entries from the set.
struct timeval 相当于超时设置.
1 2 3 4
structtimeval { int tv_sec; // seconds int tv_usec; // microseconds };