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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

(网络编程)SOCKET应用实例

發布時間:2024/8/1 编程问答 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 (网络编程)SOCKET应用实例 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目錄

  • 一、面向連接的流式套接字 C/S
  • 二、非阻塞的多人聊天服務器
  • 三、其他優秀博主

一、面向連接的流式套接字 C/S

服務器代碼

#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> #include <arpa/inet.h> #include <sys/wait.h> #include <signal.h> #define PORT "9090" // the port users will be connecting to #define BACKLOG 10 // how many pending connections queue will hold void sigchld_handler(int s) {/*waitpid函數原型:pid_t waitpid(pid_t pid,int *status,int options)作用:暫時停止目前進程的執行,直到有信號來到或子進程結束參數說明:pid:當pid>0時,只等待進程ID等于pid的子進程,不管其它已經有多少子進程運行結束退出了,只要指定的子進程還沒有結束,waitpid就會一直等下去。pid=-1時,等待任何一個子進程退出,沒有任何限制,此時waitpid和wait的作用一模一樣。pid=0時,等待同一個進程組中的任何子進程,如果子進程已經加入了別的進程組,waitpid不會對它做任何理睬。pid<-1時,等待一個指定進程組中的任何子進程,這個進程組的ID等于pid的絕對值。options:目前只要WNOHANG和WUNTRACED兩個選項,這是兩個常數,可以用"|"運算符把它們連接起來使用函數的返回值:當正常返回的時候,waitpid返回收集到的子進程的進程ID;如果設置了選項WNOHANG,而調用中waitpid發現沒有已退出的子進程可收集,則返回0;如果調用中出錯,則返回-1,這時errno會被設置成相應的值以指示錯誤所在;當pid所指示的子進程不存在,或此進程存在,但不是調用進程的子進程,waitpid就會出錯返回,這時errno被設置為ECHILD;*/while(waitpid(-1, NULL, WNOHANG) > 0); } // get sockaddr, IPv4 or IPv6(獲取IP地址): /*struct sockaddr { sa_family_t sin_family;//地址族char sa_data[14]; //14字節,包含套接字中的目標地址和端口信息 }; 如果地址是IPv4,返回32位的IP地址 */ void *get_in_addr(struct sockaddr *sa) { if (sa->sa_family == AF_INET) { return &(((struct sockaddr_in*)sa)->sin_addr); }return &(((struct sockaddr_in6*)sa)->sin6_addr); }int main(void) { int sockfd, new_fd; // listen on sock_fd, new connection on new_fd /*struct addrinfo {int ai_flags;//指示在getaddrinfo函數中使用的選項的標志。int ai_family;int ai_socktype;int ai_protocol;size_t ai_addrlen;//緩沖區的長度(以字節為單位)char *ai_canonname;//主機的規范名稱struct sockaddr *ai_addr;//向 sockaddr 結構的指針。每個返回的addrinfo結構中的ai_addr成員指向一個填充的套接字地址結構struct addrinfo *ai_next;//指向鏈表中下一個結構的指針。此參數在鏈接列表的最后一個addrinfo結構中設置為NULL。} */struct addrinfo hints, *servinfo, *p; /*struct sockaddr_storage{sa_family_t ss_family; //地址族__ss_aligntype __ss_align; //Force desired alignment. char __ss_padding[_SS_PADSIZE];};*/struct sockaddr_storage their_addr; // connector's address information socklen_t sin_size; /*struct sigaction {void (*sa_handler)(int);//新的信號處理函數void (*sa_sigaction)(int, siginfo_t *, void *);sigset_t sa_mask;//設置在處理該信號時暫時將sa_mask 指定的信號集擱置int sa_flags;//設置信號處理的其他相關操作 void (*sa_restorer)(void);}*/struct sigaction sa; int yes=1;char s[INET6_ADDRSTRLEN]; int rv; /* memset()函數原型:extern void *memset(void *buffer, int c, int count) 參數說明:buffer:為指針或是數組,c:是賦給buffer的值,count:是buffer的長度作用:將某一塊內存中的內容全部設置為指定的值, 這個函數通常為新申請的內存做初始化工作。socket中多用于清空數組。*/memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; // use my IP /*getaddrinfo函數原型:int getaddrinfo( const char *hostname, const char *service, const struct addrinfo *hints, struct addrinfo **result );參數說明:hostname:一個主機名或者地址串(IPv4的點分十進制串或者IPv6的16進制串)service:服務名可以是十進制的端口號,也可以是已定義的服務名稱,如ftp、http等hints:可以是一個空指針,也可以是一個指向某個addrinfo結構體的指針,調用者在這個結構中填入關于期望返回的信息類型的暗示。舉例來說:指定的服務既可支持TCP也可支持UDP,所以調用者可以把hints結構中的ai_socktype成員設置成SOCK_DGRAM使得返回的僅僅是適用于數據報套接口的信息。result:本函數通過result指針參數返回一個指向addrinfo結構體鏈表的指針。返回值:0——成功,非0——出錯*/if ((rv = getaddrinfo(NULL, PORT, &hints, &servinfo)) != 0) { fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv)); return 1; }for(p = servinfo; p != NULL; p = p->ai_next) { if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) { perror("server: socket"); continue; }/*setsockopt函數是用于任意類型、任意狀態套接口的設置選項值,即設置套接口的選項setsockopt(rip_sock, IPPROTO_IP, IP_HDRINCL, (char *)&one, sizeof(one))的原型:int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);sockfd:標識一個套接口的描述字。level:選項定義的層次;支持SOL_SOCKET、IPPROTO_TCP、IPPROTO_IP和IPPROTO_IPV6等。optname:需設置的選項。optval:指針,指向存放選項值的緩沖區。optlen:optval緩沖區長度。IP_HDRINCL:如果是TRUE,IP頭就會隨即將發送的數據一起提交,并從讀取的數據中返回*/if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) { perror("setsockopt"); exit(1); }/*通過給一個未命名套接口分配一個本地名字來為套接口建立本地捆綁(主機地址/端口號)。bind函數原型:int bind( int sockfd , const struct sockaddr * my_addr, socklen_t addrlen);參數說明:sockfd:已經建立的socket編號(描述符);my_addr:一個指向sockaddr結構體類型的指針;addrlen:my_addr結構的長度,可以用sizeof操作符獲得*/if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) { close(sockfd); perror("server: bind"); continue; }break; }if (p == NULL) { fprintf(stderr, "server: failed to bind\n"); return 2; }freeaddrinfo(servinfo); // all done with this structure /*創建一個套接口并監聽申請的連接.listen函數原型:int listen( int sockfd, int backlog);參數說明:sockfd:用于標識一個已捆綁未連接套接口的描述字。backlog:等待連接隊列的最大長度*/if (listen(sockfd, BACKLOG) == -1) { perror("listen"); exit(1); }sa.sa_handler = sigchld_handler; // reap all dead processes //sigemptyset 函數初始化信號集合set,將set 設置為空.sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; /*sigaction函數用來查詢和設置信號處理方式函數原型:int sigaction(int signum,const struct sigaction *act ,struct sigaction *oldact)*/if (sigaction(SIGCHLD, &sa, NULL) == -1) { perror("sigaction"); exit(1);} printf("server: waiting for connections...\n"); //循環接收客戶端的連接while(1) { // main accept() loop sin_size = sizeof their_addr; /*一個套接口接受的一個連接accept函數原型:SOCKET accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);參數說明:sockfd:套接字描述符,該套接口在listen()后監聽連接。addr:(可選)指針,指向一緩沖區,其中接收為通訊層所知的連接實體的地址。Addr參數的實際格式由套接口創建時所產生的地址族確定。addrlen:(可選)指針,輸入參數,配合addr一起使用,指向存有addr地址長度的整型數。*/new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size); if (new_fd == -1) { perror("accept"); continue; }/*inet_ntop函數原型:const char * inet_ntop(int family, const void *addrptr, char *strptr, size_t len); 作用:將數值格式轉化為點分十進制的ip地址格式返回值:若成功則為指向結構的指針,若出錯則為NULL*/inet_ntop(their_addr.ss_family, get_in_addr((struct sockaddr *)&their_addr), s, sizeof s); printf("server: got connection from %s\n", s); if (!fork()) { // this is the child process close(sockfd); // child doesn't need the listener //發送一個消息給客戶端if (send(new_fd, "Hello, world!", 13, 0) == -1) perror("send"); close(new_fd); exit(0); }close(new_fd); // parent doesn't need this }return 0; }

