基本概念
套接口也就是網絡中的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\n",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\n\0"從鍵盤寫 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 }