日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

TCP/IP网络编程复习(上)

發布時間:2023/12/14 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 TCP/IP网络编程复习(上) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

第一章 理解網絡編程和套接字

#include<sys/socket.h> int socket(int domain, int type , int protocol); //socket返回文件描述符或-1 int bind(int sockfd , struct sockaddr *myaddr, socklen_t addrlen); //bind返回0或-1 int listen(int sockfd,int backlog); //listen返回0或-1 int accept(int sockfd,struct sockaddr *addr,socklen_t *addrlen); //返回文件描述符或-1 ##以上為服務器端 編譯指令:gcc hello_server.c -o hserver
  • Linux下socket被認為是文件的一種,windows區分socket和文件
  • 0,1,2這三個文件描述符(方便描述文件)為STDIN STDOUT STDERR
int open(const char *path, int flag); //返回fd或-1 int close(int fd); //返回0或-1 ssize_t write(int fd,const void *buf , size_t nbytes); //寫入文件,返回成功寫入的字節數或-1 ssize_t read(int fd,void *buf,size_t nbytes); //返回成功接收的字節數或-1
  • windows在這方面的函數與linux差不多,不過套接字有專屬類型SOCKET

第二章 套接字類型與協議設置

  • int socket(int domain, int type , int protocol);
    第一個參數為協議族有PF_INET PF_INET6等;第二個參數為套接字類型:有SOCK_STREAM(傳送帶傳糖果,面向連接,無數據邊界,可靠有序)和SOCK_DGRAM)(摩托車,不可靠,無序,以數據的高速傳輸為目的的套接字);最后一個參數默認情況是0

第三章 地址族與數據序列

  • IPV4傳數據是先向網絡號傳輸數據(傳到路由器,交換機),再由這個網絡具體傳給相應主機號的主機
  • A類地址首字節范圍:0-127 B:128-191 C:192-223
  • NIC(網絡接口卡)為數據傳輸設備,os會將傳遞到內部的數據分配給套接字,這時需要用到端口號,NIC接收的數據內有端口號,os參考此端口號把數據傳給對應套接字;端口號范圍0-65535,但0-1023為知名端口
struct sockaddr_in //本來就是給IPV4設計的 {sa_family_t sin_family; //地址族,有AF_INET;AF_INET6;AF_LOCAL,有這一欄是因為要和sockaddr這個老版本的結構體保持一致性uint16_t sin_port; //16位TCP/UDP端口號struct in_addr sin_addr; //32位IP地址char sin_zero[8]; //不使用 } struct in_addr {in_addr_t s_addr; //32位IP地址,為uint32_t }struct sockaddr {sa_family_t sin_family;char sa_data[14]; }
  • 主機字節序一般為小端序,網絡字節序為大端序
unsigned short htons(unsigned short); unsigned short ntohs(unsigned short); unsigned long htonl(unsigned long); unsigned long htonl(unsigned long);
  • 數據在傳輸之前都需要經過轉換嗎:這個過程是自動的,除了向sockaddr_in結構體變量填充數據外,其他情況無需考慮字節序問題
in_addr_t inet_addr(const char * string); ##成功時返回32位大端序整數數值,失敗時返回INADDR_NONE int inet_aton(const char *string, struct in_addr * addr) ##成功返回1,失敗返回0,和上面函數作用一樣,不過可以直接給他弄到in_addr結構體里 char * inet_ntoa(struct in_addr adr); ##返回char*-1;需要將得到的char * 保存到新的內存空間里,否則下次用這個函數,他返回的char *就會變成新的 addr.sin_addr.s_addr = htonl(INADDR_ANY) ##自動分配本機IP地址,一般服務端才用這個
  • p52有兩個不是特別清楚的點,到時候可以回頭看看

第四章 基于TCP的服務器端/客戶端(1)

  • listen中的sock成為服務器端套接字,是接收連接或請求的保安
  • accept會內部產生一個用于數據I/O的套接字,調用accept時,若等待隊列為空,則accept函數不會返回,直到隊列中出現新的客戶端連接
