Linux的核心思想之一 "一切皆文件"
1. 例如本地文件对一个字符串的"aaaaaaa"的读写是 open()返回的句柄 fd 作为载体, 调用 write 和 read 进行读写
而网络文件的和本地文件差不多但多了一些特性 用socket()返回的句柄 socket 传输读写数据前 要再创建一个sockaddr_in 对象 设置好协议族family 地址ip 端口prot 后 把设置好的sockaddr_in 对象利用 bind() 和 socket 绑定 ; 再用 listen()监听socket 有哪些连接和设置最大监听数量
完成上述一系列后可以调用 write 和 read 进行读写 , 也有一些特殊的: accept() connect() (setsocketopt()设置sendbuff和recvbuff的size)
2.内核的socket就是这样一个简单的结构 , 里面每种结构都是函数指针或变量的集合 ,
方便实现每种协议的接口函数名和最终目的相同,但是实现过程却不同的面向对象思想
在Linux内核里 ops是一些函数的集合如accept() connect()等 , file里是一些write() read() 等 , sk是 bind() listen() 等;
每种协议栈的接口函数名是相同的但是内部实现是不同的
3.在创建cocket对象时要告诉内核你要使用哪种协议 , socket()第一个参数添加ipv4/ipv6协议 第三个参数添加TCP/UDP协议也可以为0(默认具体依赖第二个参数) , 第二个参数,如果是流套接字的SOCK_ STREAM默认是TCP协议的 , 如果是数据包套接字SOCK_DGRAM默认是UDP ,还有个原始套接字SOCK_RAW是使用其他协议,具体要给第三个参数
4.TCP/IP协议
4.1分层概念 : mac ip tcp
物理层 MAC(源MAC 目标MAC 长度和类型L/T )------>ARP 解决链路和物理连接问题
网络层 IP 解决远程通信问题
传输层 TCP/UDP ---->端口(对应了本机的某个进程) 决定了数据传给哪个进程
应用层 HTTP HTTPS FTP TELNET SSH
4.2 ip格式 点分十进制 二进制
网络字节序(大端)0x12 0x34 0x56 x078 本地字节序(小端) 0x78 0x56 0x34 0x12
case 1 : 点分十进制 转换 网络字节序的二进制 有个专用函数 in_addr_t inet_addr(const char* cp);
case2 : 网络字节序的二进制 转换 点分十进制 char* inet_ntoa(struct in_addr_t in);
4.3子网掩码 用于确定网段的范围 24位能用0~255个ip地址 28位能用14个IP地址和一个255广播地址
24 == 32位二进制中有 24位是1 剩下的8位是0 (一个字节2位)
255.255.255.0 == 11111111.11111111.11111111.00000000
4.4 拆包和封包过程
5.TCP编程原理和流程
阻塞调用是指调用结果返回之前,当前线程会被挂起。函数只有在得到结果之后才会返回; connect 和 accept 都是阻塞函数
5.1 服务端 create socket
int sock = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); if(sock < 0) { perror("creat socket error\n"); return 1; } //sockaddr_in 设置时用 sockaddr_in 调用时强转成sockaddr struct sockaddr_in local; //设置family port ip local.sin_family = AF_INET; //htons h: honst n: network s:short 把本地端口转换成网络的 , atoi 把字符串转换成整型 ,ntohs和htons相反 local.sin_port = htons(atoi(argv[2])); local.sin_addr.s_addr = inet_addr(argv[1]); //inet_addr点分十进制转换网络二进制
bind()
if(bind(sock,(struct sockaddr*)&local,sizeof(local)) < 0) { perror("bind error\n"); close(sock); return 2; }
listen()
if(listen(sock,10) < 0){ perror("listen error\n"); close(sock); return 3; } printf("bind and listen success!wait accept...\n");
accept read write
struct sockaddr_in peer; //保存客户端信息 socklen_t len = sizeof(peer); while(1)
{ int fd = accept(sock,(struct sockaddr*)&peer ,&len); //阻塞函数 if(fd < 0){ perror("accept error\n"); close(sock); return 4; } printf("get connect,ip is : %s port is : %d\n",inet_ntoa(peer.sin_addr),ntohs(peer.sin_port)); pthread_t id; pthread_create(&id,NULL,thread_run,(void*)fd); pthread_detach(id);
}
close(sock);
void thread_run(void *arg)
{
printf("creat a new thread\n");
int fd = (int)arg;
char buf[1024];
while(1){
memset(buf,'\0',sizeof(buf));
ssize_t _s = read(fd,buf,sizeof(buf) - 1);
if(_s > 0){
buf[_s] = '\0';
printf("client say : %s\n",buf);
}
memset(buf,'\0',sizeof(buf));
printf("please Enter: ");
fflush(stdout); //清空堆里的printf内容
ssize_t _s2 = read(0,buf,sizeof(buf) - 1);
if(_s2 > 0){
write(fd,buf,strlen(buf));
}
}
}
5.2 客户端 creater socket
int sock = socket(AF_INET,SOCK_STREAM,0); if(sock < 0){ perror("socket error"); return 1; } struct sockaddr_in remote; remote.sin_family = AF_INET; remote.sin_port = htons(atoi(argv[2])); remote.sin_addr.s_addr = inet_addr(argv[1]);
connect
int ret = connect(sock,(struct sockaddr*)&remote,sizeof(remote)); if(ret < 0){ printf("connect failed:%s\n",strerror(errno)); return 2; } printf("connect success!\n");
read write
char buf[1024]; while(1){ memset(buf,'\0',sizeof(buf)); printf("please enter:"); fflush(stdout); ssize_t _s = read(0,buf,sizeof(buf)-1); if(_s > 0){ buf[_s - 1] = '\0'; write(sock,buf,strlen(buf)); _s = read(sock,buf,sizeof(buf)-1); if(_s > 0){ if(strncasecmp(buf,"quit",4) == 0){ printf("qiut\n"); break; } buf[_s -1] = '\0'; printf("%s\n",buf); } } } close(sock);