http://learn.akae.cn/media/ch36s01.html(协议栈)
关于网络序列
因为网络连接可能建立在big-endian和little-endian的机器之间。
详见这篇文章http://splayx.iteye.com/blog/1739223
所以端口(port)、地址(ip)有必要从主机的字符顺序转为网络的字符顺序,
网络的字符顺序作为不同机器之间通讯的中间载体。linux(<arpa/inet.h>)提供了4个转化的操作。
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
n: net bytes order
h: host bytes order
s: short, uint16_t
l: long, uint32_t
以上每个函数的作用是显而易见的。
下面是一个简单的TCP用到的一些函数。
#include <sys/socket.h>
int listen(int socket, int backlog);
backlog: the size of listening queue
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
domain:
AF_UNIX
File system pathnames.
AF_INET
Internet address.
---------------------------
type:
SOCK_STREAM
TCP
SOCK_DGRAM
UDP
SOCK_SEQPACKET
一条长记录可以通过几次接收,并有MSG_EOR作为结束标记
---------------------------
protocol:
如果不用其他协议,可以无视这项,为0就好了。
#include <sys/socket.h>
---------------------------
int accept(int socket, struct sockaddr *address,socklen_t *address_len);
---------------------------
int bind(int socket, const struct sockaddr *address,socklen_t address_len);
---------------------------
int connect(int socket, const struct sockaddr *address,socklen_t address_len);
因为不用协议地址的长度可能是不一样的,例如ipv4跟ipv6,
使得结构体sockaddr的大小有差异,所以需要address_len来指定长度。
最后使用这些接口建立一个简单的server-client的通讯。
server:
#include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <unistd.h> const int N = 1 << 20; char buffer[N]; int main() { int sockfd; // Create A socket. if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { fprintf(stderr, "Socket Error:%s\n\a", strerror(errno)); return 1; } // Info of server's addr. struct sockaddr_in srv_info; srv_info.sin_family = AF_INET; srv_info.sin_addr.s_addr = inet_addr("127.0.0.1"); srv_info.sin_port = htons(60530); bzero(srv_info.sin_zero, 8); // bind the socket to the server's addr if (bind(sockfd, (struct sockaddr*)&srv_info, sizeof(struct sockaddr)) == -1) { fprintf(stderr, "Bind Error:%s\n\a", strerror(errno)); return 1; } // listen to sockfd, with a listening queue of size 10. if (listen(sockfd, 10) == -1) { fprintf(stderr, "LIsten Error:%s\n\a", strerror(errno)); return 1; } socklen_t size = sizeof(struct sockaddr_in); struct sockaddr_in rmt_info; // Blocked wait for a connect to the server. int nsockfd = accept(sockfd, (struct sockaddr *)&rmt_info, &size); if (nsockfd == -1) { fprintf(stderr, "Accept Error:%s\n\a", strerror(errno)); return 1; } while (true) { printf("Msg to send:\n"); scanf("%s", buffer); write(nsockfd, buffer, strlen(buffer)); } close(nsockfd); close(sockfd); return 0; }
client:
#include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <unistd.h> const int N = 1 << 10; char buffer[N]; int main() { int sockfd; // Creat a socket for TCP. if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { fprintf(stderr, "Socket Error:%s\n\a", strerror(errno)); return 1; } // Info of server's addr. struct sockaddr_in srv_info; srv_info.sin_family = AF_INET; srv_info.sin_addr.s_addr = inet_addr("127.0.0.1"); srv_info.sin_port = htons(60530); bzero(srv_info.sin_zero, 8); // Use the sockfd to connect the server. // Failed, when the server doesn't set up. if (connect(sockfd, (struct sockaddr *)&srv_info, sizeof(struct sockaddr)) == -1) { fprintf(stderr, "Connect Error:%s\n\a", strerror(errno)); return 1; } int nbytes; // A blocked read from server // And nbytes' data actually read. while (true) { if ((nbytes = read(sockfd, buffer, N - 1)) == -1) { fprintf(stderr, "Read Error:%s\n", strerror(errno)); return 1; } buffer[nbytes] = '\0'; printf("Msg received:\n%s\n", buffer); } close(sockfd); return 0; }
server的输入
Msg to send:
hellosefise
Msg to send:
this is server speaking
Msg to send:
Msg to send:
Msg to send:
Msg to send:
client的输出
Msg received:
hellosefise
Msg received:
thisisserverspeaking
这里我们看到当server端输入够快时,client端一次性全收到了。
关于tcp内核的行为和用户具体应对策略,甚至更底层的tcp对ip协议的封装,
有很多有意思的地方值得我们去探究。
参考
http://pubs.opengroup.org/onlinepubs/7908799/xns/netinetin.h.html
http://pubs.opengroup.org/onlinepubs/7908799/xns/syssocket.h.html