(网络编程)SOCKET应用实例
生活随笔
收集整理的這篇文章主要介紹了
(网络编程)SOCKET应用实例
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
目錄
- 一、面向連接的流式套接字 C/S
- 二、非阻塞的多人聊天服務(wù)器
- 三、其他優(yōu)秀博主
一、面向連接的流式套接字 C/S
服務(wù)器代碼
#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函數(shù)原型:pid_t waitpid(pid_t pid,int *status,int options)作用:暫時(shí)停止目前進(jìn)程的執(zhí)行,直到有信號(hào)來(lái)到或子進(jìn)程結(jié)束參數(shù)說(shuō)明:pid:當(dāng)pid>0時(shí),只等待進(jìn)程ID等于pid的子進(jìn)程,不管其它已經(jīng)有多少子進(jìn)程運(yùn)行結(jié)束退出了,只要指定的子進(jìn)程還沒(méi)有結(jié)束,waitpid就會(huì)一直等下去。pid=-1時(shí),等待任何一個(gè)子進(jìn)程退出,沒(méi)有任何限制,此時(shí)waitpid和wait的作用一模一樣。pid=0時(shí),等待同一個(gè)進(jìn)程組中的任何子進(jìn)程,如果子進(jìn)程已經(jīng)加入了別的進(jìn)程組,waitpid不會(huì)對(duì)它做任何理睬。pid<-1時(shí),等待一個(gè)指定進(jìn)程組中的任何子進(jìn)程,這個(gè)進(jìn)程組的ID等于pid的絕對(duì)值。options:目前只要WNOHANG和WUNTRACED兩個(gè)選項(xiàng),這是兩個(gè)常數(shù),可以用"|"運(yùn)算符把它們連接起來(lái)使用函數(shù)的返回值:當(dāng)正常返回的時(shí)候,waitpid返回收集到的子進(jìn)程的進(jìn)程ID;如果設(shè)置了選項(xiàng)WNOHANG,而調(diào)用中waitpid發(fā)現(xiàn)沒(méi)有已退出的子進(jìn)程可收集,則返回0;如果調(diào)用中出錯(cuò),則返回-1,這時(shí)errno會(huì)被設(shè)置成相應(yīng)的值以指示錯(cuò)誤所在;當(dāng)pid所指示的子進(jìn)程不存在,或此進(jìn)程存在,但不是調(diào)用進(jìn)程的子進(jìn)程,waitpid就會(huì)出錯(cuò)返回,這時(shí)errno被設(shè)置為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字節(jié),包含套接字中的目標(biāo)地址和端口信息 }; 如果地址是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函數(shù)中使用的選項(xiàng)的標(biāo)志。int ai_family;int ai_socktype;int ai_protocol;size_t ai_addrlen;//緩沖區(qū)的長(zhǎng)度(以字節(jié)為單位)char *ai_canonname;//主機(jī)的規(guī)范名稱(chēng)struct sockaddr *ai_addr;//向 sockaddr 結(jié)構(gòu)的指針。每個(gè)返回的addrinfo結(jié)構(gòu)中的ai_addr成員指向一個(gè)填充的套接字地址結(jié)構(gòu)struct addrinfo *ai_next;//指向鏈表中下一個(gè)結(jié)構(gòu)的指針。此參數(shù)在鏈接列表的最后一個(gè)addrinfo結(jié)構(gòu)中設(shè)置為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);//新的信號(hào)處理函數(shù)void (*sa_sigaction)(int, siginfo_t *, void *);sigset_t sa_mask;//設(shè)置在處理該信號(hào)時(shí)暫時(shí)將sa_mask 指定的信號(hào)集擱置int sa_flags;//設(shè)置信號(hào)處理的其他相關(guān)操作 void (*sa_restorer)(void);}*/struct sigaction sa; int yes=1;char s[INET6_ADDRSTRLEN]; int rv; /* memset()函數(shù)原型:extern void *memset(void *buffer, int c, int count) 參數(shù)說(shuō)明:buffer:為指針或是數(shù)組,c:是賦給buffer的值,count:是buffer的長(zhǎng)度作用:將某一塊內(nèi)存中的內(nèi)容全部設(shè)置為指定的值, 這個(gè)函數(shù)通常為新申請(qǐng)的內(nèi)存做初始化工作。socket中多用于清空數(shù)組。*/memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; // use my IP /*getaddrinfo函數(shù)原型:int getaddrinfo( const char *hostname, const char *service, const struct addrinfo *hints, struct addrinfo **result );參數(shù)說(shuō)明:hostname:一個(gè)主機(jī)名或者地址串(IPv4的點(diǎn)分十進(jìn)制串或者IPv6的16進(jìn)制串)service:服務(wù)名可以是十進(jìn)制的端口號(hào),也可以是已定義的服務(wù)名稱(chēng),如ftp、http等hints:可以是一個(gè)空指針,也可以是一個(gè)指向某個(gè)addrinfo結(jié)構(gòu)體的指針,調(diào)用者在這個(gè)結(jié)構(gòu)中填入關(guān)于期望返回的信息類(lèi)型的暗示。舉例來(lái)說(shuō):指定的服務(wù)既可支持TCP也可支持UDP,所以調(diào)用者可以把hints結(jié)構(gòu)中的ai_socktype成員設(shè)置成SOCK_DGRAM使得返回的僅僅是適用于數(shù)據(jù)報(bào)套接口的信息。result:本函數(shù)通過(guò)result指針參數(shù)返回一個(gè)指向addrinfo結(jié)構(gòu)體鏈表的指針。返回值:0——成功,非0——出錯(cuò)*/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函數(shù)是用于任意類(lèi)型、任意狀態(tài)套接口的設(shè)置選項(xiàng)值,即設(shè)置套接口的選項(xiàng)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:標(biāo)識(shí)一個(gè)套接口的描述字。level:選項(xiàng)定義的層次;支持SOL_SOCKET、IPPROTO_TCP、IPPROTO_IP和IPPROTO_IPV6等。optname:需設(shè)置的選項(xiàng)。optval:指針,指向存放選項(xiàng)值的緩沖區(qū)。optlen:optval緩沖區(qū)長(zhǎng)度。IP_HDRINCL:如果是TRUE,IP頭就會(huì)隨即將發(fā)送的數(shù)據(jù)一起提交,并從讀取的數(shù)據(jù)中返回*/if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) { perror("setsockopt"); exit(1); }/*通過(guò)給一個(gè)未命名套接口分配一個(gè)本地名字來(lái)為套接口建立本地捆綁(主機(jī)地址/端口號(hào))。bind函數(shù)原型:int bind( int sockfd , const struct sockaddr * my_addr, socklen_t addrlen);參數(shù)說(shuō)明:sockfd:已經(jīng)建立的socket編號(hào)(描述符);my_addr:一個(gè)指向sockaddr結(jié)構(gòu)體類(lèi)型的指針;addrlen:my_addr結(jié)構(gòu)的長(zhǎng)度,可以用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 /*創(chuàng)建一個(gè)套接口并監(jiān)聽(tīng)申請(qǐng)的連接.listen函數(shù)原型:int listen( int sockfd, int backlog);參數(shù)說(shuō)明:sockfd:用于標(biāo)識(shí)一個(gè)已捆綁未連接套接口的描述字。backlog:等待連接隊(duì)列的最大長(zhǎng)度*/if (listen(sockfd, BACKLOG) == -1) { perror("listen"); exit(1); }sa.sa_handler = sigchld_handler; // reap all dead processes //sigemptyset 函數(shù)初始化信號(hào)集合set,將set 設(shè)置為空.sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; /*sigaction函數(shù)用來(lái)查詢和設(shè)置信號(hào)處理方式函數(shù)原型: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"); //循環(huán)接收客戶端的連接while(1) { // main accept() loop sin_size = sizeof their_addr; /*一個(gè)套接口接受的一個(gè)連接accept函數(shù)原型:SOCKET accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);參數(shù)說(shuō)明:sockfd:套接字描述符,該套接口在listen()后監(jiān)聽(tīng)連接。addr:(可選)指針,指向一緩沖區(qū),其中接收為通訊層所知的連接實(shí)體的地址。Addr參數(shù)的實(shí)際格式由套接口創(chuàng)建時(shí)所產(chǎn)生的地址族確定。addrlen:(可選)指針,輸入?yún)?shù),配合addr一起使用,指向存有addr地址長(zhǎng)度的整型數(shù)。*/new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size); if (new_fd == -1) { perror("accept"); continue; }/*inet_ntop函數(shù)原型:const char * inet_ntop(int family, const void *addrptr, char *strptr, size_t len); 作用:將數(shù)值格式轉(zhuǎn)化為點(diǎn)分十進(jìn)制的ip地址格式返回值:若成功則為指向結(jié)構(gòu)的指針,若出錯(cuò)則為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 //發(fā)送一個(gè)消息給客戶端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]; //若輸入的命令行參數(shù)(包括命令本身)不等于2,就輸出錯(cuò)誤信息,并退出程序。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; }//連接服務(wù)器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; }二、非阻塞的多人聊天服務(wù)器
服務(wù)器代碼
#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() 的臨時(shí)文件描述符列表int fdmax; //最大文件int listener; //監(jiān)聽(tīng)套接字struct sockaddr_storage remoteaddr; //客戶端地址,該數(shù)據(jù)結(jié)構(gòu)是用于存儲(chǔ)套接字地址信息socklen_t addrlen;char buf[256]; //用于客戶端數(shù)據(jù)的緩沖區(qū)int nbytes;int yes=1; //for setsockopt() SO_REUSEADDR,belowint i,j,rv;char remoteIP[INET_ADDRSTRLEN];struct addrinfo hints,*ai,*p; //地址信息結(jié)構(gòu)體FD_ZERO(&master); //清除主文件描述符列表FD_ZERO(&read_fds); //清楚臨時(shí)文件描述符列表//給我們一個(gè)套接字并綁定它memset(&hints, 0, sizeof hints); //將某一塊內(nèi)存中的內(nèi)容全部設(shè)置為指定的值, 這個(gè)函數(shù)通常為新申請(qǐng)的內(nèi)存做初始化工作。socket中多用于清空數(shù)組。此處是將地址信息置零。hints.ai_family = AF_UNSPEC; //AF_UNSPEC(協(xié)議無(wú)關(guān))hints.ai_socktype = SOCK_STREAM; //SOCK_STREAM(流)hints.ai_flags = AI_PASSIVE; //AI_PASSIVE(被動(dòng)的,用于 bind)if((rv = getaddrinfo(NULL, PORT, &hints, &ai)) != 0){fprintf(stderr, "selectserver:%s\n", gai_strerror(rv));exit(1);}//遍歷所有的結(jié)果for(p = ai; p != NULL; p = p->ai_next){//創(chuàng)建套接字并賦值給 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 函數(shù):用于連接的數(shù)據(jù)報(bào)或流類(lèi)套接口,進(jìn)行綁定*/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);}//到了這里,意味著服務(wù)器的地址和端口綁定完成,可以釋放 ai 的內(nèi)存freeaddrinfo(ai); /*listen 函數(shù):創(chuàng)建一個(gè)套接口并監(jiān)聽(tīng)申請(qǐng)的連接.參數(shù)一:用于標(biāo)識(shí)一個(gè)已捆綁未連接套接口的描述符參數(shù)二:等待連接隊(duì)列的最大長(zhǎng)度(這里是 10),即表示最大可以連接的客戶端數(shù)量*/if(listen(listener, 10) == -1){perror("listen");exit(3);}//將偵聽(tīng)器添加到主文件FD_SET(listener, &master);//跟蹤最大的文件描述符fdmax = listener; 、//主循環(huán)for(;;){read_fds = master; //將主文件描述符表復(fù)制到臨時(shí)文件描述符表/*select 函數(shù):確定一個(gè)或多個(gè)套接口的狀態(tài),如需要?jiǎng)t等待。原型:int select( int nfds, fd_set FAR* readfds, fd_set * writefds,fd_set * exceptfds, const struct timeval * timeout);nfds:是一個(gè)整數(shù)值,是指集合中所有文件描述符的范圍,即所有文件描述符的最大值加1,不能錯(cuò)!在Windows中這個(gè)參數(shù)的值無(wú)所謂,可以設(shè)置不正確。readfds:(可選)指針,指向一組等待可讀性檢查的套接口。writefds:(可選)指針,指向一組等待可寫(xiě)性檢查的套接口。exceptfds:(可選)指針,指向一組等待錯(cuò)誤檢查的套接口。timeout:select()最多等待時(shí)間,對(duì)阻塞操作則為 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)在調(diào)用 selelct() 函數(shù)后,用 FD_ISSET 來(lái)檢測(cè) fd 在 fdset 集合中的狀態(tài)是否變化返回整型,當(dāng)檢測(cè)到 fd 狀態(tài)發(fā)生變化時(shí)返回真,否則返回假*/if(FD_ISSET(i, &read_fds)) //得到了一個(gè)連接{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{//處理來(lái)自客戶端的數(shù)據(jù)if((nbytes = recv(i, buf, sizeof buf, 0)) <= 0){//出現(xiàn)錯(cuò)誤或連接被客戶端關(guān)閉if(nbytes == 0){//連接關(guān)閉了printf("selectserver:socket %d hung up\n", i);}else{perror("recv");}close(i); //關(guān)閉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函數(shù)從TCP連接的另一端接收數(shù)據(jù)第一個(gè)參數(shù)指定接收端套接字描述符;第二個(gè)參數(shù)指明一個(gè)緩沖區(qū),該緩沖區(qū)用來(lái)存放recv函數(shù)接收到的數(shù)據(jù);第三個(gè)參數(shù)指明buf的長(zhǎng)度;第四個(gè)參數(shù)一般置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; }三、其他優(yōu)秀博主
https://blog.csdn.net/xianyudewo/article/details/110647187
總結(jié)
以上是生活随笔為你收集整理的(网络编程)SOCKET应用实例的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 儿童素描手绘创意设计字体 for mac
- 下一篇: ListView优化的