基本概念
套接口也就是网络中的ID。网络通信,归根到底还是进程间通信(不同计算机上的进程间的通信)。在网络中,每一个节点(计算机或路由器)都有一个网络地址,也就是IP地址。
- IP地址:在网络中唯一标识一台主机,32
- 端口号:在主机中唯一标识一个进程,16
- IP+port:在网络环境中唯一标识一个进程(socket)
socket也称套接字,是linux文件的一种类型(伪文件:不占存储空间的文件。linux 有7中文件, 1.普通文件 2.目录(d)3.字符设备文件(c) 4.块设备文件(b) 5.套接口文件(s)[如我们开启MySQL服务后,在/var/lib/mysql/下生成的mysql.sock文件,关闭MySQL服务后,这个文件就消失了],6.管道(p):有两端,读端 r:流出 fd[0] 写段w:流入 fd[1] 7.符号链接文件(l)[有点儿像WIN下的快捷方式] 其中占存储的有三种:普通,目录,软链接)
socket成对出现,
一个文件描述符指向两个缓冲区,一个读一个写。
预备知识 、
网路字节序:
TCP/IP规定,网路数据流应采用大端字节序,即低地址高字节。(大端:低地址--高位 高地址--地位 小端:低--低 高--高,即端口号和IP地址都是以网路字节序存储的,网络字节序使用的是大端模式。称给某个系统所采用的字节序为主机字节序,它可能是小端模式也可能是大端模式。
为使网络编程具有可移植性,使同样的C代码在大端和小端计算机上编译后能正常运行,可以调用一下库函数做网络字节序和主机字节序的转换。
#include<arpa/inet.h> uint32_t htoal(uint32_t hostlong); uint16_t htons(uint16_t hostshort); //返回的是网络字节序 uint32_t ntoh1(uint32_t netlong); uint16_t ntohs(uint16_t netshort); //返回的主机字节序 //h表示host,to,n代表network,s代表short,l表示long //端口16位 ,ip 32位
套接口的数据结构
在Linux中,每一种协议都有自己的网络地址数据结构,以sockaddr_开头,如IPV4对应的是sockaddr_in.
//man 7 ip 查看 struct sockaddr_in { sa_family_t sin_family; /* address family: AF_INET */ /*套接口结构地址族,如IPV4为AF_INET*/ in_port_t sin_port; /* port in network byte order */ /*16位TCP或UDP端口号,网络字节顺序*/ struct in_addr sin_addr; /* internet address */ }; /* Internet address. */ struct in_addr { uint32_t s_addr; /* address in network byte order */ };
C/S编程
服务器是指在网络上提供服务的应用程序,客户机是指用户为了得到某种服务所需要 运行的应用程序,即申请服务的程序。一个服务器程序可以同时接收多个客户机的请求。
server.c: socket() 建立套接字 bind() 绑定IP 端口号(struct sockaddr_in addr 初始化) listen() 指定最大同时发起连接数 accept() 阻塞等待客户端发起连接 read() 小--大 write 给客户端 close();
client.c:
socket();
bind();可以依赖“隐式绑定”,即写不写都行
connect();发起连接
write();
read();
close();
1 #include<stdio.h> 2 #include<unistd.h> 3 #include<sys/socket.h> 4 #include<stdlib.h> 5 #include<arpa/inet.h> 6 #include<ctype.h> 7 #include<strings.h> 8 #define SERV_PORT 6666 //端口号 9 int main(void) 10 { 11 int lfd,cfd;//定义套接口描述符 12 struct sockaddr_in serv_addr;//定义服务器IPV4套接口地址数据结构serv_addr 13 struct sockaddr_in clie_addr;//d定义客户端client 14 socklen_t clie_addr_len; 15 char buf[BUFSIZ],clie_IP[BUFSIZ];//用BUFSIZ就不需要自己定义宏了 16 int i,n; 17 lfd=socket(AF_INET,SOCK_STREAM,0);//AF_INET:IPV4 AF_INET6:IPV6 18 bzero(&serv_addr,sizeof(serv_addr));//清0,memset清任何数 19 serv_addr.sin_family=AF_INET;//设置addr的成员信息 20 serv_addr.sin_port=htons(SERV_PORT);//本地字节序转换为网络字节序 21 serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);//IP地址设为本地IP INADDR_ANY本地任意IP 22 23 bind(lfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr));//绑定端口 24 25 listen(lfd,128);//最多可以接受128个客户端的请求,监听ifd描述符 26 27 clie_addr_len=sizeof(clie_addr); 28 cfd=accept(lfd,(struct sockaddr *)&clie_addr,&clie_addr_len);//bind accept connect 需要强转 29 printf("client IP:%s,client port:%d ",inet_ntop(AF_INET,&clie_addr.sin_addr,clie_IP,sizeof(clie_IP)),ntohs(clie_addr.sin_port)); 30 while(1){ 31 n= read(cfd,buf,sizeof(buf)); 32 for(i=0;i<n;i++) 33 { 34 buf[i]=toupper(buf[i]);//下写转换为大写 35 } 36 write(cfd,buf,n); 37 } 38 close(lfd); 39 close(cfd); 40 41 return 0; 42 43 }
1 #include<stdio.h> 2 #include<unistd.h> 3 #include<sys/socket.h> 4 #include<stdlib.h> 5 #include<arpa/inet.h> 6 #include<ctype.h> 7 #include<string.h> 8 #define SERV_PORT 6666 9 #define SERV_IP "127.0.0.1 " 10 int main(void) 11 { 12 int cfd; 13 int n; 14 char buf[BUFSIZ]; 15 struct sockaddr_in serv_addr; 16 // socklen_t serv_addr_len; 17 cfd = socket(AF_INET,SOCK_STREAM,0); 18 memset(&serv_addr,0,sizeof(serv_addr)); 19 serv_addr.sin_family=AF_INET;//设置addr的成员信息 20 serv_addr.sin_port=htons(SERV_PORT);//本地字节序转换为网络字节序 21 inet_pton(AF_INET,SERV_IP,&serv_addr.sin_addr.s_addr); 22 // serv_addr.sin_addr=*((struct in_addr *)host->haddr); 23 connect(cfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr));//客户端程序发起连接请求 24 while(1) 25 { 26 fgets(buf,sizeof(buf),stdin);//hello world ----fgets---"hello world "从键盘写 27 write(cfd,buf,strlen(buf)); 28 n=read(cfd,buf,sizeof(buf)); 29 write(STDOUT_FILENO,buf,n); 30 } 31 close(cfd); 32 return 0; 33 }