Linux系统编程笔记1
内容来自《unix环境高级编程》和《tcp/ip网络编程》,做些笔记整理一下,方便之后阅读。
tcp连接:可靠(一般不发生数据丢失),有序(先发先到),不存在边界 ----传送带模型
套接字约等于文件,fd文件描述符代表着文件编号,也是套接字的编号。每次系统分配的最小的未用的描述符
套接字内部有缓冲(字节数组),有可能在好几次向套接字write后,等缓冲区填满后直接调用一次read,也可能分多次调用read,如果发生从缓冲区read的速度跟不上导致缓冲区填满,此时不会发生数据丢失(tcp连接),这种情况下write将阻塞。
udp连接:快速(相对来说容易丢包),无序,有边界,限制每次传输大小(寄信模型)
三种类型的标准I/O缓冲
(1)全缓冲,填满缓冲区后才执行I/O操作,可以主动flush冲洗缓冲区
(2)行缓冲,遇到换行符执行I/O操作
(3)不带缓冲,不对字符进行缓冲存储,例如标准错误流stderr
TCP:
服务端:socket创建套接字->bind绑定套接字与ip地址和端口号->listen宣告可接收请求了->accept受理请求
客户端:socket创建套接字->connect连接服务端地址
bind绑定服务器中具体的IP地址,建造了服务器要和外界通讯的大门,listen把大门打开,连接请求可以来排队了,accept受理请求后分配一个专门用来和该客户端通信的fd,这个fd和客户端调用socket函数分配的fd组成一个pair对,准确来说是套接字组成pair,就像书中开头讲到的插头与插座的概念,由此形成一条通路,进行网络通信。这两个fd的配对我理解是通过ip地址+端口唯一的识别出了他们的彼此。因为accept的时候有传出参数来表示请求的地址结构。
IPV4 D类ip地址,前三字节表示网络id,第四字节表示主机id,先找网络地址,再找主机地址
大端序:高位字节存放低位地址
小端绪:高位字节存放高位地址
tcp/ip网络字节序指定大端序
htonl、htons、ntohl、ntohs(主机字节序与网络字节序的转换)
inet_addr(inet_aton)、inet_ntoa(二进制地址格式(网络字节序)与点分十进制字符表示的转换)
INADDR_ANY自动获取运行服务器端的计算机IP地址。
网络模型:物理层(光纤,电缆),数据链路层(网卡),网络层(IP、路由),传输层(TCP/UDP),应用层(HTTP等)
IP是面向消息的不可靠的协议。传输顺序与传输本身是不可靠的。
客户端只有在服务端listen后才能调用connect
tcp滑动窗口协议,write函数在数据移到输出缓冲时返回,如果此时关闭套接字也会继续传输输出缓冲中的数据,关闭套接字会丢失输入缓冲的数据
tcp经典的三次握手,四次挥手,syn,ack,fin等各个状态需要掌握,超时重传time-out,以及最后关闭连接前的time-wait
tcp在不可靠的IP层进行流控制(收发数据过程中为保证可靠性而添加的流控制),udp缺少这种流控制机制。udp的传输速度快,但在每次交换的数据量越大,tcp传输速度越接近udp的传输速度,因为tcp的时间主要用在建立连接,三次握手四次挥手和收发过程中的流控制(SEQ,ACK)当每次交换的数据量越大,连接的时间占比就越小,所以就越接近udp的传输速度。
传输压缩文件时必须使用tcp,传输多媒体数据,如音频视频时丢失一部分问题不大,更注重传输速度,所以选用udp,
tcp中的套接字时一一对应,先建立好连接
udp只有创建套接字和交换数据,客户端和服务端都只需要1个套接字,只要都有邮筒,就可以寄向各地,与之通信。(没有listen和accept)
每个套接字都会有一个ip地址和端口号,tcp连接时bind,accept,connect时都会分配,udp中通过bind,sendto,recvfrom都会自动分配
udp是就有边界的协议,调用几次输出就要调用几次输入来接收,不同于tcp可以一次读入
未连接的udp套接字(默认,所以每次sendto和recvfrom都要注册一下ip地址和端口在struct sockaddr_in)和有连接的udp套接字(先调用connect绑定目标ip和端口,然后直接read和write,在每次都给同一地址发消息时这样能省去每次都recvfrom和sendto时注册地址和删除地址信息的时间)
Nagle算法,最大限度进行缓冲,收到ACK后将缓冲的数据打包再发出去,尽量使用nagle算法,不使用nagle算法容易对网络流量造成负面影响。网络流量不受太大影响时可不使用nagle算法,传输快,无需等待ACK。
1 //tcp_server.cpp 2 3 #include<iostream> 4 #include<stdlib.h> 5 #include<string.h> 6 #include<unistd.h> 7 #include<arpa/inet.h> 8 #include<sys/socket.h> 9 using namespace std; 10 11 int main(int argc,char* argv[]) 12 { 13 int serv_sock,clnt_sock; 14 15 struct sockaddr_in serv_addr,clnt_addr; 16 socklen_t clnt_addr_size; 17 char message[] = "hello,world!"; 18 19 if(argc!=2){ 20 cout<<"usage "<< argv[0] <<" port"<<endl; 21 } 22 23 serv_sock = socket(AF_INET,SOCK_STREAM,0); 24 memset(&serv_addr,0,sizeof(serv_addr)); 25 serv_addr.sin_family = AF_INET; 26 serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); 27 serv_addr.sin_port = htons(atoi(argv[1])); 28 bind(serv_sock,(struct sockaddr*)&serv_addr,sizeof(serv_addr)); 29 listen(serv_sock,5); 30 clnt_addr_size = sizeof(clnt_addr); 31 clnt_sock = accept(serv_sock,(struct sockaddr*)&clnt_addr,&clnt_addr_size); 32 33 write(clnt_sock,message,sizeof(message)); 34 close(clnt_sock); 35 close(serv_sock); 36 return 0; 37 }
1 //tcp_client.cpp 2 3 #include<iostream> 4 #include<stdlib.h> 5 #include<string.h> 6 #include<unistd.h> 7 #include<arpa/inet.h> 8 #include<sys/socket.h> 9 using namespace std; 10 11 int main(int argc,char* argv[]) 12 { 13 int clnt_sock; 14 15 struct sockaddr_in serv_addr; 16 //socklen_t clnt_addr_size; 17 char message[30]; 18 19 if(argc!=3){ 20 cout<<"usage "<< argv[0]<<" <address> <port>"<<endl; 21 } 22 23 clnt_sock = socket(AF_INET,SOCK_STREAM,0); 24 memset(&serv_addr,0,sizeof(serv_addr)); 25 serv_addr.sin_family = AF_INET; 26 serv_addr.sin_addr.s_addr = inet_addr(argv[1]); 27 serv_addr.sin_port = htons(atoi(argv[2])); 28 //bind(serv_sock,(struct sockaddr*)&serv_addr,sizeof(serv_addr)); 29 //listen(serv_sock,5); 30 //clnt_addr_size = sizeof(clnt_addr); 31 connect(clnt_sock,(struct sockaddr*) &serv_addr,sizeof(serv_addr)); 32 int idx = 0; int read_len = 0; int count = 0; 33 while(read_len = read(clnt_sock,&message[idx],1)) //每次读一个字节 34 { 35 if(read_len == -1) 36 { 37 cout<<"read error!"<<endl; 38 } 39 idx++;count++; 40 } 41 cout<<"message from server: "<<message<<endl; 42 cout<<"read function call count: "<<count<<endl; 43 close(clnt_sock); 44 return 0; 45 }
1 //echo_server.cpp 2 3 #include<iostream> 4 #include<stdio.h> 5 #include<stdlib.h> 6 #include<string.h> 7 #include<unistd.h> 8 #include<arpa/inet.h> 9 #include<sys/socket.h> 10 using namespace std; 11 12 #define BUF_SIZE 1024 13 14 int main(int argc, char* argv[]) 15 { 16 int serv_sock, clnt_sock; 17 18 struct sockaddr_in serv_addr, clnt_addr; 19 socklen_t clnt_addr_size; 20 char message[BUF_SIZE]; 21 22 if (argc != 2) { 23 cout << "usage " << argv[0] << " port" << endl; 24 } 25 26 serv_sock = socket(AF_INET, SOCK_STREAM, 0); 27 memset(&serv_addr, 0, sizeof(serv_addr)); 28 serv_addr.sin_family = AF_INET; 29 serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); 30 serv_addr.sin_port = htons(atoi(argv[1])); 31 bind(serv_sock, (struct sockaddr*) & serv_addr, sizeof(serv_addr)); 32 listen(serv_sock, 5); 33 clnt_addr_size = sizeof(clnt_addr); 34 35 int str_len = 0; 36 for (int i = 0; i < 5; ++i) 37 { 38 clnt_sock = accept(serv_sock, (struct sockaddr*) & clnt_addr, &clnt_addr_size); 39 printf("connect client %d \n", i + 1); 40 while ((str_len = read(clnt_sock, message, BUF_SIZE)) != 0) 41 write(clnt_sock, message, str_len); 42 close(clnt_sock); 43 } 44 close(serv_sock); 45 return 0; 46 }
1 //echo_client.cpp 2 3 #include<iostream> 4 #include<stdio.h> 5 #include<stdlib.h> 6 #include<string.h> 7 #include<unistd.h> 8 #include<arpa/inet.h> 9 #include<sys/socket.h> 10 using namespace std; 11 12 #define BUF_SIZE 1024 13 14 int main(int argc, char* argv[]) 15 { 16 int clnt_sock; 17 struct sockaddr_in serv_addr; 18 char message[BUF_SIZE]; 19 20 if (argc != 3) { 21 cout << "usage " << argv[0] << " <address> <port>" << endl; 22 } 23 24 clnt_sock = socket(AF_INET, SOCK_STREAM, 0); 25 memset(&serv_addr, 0, sizeof(serv_addr)); 26 serv_addr.sin_family = AF_INET; 27 serv_addr.sin_addr.s_addr = inet_addr(argv[1]); 28 serv_addr.sin_port = htons(atoi(argv[2])); 29 30 connect(clnt_sock, (struct sockaddr*) & serv_addr, sizeof(serv_addr)); 31 puts("connected..."); 32 33 int str_len,recv_len,recv_cnt; 34 while (1) 35 { 36 fputs("input q to quit: ", stdout); 37 fgets(message, BUF_SIZE, stdin); //fgets会读入'\n' 38 if (message == "q\n" || message == "Q\n") 39 break; 40 41 str_len = write(clnt_sock, message, strlen(message)); 42 recv_len = 0; 43 while (recv_len < str_len) 44 { 45 recv_cnt = read(clnt_sock, &message[recv_len], BUF_SIZE - 1); 46 recv_len += recv_cnt; 47 } 48 message[BUF_SIZE] = '\0'; 49 printf("message from server: %s", message); 50 } 51 close(clnt_sock); 52 return 0; 53 }
1 //echo_server_udp.cpp 2 3 #include <stdio.h> 4 #include <stdlib.h> 5 #include <string.h> 6 #include <unistd.h> 7 #include <arpa/inet.h> 8 #include <sys/socket.h> 9 10 #define BUF_SIZE 30 11 12 int main(int argc,char* argv[]) 13 { 14 int sock; 15 struct sockaddr_in serv_addr,clnt_addr; 16 socklen_t clnt_size; //socklen_t 地址长度类型 17 char message[BUF_SIZE]; 18 //printf("hello"); 19 if (argc != 2) 20 { 21 printf("usage %s <port>\n", argv[0]); 22 exit(1); 23 } 24 25 printf("server start...\n");//printf向终端设备打印,是行缓冲的,加上'\n',才能立即看到显示 26 sock = socket(PF_INET, SOCK_DGRAM, 0); 27 if (sock == -1) 28 { 29 printf("socket error"); 30 exit(1); 31 } 32 memset(&serv_addr, 0, sizeof(serv_addr)); 33 serv_addr.sin_family = AF_INET; 34 serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); 35 serv_addr.sin_port = htons(atoi(argv[1])); 36 bind(sock,(struct sockaddr*)&serv_addr,sizeof(serv_addr)); 37 int str_len; 38 39 printf("socket created..\n"); 40 while (1) 41 { 42 clnt_size = sizeof(clnt_addr); 43 printf("start...\n"); 44 str_len = recvfrom(sock, message, BUF_SIZE, 0, (struct sockaddr*)&clnt_addr, &clnt_size); 45 printf("recv over\n"); 46 sendto(sock, message, str_len, 0, (struct sockaddr*)&clnt_addr, clnt_size); 47 printf("send over\n"); 48 } 49 close(sock); 50 return 0; 51 }
1 //echo_client_udp.cpp 2 3 #include <stdio.h> 4 #include <stdlib.h> 5 #include <string.h> 6 #include <unistd.h> 7 #include <arpa/inet.h> 8 #include <sys/socket.h> 9 10 #define BUF_SIZE 30 11 12 int main(int argc,char* argv[]) 13 { 14 int sock; 15 struct sockaddr_in serv_addr,from_addr; 16 char message[BUF_SIZE]; 17 if (argc != 3) 18 { 19 printf("usage %s <ip> <port>\n", argv[0]); 20 } 21 sock = socket(AF_INET, SOCK_DGRAM, 0); 22 memset(&serv_addr, 0, sizeof(serv_addr)); 23 serv_addr.sin_family = AF_INET; 24 //inet_addr(inet_aton)将点分十进制转换为二进制的网络字节序,此时不需要htonl,套了一层htonl后一直不能收发消息 25 serv_addr.sin_addr.s_addr = inet_addr(argv[1]); 26 serv_addr.sin_port = htons(atoi(argv[2])); 27 28 socklen_t addr_size = sizeof(from_addr); 29 int str_len; 30 while (1) 31 { 32 fputs("input message and press q to quit:\n", stdout); 33 //printf("%d\n",sizeof(message)); 34 fgets(message,sizeof(message), stdin); 35 //printf("%c\n", message[0]); 36 if (!strcmp(message, "q\n")) 37 break; 38 sendto(sock, message, strlen(message), 0, (struct sockaddr*) &serv_addr, sizeof(serv_addr)); 39 printf("send over!\n"); 40 str_len = recvfrom(sock, message, BUF_SIZE, 0, (struct sockaddr*) &from_addr, &addr_size); 41 printf("recv over!\n"); 42 message[str_len] = '\0'; 43 printf("Message from other: %s", message); 44 } 45 close(sock); 46 return 0; 47 }