客戶端代碼

#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <string.h> #include <netdb.h> #include <sys/types.h> #include <netinet/in.h> #include <sys/socket.h> #include <arpa/inet.h> #define PORT "9090" // the port client will be connecting to #define MAXDATASIZE 100 // max number of bytes we can get at once // get sockaddr, IPv4 or IPv6: void *get_in_addr(struct sockaddr *sa) { if (sa->sa_family == AF_INET) { return &(((struct sockaddr_in*)sa)->sin_addr); }return &(((struct sockaddr_in6*)sa)->sin6_addr); }int main(int argc, char *argv[]) { int sockfd, numbytes; char buf[MAXDATASIZE]; struct addrinfo hints, *servinfo, *p; int rv; char s[INET6_ADDRSTRLEN]; //若輸入的命令行參數(包括命令本身)不等于2,就輸出錯誤信息,并退出程序。if (argc != 2) { fprintf(stderr,"usage: client hostname\n");exit(1); }memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; if ((rv = getaddrinfo(argv[1], PORT, &hints, &servinfo)) != 0) { fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv)); return 1; }for(p = servinfo; p != NULL; p = p->ai_next) { if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) { perror("client: socket"); continue; }//連接服務器if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) { close(sockfd); perror("client: connect"); continue; }break; }if (p == NULL) { fprintf(stderr, "client: failed to connect\n"); return 2; }inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr), s, sizeof s); printf("client: connecting to %s\n", s); freeaddrinfo(servinfo); // all done with this structure if ((numbytes = recv(sockfd, buf, MAXDATASIZE-1, 0)) == -1) { perror("recv"); exit(1);}buf[numbytes] = '\0'; printf("client: received '%s'\n",buf); close(sockfd); return 0; }