int connect(int sock, struct sockaddr * servaddr, socklen_t addrlen); ##客戶端調用connect后只有服務器端接收連接請求(服務器端接收他到等待隊列)或發生斷網等異常情況 而中斷連接請求才會使connect返回;客戶端的IP地址和端口在調用connect時自動分配,不用再用bind分 配

agrv agrc參數的描述

###echo服務端 #include <stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h> #include<arpa/inet.h> #include<sys/socket.h>#define BUF_SIZE 1024 void error_handling(char *message);int main(int argc,char *agrv[]) {int serv_sock,clnt_sock;char message[BUF_SIZE];int str_len,i;struct sockaddr_in serv_adr,clnt_adr;socklen_t clnt_adr_sz;if(argc!=2){printf("Usage : %s <PORT>\n", agrv[0]);exit(1);}serv_sock=socket(PF_INET,SOCK_STREAM,0);if(serv_sock==-1)error_handling("socket() error");memset(&serv_adr,0,sizeof(serv_adr));serv_adr.sin_family=AF_INET;serv_adr.sin_addr.s_addr=htonl(INADDR_ANY);serv_adr.sin_port=htons(atoi(agrv[1]));if(bind(serv_sock,(struct sockaddr*)&serv_adr,sizeof(serv_adr))==-1)error_handling("bind() error");if(listen(serv_sock,5)==-1)error_handling("listen() error");clnt_adr_sz=sizeof(clnt_adr);for(int i=0; i<5; i++){clnt_sock=accept(serv_sock,(struct sockaddr*)&clnt_adr,&clnt_adr_sz);if(clnt_sock==-1)error_handling("accept() error");elseprintf("Connected client %d \n",i+1);while((str_len=read(clnt_sock,message,BUF_SIZE))!=0)write(clnt_sock,message,str_len);close(clnt_sock);}close(serv_sock);return 0; } void error_handling(char *message) {fputs(message,stderr);fputc('\n',stderr);exit(1); }


gcc編譯顯示警告

##echo客戶端,這個客戶端存在數據邊界的問題,因為他默認是以字符串為單位傳遞數據的, 忽略了字符串過長和傳遞過程過遠數據累積的情況 #include <stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h> #include<arpa/inet.h> #include<sys/socket.h>#define BUF_SIZE 1024 void error_handling(char *message);int main(int argc,char *agrv[]) {int sock;char message[BUF_SIZE];int str_len;struct sockaddr_in serv_adr;if(argc!=3){printf("Usage : %s <IP> <PORT>\n", agrv[0]);exit(1);}sock=socket(PF_INET,SOCK_STREAM,0);if(sock==-1)error_handling("socket() error");memset(&serv_adr,0,sizeof(serv_adr));serv_adr.sin_family=AF_INET;serv_adr.sin_addr.s_addr=inet_addr(agrv[1]);serv_adr.sin_port=htons(atoi(agrv[2]));if(connect(sock,(struct sockaddr*)&serv_adr,sizeof(serv_adr))==-1)error_handling("connect() error");elseputs("Connected............");while(1){fputs("Input message(Q to quit): ",stdout);fgets(message,BUF_SIZE,stdin);if(!strcmp(message,"q\n")||!strcmp(message,"Q\n"))break;write(sock,message,strlen(message));str_len=read(sock,message,BUF_SIZE-1);message[str_len]=0;printf("Message from server: %s",message);}close(sock);return 0; } void error_handling(char *message) {fputs(message,stderr);fputc('\n',stderr);exit(1); }

第五章 基于TCP的服務器端/客戶端(2)

