- 論壇徽章:
- 0
|
- /**********************************************
- 作者:Minuit
- 時(shí)間:2007年01月30日 星期二 04時(shí)36分08秒
- 文件名:tcp6servcli.c
- 描述:tcp服務(wù)器客戶端
- **********************************************/
- #include<stdio.h>
- #include<stdlib.h>
- #include<unistd.h>
- #include<string.h>
- #include<errno.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <netdb.h>
- enum {
- NETINIT_TCPSERV=1, /*創(chuàng)建一個(gè)tcp已經(jīng)監(jiān)聽套接口服務(wù)器*/
- NETINIT_TCPCLI, /*創(chuàng)建一個(gè)tcp已經(jīng)連接套接口客戶端*/
- NETINIT_TCPCLIBIND, /*創(chuàng)建一個(gè)tcp已經(jīng)連接并且綁定本IP地址和端口的客戶端*/
- NETINIT_UDPSERV, /*創(chuàng)建一個(gè)udp已經(jīng)綁定套接口服務(wù)器*/
- NETINIT_UDPCLI, /*創(chuàng)建一個(gè)udp客戶端*/
- NETINIT_UDPCLICONN, /*創(chuàng)建一個(gè)udp并且已經(jīng)連接服務(wù)器套接口客戶端*/
- NETINIT_BACKLOG=100
- };
- /*
- netinit 所能干的事情就上面那幾種
- 第一個(gè)參數(shù)主機(jī)名或者ip地址(當(dāng)建服務(wù)器時(shí)此參數(shù)可為空默認(rèn)BING一個(gè)通配的ipv6地址)
- 第二個(gè)參數(shù)服務(wù)名在/etc/services或者端口號(hào)(不能為空)
- 第三,四參數(shù)就不用介紹了說說有什么用處當(dāng)建立服務(wù)器時(shí)他返回相關(guān)協(xié)議的地址長度和地址結(jié)構(gòu)兩個(gè)都可能為空
- 根據(jù)須要比如建一個(gè)服務(wù)器當(dāng)調(diào)用accept(2)的時(shí)候如果想返回客戶機(jī)的信息那就要用到第四個(gè)參數(shù)地址長度(注:accept后兩個(gè)參數(shù)是可選的)又比如客戶端想用sendto發(fā)送信息到服務(wù)器那就可能須要一個(gè)服務(wù)器的地址如果你填了第三個(gè)參數(shù)它就返回一個(gè)服器的地址結(jié)構(gòu)這個(gè)還是很智能的(^_^)當(dāng)然像udp服務(wù)器只有調(diào)用recvfrom或者recvmsg才須要第四個(gè)參數(shù),udp客戶端只用用sendto或者sendmsg時(shí)會(huì)用到第三個(gè)參數(shù)總之只要第三,四個(gè)參數(shù)不空它就會(huì)給你填上你想要的東西如果為空那就是你不想讓他返回什么(你也可以過后調(diào)用get[sock|peer]name系統(tǒng)調(diào)用獲取你想要的結(jié)構(gòu)信息)
- 第四個(gè)參數(shù)已經(jīng)說過了
- */
- int netinit(const char *host,const char *serv,struct sockaddr *addr,socklen_t *addrlen,int flags)
- {
- struct addrinfo hist,*res,*save;
- int sockfd,on=1,errcode;
- bzero(&hist,sizeof(struct addrinfo));
- if(serv==NULL)
- return -1;
- hist.ai_family=AF_UNSPEC;
- switch(flags)
- {
- case NETINIT_TCPSERV:
- case NETINIT_UDPSERV:
- hist.ai_flags=AI_PASSIVE; /*如果是服務(wù)器的話就設(shè)置被動(dòng)模式*/
- break;
- case NETINIT_TCPCLI:
- case NETINIT_UDPCLI:
- case NETINIT_UDPCLICONN:
- case NETINIT_TCPCLIBIND:
- hist.ai_flags=AI_CANONNAME; /*如果是客戶端那就addrinfo結(jié)構(gòu)組成的鏈表第一個(gè)結(jié)構(gòu)里面的一個(gè)成員被填充主機(jī)名*/
- break;
- default:
- errno=ENOTSUP; /*不支持有時(shí)間把unix域加上*/
- return -1;
- }
- switch(flags)
- {
- case NETINIT_TCPCLI:
- case NETINIT_TCPSERV:
- case NETINIT_TCPCLIBIND:
- hist.ai_socktype=SOCK_STREAM; /*udp還是tcp*/
- break;
- case NETINIT_UDPSERV:
- case NETINIT_UDPCLI:
- case NETINIT_UDPCLICONN:
- hist.ai_socktype=SOCK_DGRAM;
- break;
- default:
- errno=ENOTSUP;
- return -1;
- }
- if((errcode=getaddrinfo(host,serv,&hist,&res))!=0)
- {
- fprintf(stderr,"getaddrinfo:%s\n",gai_strerror(errcode));
- return -1;
- }
- save=res; /*保存res待會(huì)好毀了它*/
- do
- {
- /*創(chuàng)建套接口不管是服務(wù)器還是客戶端的共同點(diǎn)必須要做的*/
- if((sockfd=socket(res->ai_family,res->ai_socktype,res->ai_protocol))>0)
- {
- /*要綁定地址的者在這里做*/
- if(flags==NETINIT_TCPSERV||flags==NETINIT_TCPCLIBIND||flags==NETINIT_UDPSERV)
- {
- if(flags!=NETINIT_TCPCLIBIND)
- if(setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on))!=0) /*給服務(wù)器設(shè)置端口重用*/
- return -1;
- if(bind(sockfd,res->ai_addr,res->ai_addrlen)==0)
- break;
- }
- else
- {
- /*要建連接的者在這里做*/
- if(flags!=NETINIT_UDPCLI)
- {
- if(connect(sockfd,res->ai_addr,res->ai_addrlen)==0)
- break;
- }
- else
- break;
- }/*如果成功建立套接口那就先出去不然等到鏈表里的結(jié)構(gòu)都試完了就出去*/
- }
- close(sockfd); /*如果創(chuàng)建不成功的話先關(guān)閉再說再試下面一個(gè)結(jié)構(gòu)*/
- } while((res=res->ai_next)!=NULL);
- if(res==NULL) /*如果失敗了返回*/
- return -1;
- if(addr!=NULL)
- memcpy(addr,res->ai_addr,res->ai_addrlen);
- if(addrlen!=NULL)
- *addrlen=res->ai_addrlen;
- freeaddrinfo(save); /*釋放addrinfo鏈表之前已經(jīng)保存了地址*/
- if(flags!=NETINIT_TCPSERV) /*如果是tcp服務(wù)器的話還有一點(diǎn)事沒做其它的返回*/
- return sockfd;
- else
- if(listen(sockfd,NETINIT_BACKLOG)!=0) /*監(jiān)聽套接口*/
- return -1;
- return sockfd;
- }
- void usage(char *arg)
- {
- fprintf(stderr,\
- "Usage:%s [-cs] [<ip or hostname>] <port or server>\n",\
- arg);
- exit(-1);
- }
- int main(int argc,char **argv)
- {
- struct sockaddr *addr;
- socklen_t addrlen;
- int servfd,clifd,pid,c,mode=1;
- opterr=0;
- while((c=getopt(argc,argv,"cs"))!=EOF)
- {
- switch(c)
- {
- case 'c':
- mode=0;
- break;
- case 's':
- mode=1;
- break;
- default:
- usage(argv[0]);
- }
- }
- if((addr=malloc(sizeof(struct sockaddr_in6)))==NULL)
- return -1;
- c=argc-optind;
- switch(c)
- {
- case 1:
- if(!mode)
- usage(argv[0]);
- servfd=netinit(NULL,argv[optind],addr,NULL,NETINIT_TCPSERV);
- break;
- case 2:
- servfd=netinit(argv[optind],argv[optind+1],addr,mode?NULL:&addrlen,mode?NETINIT_TCPSERV:NETINIT_TCPCLI);
- break;
- default:
- usage(argv[0]);
- }
- if(servfd==-1)
- usage(argv[0]);
- if(mode)
- {
- while(1)
- {
- if((clifd=accept(servfd,addr,&addrlen))<=0)
- {
- fprintf(stderr,"accept:%s\n",strerror(errno));
- return -1;
- }
- if((pid=fork())<0)
- {
- fprintf(stderr,"fork:%s\n",strerror(errno));
- return -1;
- }
- else if(pid==0)
- {
- char buf[BUFSIZ];
- int n;
- while((n=read(clifd,buf,BUFSIZ))>0)
- write(clifd,buf,n);
- exit(0);
- }
- close(clifd);
- }
- }
- else
- {
- int n;
- char buf[BUFSIZ];
- while(1)
- {
- if(n=read(STDIN_FILENO,buf,BUFSIZ))
- {
- if(write(servfd,buf,n)!=n)
- break;
- }
- else
- break;
- if((n=read(servfd,buf,BUFSIZ))>0)
- {
- if(write(STDOUT_FILENO,buf,n)!=n)
- break;
- }
- else
- break;
- }
- close(servfd);
- }
- return 0;
- }
復(fù)制代碼
看了一步一步網(wǎng)絡(luò)編程*不知道還有人愛好這個(gè)
就把我前幾個(gè)月寫的通用的網(wǎng)絡(luò)初始化函數(shù)截下來寫這個(gè)二百行的服務(wù)器+客戶端簡(jiǎn)單網(wǎng)絡(luò)程序
讓大家也偷偷懶寫ipv[46]服務(wù)器客戶端也就那兩下子而且主機(jī)名IP地址 服務(wù)端口都可以用
我就不試范怎么用(4點(diǎn)多鐘來的才寫了這么多注釋^_^!)
就說一下兩個(gè)命令行參數(shù)
-c 以客戶端起動(dòng)
-s 以服務(wù)器起動(dòng)
如果沒有選項(xiàng)的話那默認(rèn)就服務(wù)器
服務(wù)器還最少要一個(gè)參數(shù)那就是端口或者服務(wù)名如果兩個(gè)參數(shù)那就綁定第一個(gè)參數(shù)所指的ip地址
客戶端必須兩個(gè)參數(shù)并且-c選項(xiàng)不能少
就這么多了
如果有什么不足之處還請(qǐng)指出^_^
[ 本帖最后由 lovesaka 于 2007-1-30 07:13 編輯 ] |
|