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