##完美的回聲客戶端 #include <stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h> #include<arpa/inet.h> #include<sys/socket.h>#define BUF_SIZE 1024 void error_handling(char *message);int main(int argc,char *agrv[]) {int sock;char message[BUF_SIZE];int str_len,recv_len,recv_cnt;struct sockaddr_in serv_adr;if(argc!=3){printf("Usage : %s <IP> <PORT>\n", agrv[0]);exit(1);}sock=socket(PF_INET,SOCK_STREAM,0);if(sock==-1)error_handling("socket() error");memset(&serv_adr,0,sizeof(serv_adr));serv_adr.sin_family=AF_INET;serv_adr.sin_addr.s_addr=inet_addr(agrv[1]);serv_adr.sin_port=htons(atoi(agrv[2]));if(connect(sock,(struct sockaddr*)&serv_adr,sizeof(serv_adr))==-1)error_handling("connect() error");elseputs("Connected............");while(1){fputs("Input message(Q to quit): ",stdout);fgets(message,BUF_SIZE,stdin);if(!strcmp(message,"q\n")||!strcmp(message,"Q\n"))break;str_len=write(sock,message,strlen(message));recv_len=0;while(recv_len<str_len){recv_cnt=read(sock,message,BUF_SIZE-1);if(recv_cnt==-1)error_handling("read() error");recv_len+=recv_cnt;}message[str_len]=0;printf("Message from server: %s",message);}close(sock);return 0; } void error_handling(char *message) {fputs(message,stderr);fputc('\n',stderr);exit(1); }
  • I/O緩沖在每個TCP套接字中單獨存在;I/O緩沖在創建套接字時自動生成;即使關閉套接字也會繼續傳遞輸入緩沖中遺留的數據;關閉套接字將丟失輸入緩沖中的數據
  • TCP中因為TCP會控制數據流(Sliding Window協議),不會發生超過輸入緩沖大小的傳輸數據
  • write函數并不會在完成向對方主機的數據傳輸時返回,而是在數據移到輸出緩沖時,但TCP會保證對輸出緩沖數據的傳輸

第六章 基于UDP的服務器端/客戶端

  • UDP提供不可靠的數據傳輸服務,在重視性能而非可靠性的情況下,UDP是一種很好的選擇
  • UDP最重要的作用就是根據端口號將傳到主機的數據包交付給最終的UDP套接字,TCP中套接字是一對一的關系,而UDP每方只需要一個,收發信件的郵箱可以比喻成UDP套接字
##UDP的接收發送函數,與TCP的重大區別是每次發送都需要指定對方的socket ssize_t sendto(int sock, void *buff, size_t nbytes, int flags, struct sockaddr * to , socklen_t addrlen); ssize_t recvfrom(int sock, void * buff,size_t nbytes, int flags, struct sockaddr * from, socklen_t *addrlen); ##UDP回聲server端 #include <stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h> #include<arpa/inet.h> #include<sys/socket.h>#define BUF_SIZE 30 void error_handling(char *message);int main(int argc,char *agrv[]) {int serv_sock;char message[BUF_SIZE];int str_len;struct sockaddr_in serv_adr,clnt_adr;socklen_t clnt_adr_sz;if(argc!=2){printf("Usage : %s <PORT>\n", agrv[0]);exit(1);}serv_sock=socket(PF_INET,SOCK_DGRAM,0);if(serv_sock==-1)error_handling("socket() error");memset(&serv_adr,0,sizeof(serv_adr));serv_adr.sin_family=AF_INET;serv_adr.sin_addr.s_addr=htonl(INADDR_ANY);serv_adr.sin_port=htons(atoi(agrv[1]));if(bind(serv_sock,(struct sockaddr*)&serv_adr,sizeof(serv_adr))==-1)error_handling("bind() error");while(1){clnt_adr_sz=sizeof(clnt_adr);str_len=recvfrom(serv_sock,message,BUF_SIZE,0,(struct sockaddr*)&clnt_adr,&clnt_adr_sz);sendto(serv_sock,message,str_len,0,(struct sockaddr*)&clnt_adr,clnt_adr_sz);}close(serv_sock);return 0; } void error_handling(char *message) {fputs(message,stderr);fputc('\n',stderr);exit(1); } ##UDP回聲客戶端 #include <stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h> #include<arpa/inet.h> #include<sys/socket.h>#define BUF_SIZE 30 void error_handling(char *message);int main(int argc,char *agrv[]) {int sock;char message[BUF_SIZE];int str_len;socklen_t adr_sz;struct sockaddr_in serv_adr, from_adr;if(argc!=3){printf("Usage : %s <IP> <PORT>\n", agrv[0]);exit(1);}sock=socket(PF_INET,SOCK_DGRAM,0);if(sock==-1)error_handling("socket() error");memset(&serv_adr,0,sizeof(serv_adr));serv_adr.sin_family=AF_INET;serv_adr.sin_addr.s_addr=inet_addr(agrv[1]);serv_adr.sin_port=htons(atoi(agrv[2]));while(1){fputs("Input message(Q to quit): ",stdout);fgets(message,BUF_SIZE,stdin);if(!strcmp(message,"q\n")||!strcmp(message,"Q\n"))break;sendto(sock,message,strlen(message),0,(struct sockaddr*)&serv_adr,sizeof(serv_adr));adr_sz=sizeof(from_adr);str_len=recvfrom(sock,message,BUF_SIZE,0,(struct sockaddr*)&from_adr,&adr_sz);message[str_len]=0;printf("Message from server: %s",message);}close(sock);return 0; } void error_handling(char *message) {fputs(message,stderr);fputc('\n',stderr);exit(1); }
  • UDP存在數據邊界,一個數據包即可成為一個完整數據,UDP通信過程中使用I/O函數調用次數應保持一致
  • 通過sendto傳遞數據分為3個階段:1.向UDP套接字注冊目標IP和端口號;2.傳輸數據;3.刪除UDP套接字中注冊的目標地址信息。這種未注冊目標地址信息的套接字稱為未連接套接字,與此相對的還有已連接套接字(可以避免了1,3階段的過程)
  • 創建已連接UDP套接字:connect(sock,(struct sockaddr*)&adr,sizeof(adr))就是調用了connect函數,向UDP套接字注冊目標IP和端口信息,注冊后可以使用write,read和sendto,recvfrom

