segment
Packet
程序中的数据会先打包在TCP的segment
,然后TCP的segment
会被打包到IP的Packet
,然后再到以太网Ethernet
的Frame
,传递到对端后,各个层解析自己的协议,然后把数据交给更高层的协议处理
用户进程和内核之间通过系统提供的API来交流
WAN
)的用于获取时间服务
#include
#include
#include
#include
#include
#include
#include
#include
#define MAXLINE 4096
int main(int argc, char **argv){
int sockfd, n;
struct sockaddr_in servaddr;
char recvline[MAXLINE + 1];
if (argc != 2){
printf("usage: a.out " );
exit(-1);
}
// 创建套接字
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
printf("socket error");
exit(-1);
}
// 设置要连接的服务器属性
bzero(&servaddr, sizeof(servaddr)); // 清零
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(13); // 时间获取服务器的端口都是13
if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0){ // 将argv[1]转换为合适的格式: ipv4/ipv6
printf("inet_pton error for %s: %s", argv[1], strerror(errno));
exit(-1);
}
// 使用tcp套接字与指定服务器建立一个tcp连接
if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0){
printf("connect error: %s", strerror(errno));
exit(-1);
}
// read读取服务器应答: read返回0(表示对端关闭)/负数(表示读取错误)
while ( (n = read(sockfd, recvline, MAXLINE)) > 0) {
recvline[n] = 0; /* null terminate */
if (fputs(recvline, stdout) == EOF){ // 使用fputs输出到标准输出端
printf("fputs error");
exit(-1);
}
}
if (n < 0){
printf("read error");
exit(-1);
}
exit(0);
}
#include
#include
#include
#include
#include
#include
#include
#include
#define MAXLINE 4096
int main(int argc, char **argv){
int sockfd, n;
struct sockaddr_in6 servaddr;
char recvline[MAXLINE + 1];
if (argc != 2){
printf("usage: a.out " );
exit(-1);
}
// 创建套接字
if ((sockfd = socket(AF_INET6, SOCK_STREAM, 0)) < 0){
printf("socket error");
exit(-1);
}
// 设置要连接的服务器属性
bzero(&servaddr, sizeof(servaddr)); // 清零
servaddr.sin6_family = AF_INET6;
servaddr.sin6_port = htons(13); // 时间获取服务器的端口都是13
if (inet_pton(AF_INET, argv[1], &servaddr.sin6_addr) <= 0){ // 将argv[1]转换为合适的格式: ipv4/ipv6
printf("inet_pton error for %s: %s", argv[1], strerror(errno));
exit(-1);
}
// 使用tcp套接字与指定服务器建立一个tcp连接
if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0){
printf("connect error: %s", strerror(errno));
exit(-1);
}
// read读取服务器应答: read返回0(表示对端关闭)/负数(表示读取错误)
while ( (n = read(sockfd, recvline, MAXLINE)) > 0) {
recvline[n] = 0; /* null terminate */
if (fputs(recvline, stdout) == EOF){ // 使用fputs输出到标准输出端
printf("fputs error");
exit(-1);
}
}
if (n < 0){
printf("read error");
exit(-1);
}
exit(0);
}
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MAXLINE 4096
#define LISTENQ 1024
int main(int argc, char **argv){
int listenfd, connfd;
struct sockaddr_in servaddr;
char buff[MAXLINE];
time_t ticks;
listenfd = socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); // 这里指定了IP地址为INADDR_ANY,意思是如果服务器主机有多个网络接口,服务器进程就可以在任意网络上接受客户连接
servaddr.sin_port = htons(13);
printf("%d", htons(13));
if( bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0){
printf("bind error, %s", strerror(errno));
exit(0);
}
if(listen(listenfd, LISTENQ) < 0){
printf("listen error, %s", strerror(errno));
exit(0);
} // 监听listenfd,这样来自客户端的连接就可以在该套接字上由内核接受。 LISTENQ表示内核允许在这个监听描述符上排队的最大客户连接数
for ( ; ; ) {
if((connfd = accept(listenfd, (struct sockaddr *) NULL, NULL)) < 0){
printf("accept error, %s", strerror(errno));
continue;
}// 当前服务器进程会在accept上睡眠阻塞,直到某个客户断连接唤醒:TCP连接使用三次握手来建立连接,握手完毕accept返回一个已连接的描述符,我们就可以使用这个描述符与客户端通信了
//sleep(60);
ticks = time(NULL); // time返回一个int秒数
snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks)); // ctime将int转换成直观可读的时间格式,然后使用snprintf复制到buff中(最大不能超过sizeof(buff)个字符)
write(connfd, buff, strlen(buff)); // 将消息通过connfd写回给客户端
close(connfd); //关闭connfd: 该调用引发正常的TCP连接终止序列:每个方向上发送一个FIN,每个FIN又由各自的对端确认
printf("客户端连接关闭\n");
}
}
问:如果我们想要知道客户端的IP地址和端口,该怎么改写?
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MAXLINE 4096
#define LISTENQ 1024
int main(int argc, char **argv){
int listenfd, connfd;
struct sockaddr_in servaddr, cliaddr;
char buff[MAXLINE];
time_t ticks;
socklen_t len;
listenfd = socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); // 这里指定了IP地址为INADDR_ANY,意思是如果服务器主机有多个网络接口,服务器进程就可以在任意网络上接受客户连接
servaddr.sin_port = htons(13);
if( bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0){
printf("bind error, %s", strerror(errno));
exit(0);
}
if(listen(listenfd, LISTENQ) < 0){
printf("listen error, %s", strerror(errno));
exit(0);
} // 监听listenfd,这样来自客户端的连接就可以在该套接字上由内核接受。 LISTENQ表示内核允许在这个监听描述符上排队的最大客户连接数
for ( ; ; ) {
len = sizeof(cliaddr);
if((connfd = accept(listenfd, (struct sockaddr *) &cliaddr, &len)) < 0){
printf("accept error, %s", strerror(errno));
continue;
}
printf("connectio from %s, port %d\n",
inet_ntop(AF_INET, &cliaddr.sin_addr.s_addr, buff, sizeof(buff)),
ntohs(cliaddr.sin_port));
//sleep(60);
ticks = time(NULL); // time返回一个int秒数
snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks)); // ctime将int转换成直观可读的时间格式,然后使用snprintf复制到buff中(最大不能超过sizeof(buff)个字符)
write(connfd, buff, strlen(buff)); // 将消息通过connfd写回给客户端
close(connfd); //关闭connfd: 该调用引发正常的TCP连接终止序列:每个方向上发送一个FIN,每个FIN又由各自的对端确认
printf("客户端连接关闭\n");
}
}