二、非阻塞的多人聊天服務器

服務器代碼

#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h>#define PORT "9090" //port we're listening on //get sockaddr,IPv4 or IPv6: void *get_in_addr(struct sockaddr *sa) {if(sa->sa_family == AF_INET){return &(((struct sockaddr_in*)sa)->sin_addr);}return &(((struct sockaddr_in6*)sa)->sin6_addr); }int main(void) {fd_set master; //主文件描述符列表fd_set read_fds; //select() 的臨時文件描述符列表int fdmax; //最大文件int listener; //監聽套接字struct sockaddr_storage remoteaddr; //客戶端地址,該數據結構是用于存儲套接字地址信息socklen_t addrlen;char buf[256]; //用于客戶端數據的緩沖區int nbytes;int yes=1; //for setsockopt() SO_REUSEADDR,belowint i,j,rv;char remoteIP[INET_ADDRSTRLEN];struct addrinfo hints,*ai,*p; //地址信息結構體FD_ZERO(&master); //清除主文件描述符列表FD_ZERO(&read_fds); //清楚臨時文件描述符列表//給我們一個套接字并綁定它memset(&hints, 0, sizeof hints); //將某一塊內存中的內容全部設置為指定的值, 這個函數通常為新申請的內存做初始化工作。socket中多用于清空數組。此處是將地址信息置零。hints.ai_family = AF_UNSPEC; //AF_UNSPEC(協議無關)hints.ai_socktype = SOCK_STREAM; //SOCK_STREAM(流)hints.ai_flags = AI_PASSIVE; //AI_PASSIVE(被動的,用于 bind)if((rv = getaddrinfo(NULL, PORT, &hints, &ai)) != 0){fprintf(stderr, "selectserver:%s\n", gai_strerror(rv));exit(1);}//遍歷所有的結果for(p = ai; p != NULL; p = p->ai_next){//創建套接字并賦值給 listener 套接字描述符listener = socket(p->ai_family, p->ai_socktype, p->ai_protocol);if(listener < 0){continue;}setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));/*bind 函數:用于連接的數據報或流類套接口,進行綁定*/if(bind(listener, p->ai_addr, p->ai_addrlen) < 0){close(listener);continue;}break;}if(p == NULL){fprintf(stderr, "selectserver:failed to bind\n");exit(2);}//到了這里,意味著服務器的地址和端口綁定完成,可以釋放 ai 的內存freeaddrinfo(ai); /*listen 函數:創建一個套接口并監聽申請的連接.參數一:用于標識一個已捆綁未連接套接口的描述符參數二:等待連接隊列的最大長度(這里是 10),即表示最大可以連接的客戶端數量*/if(listen(listener, 10) == -1){perror("listen");exit(3);}//將偵聽器添加到主文件FD_SET(listener, &master);//跟蹤最大的文件描述符fdmax = listener;//主循環for(;;){read_fds = master; //將主文件描述符表復制到臨時文件描述符表/*select 函數:確定一個或多個套接口的狀態,如需要則等待。原型:int select( int nfds, fd_set FAR* readfds, fd_set * writefds,fd_set * exceptfds, const struct timeval * timeout);nfds:是一個整數值,是指集合中所有文件描述符的范圍,即所有文件描述符的最大值加1,不能錯!在Windows中這個參數的值無所謂,可以設置不正確。readfds:(可選)指針,指向一組等待可讀性檢查的套接口。writefds:(可選)指針,指向一組等待可寫性檢查的套接口。exceptfds:(可選)指針,指向一組等待錯誤檢查的套接口。timeout:select()最多等待時間,對阻塞操作則為 NULL。*/if(select(fdmax + 1, &read_fds, NULL, NULL, NULL) == -1){perror("select");exit(4);}for(i = 0; i <= fdmax; i++){/*宏原型:int FD_ISSET(int fd,fd_set *fdset)在調用 selelct() 函數后,用 FD_ISSET 來檢測 fd 在 fdset 集合中的狀態是否變化返回整型,當檢測到 fd 狀態發生變化時返回真,否則返回假*/if(FD_ISSET(i, &read_fds)) //得到了一個連接{if(i == listener)//如果新連接為最大文件描述符{//處理新連接addrlen = sizeof remoteaddr;newfd = accept(listener, (struct sockaddr *)&remoteaddr, &addrlen);if(newfd == -1){perror("accept");}else{FD_SET(newfd, &master); //添加到主文件描述符列表if(newfd > fdmax) //記錄最大值{fdmax = newfd;}printf("selectserver:new connection from %s on socket %d\n", inet_ntop(remoteaddr.ss_family, get_in_addr((struct sockaddr*)&remoteaddr), remoteIP, INET_ADDRSTRLEN), newfd);}}else{//處理來自客戶端的數據if((nbytes = recv(i, buf, sizeof buf, 0)) <= 0){//出現錯誤或連接被客戶端關閉if(nbytes == 0){//連接關閉了printf("selectserver:socket %d hung up\n", i);}else{perror("recv");}close(i); //關閉FD_CLR(i, &master); //從主文件描述符列表中刪除}else{for(j =0; j <= fdmax; j++){if(FD_ISSET(j, &master)){if(j != listener && j != i){if(send(j, buf, nbytes, 0) == -1){perror("send");}}}}}} //END handle from client} //END got new incoming connection} //END looping through file descriptors} //END for(;;)--and you thought it would never end!return 0; }