第七章 優雅地斷開套接字連接

  • TCP的半關閉:兩臺主機通過套接字建立連接后進入可交換數據的狀態,又稱“流形成的狀態”,此時有I/O流1和I/O流2,close函數會同時斷開這兩個流,不夠優雅(發送完數據可能就調用close函數,而此時可能還有對面發過來讓你必須接收的數據,這種數據就被損毀了)
int shutdown(int sock, int howto); ##成功返回0,失敗返回-1 ##howto的值:SHUT_RD斷開輸入流;SHUT_WR斷開輸出流(輸出緩沖的數據依然會傳遞);SHUT_RDWR同時斷開I/O流
  • 服務器端向客戶端傳送文件時,客戶端不知道要接收到什么時候,也不能一直調用read函數(會阻塞),所以約定一個EOF標志文件傳輸結束

第八章 域名及網絡地址

  • DNS將域名轉換為IP
  • IP地址比域名發生變更的概率更高,用域名比較好一些
##成功返回hostent指針,失敗返回空指針,利用域名查IP struct hostent * gethostbyname(const char * hostname);struct hostent {char *h_name ; ##官方域名char ** h_aliases ; ##一個IP可以綁定多個域名int h_addrtype ; ##IPV4 IPV6之類的int h_length ; ##IP地址長度char ** h_addr_list ; ##以整數形式保存域名對應的IP地址 }
  • 對于上面的h_addr_list,他指向字符串指針數組,但字符串指針數組中的元素實際指向的是(實際保存的是)in_addr結構體變量地址值;這里面用char **是因為它為多門協議所用,指針類型不確定,而當時又沒有void *,就用char *表示無法確定的指針類型
##利用IP地址查域名 struct hostent * gethostbyaddr(const char * addr , socklen_t len,int family);

第九章 套接字的多種可選項

int getsockopt(int sock,int level, int optname, void *optval,socklen_t *optlen); ##optval為保存查看結果的緩沖地址值;optlen保存通過第四個參數返回的可選項信息的字節數 int setsockopt(int sock,int level,int optname,const void* optval,socklen_t optlen); ##level為協議層;optval為可選項名
  • 一些常用的選項:SO_SNDBUF&SO_RCVBUF;SO_REUSEADDR;TCP_NODELAY
  • Nagle算法:p150,TCP套接字默認使用Nagle算法交換數據,網絡流量未受到太大影響時,不使用Nagle算法要比使用它時傳輸速度更快(如傳輸大文件數據);未準確判斷數據特性時不應禁用Nagle算法

總結

以上是生活随笔為你收集整理的TCP/IP网络编程复习(上)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。