客戶端代碼

#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <string.h> #include <netdb.h> #include <netinet/in.h> #include <sys/socket.h> #include <arpa/inet.h> #include <pthread.h>#define PORT "9090" //the port client will be connecting to #define MAXDATASIZE 100 //max number of bytes we can get at onceint sockfd, numbytes; char buf[MAXDATASIZE];//get sockaddr, IPv4 or IPv6 void *get_in_addr(struct sockaddr *sa) {if(sa->sa_family == AF_INET){return &(((struct sockaddr_in*)sa)->sin_addr);}return &(((struct sockaddr_in6*)sa)->sin6_addr); }void *recvMag() {/*int recv( SOCKET s,char FAR *buf,int len,int flags); recv函數從TCP連接的另一端接收數據第一個參數指定接收端套接字描述符;第二個參數指明一個緩沖區,該緩沖區用來存放recv函數接收到的數據;第三個參數指明buf的長度;第四個參數一般置0。*/while(1){if((numbytes = recv(sockfd, buf, MAXDATASIZE-1, 0)) == -1){perror("recv");exit(1);}if(numbytes == 1)continue;buf[numbytes] = '\0';printf("\nreceived:%s\n",buf);} }int main(int argc, char *argv[]) {struct addrinfo hints, *servinfo, *p;int rv;char s[INET6_ADDRSTRLEN];pthread_t t1;char mag[MAXDATASIZE];if(argc != 2){fprintf(stderr, "usage:client hostname\n");exit(1);}memset(&hints, 0, sizeof hints);hints.ai_family = AF_UNSPEC;hints.ai_socktype = SOCK_STREAM;if((rv = getaddrinfo(argv[1], PORT, &hints, &servinfo)) != 0){fprintf(stderr, "getaddrinfo:%s\n",gai_strerror(rv));return 1;}for(p = servinfo; p != NULL; p = p->ai_next){if((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1){perror("client:socket");continue;}if(connect(sockfd, p->ai_addr, p->ai_addrlen) == -1){close(sockfd);perror("client:connect");continue;}break;}if(p == NULL){fprintf(stderr, "client:failed to connect\n");return 2;}inet_ntop(p->ai_family, get_in_addr((struct sockaddr*)p->ai_addr), s, sizeof s);printf("client:connecting to %s\n",s);freeaddrinfo(servinfo);int err = pthread_create(&t1, NULL, recvMag, NULL);if(err != 0){printf("receive failed");exit(1);}while(1){scanf("%s", mag);if(send(sockfd, mag, sizeof mag, 0) == -1){printf("send failed!\n");}}return 0; }

三、其他優秀博主

https://blog.csdn.net/xianyudewo/article/details/110647187

總結

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

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