新手socket编程入门详解指南
开发环境
运行平台:Ubantu14.04LTS
疑问引导
问题1:头文件的疑问:
#include
解答:
1.使用diff查看:adc分别表示添加、删除、修改
2.其实是路径的不同导致有不同的socke.h文件
3.
4.
问题2:大小端字节序问题:
1.c语言检测:利用指针取值和取址的交叉应用,为了增强网络移植性
2.而socket提供了字节序转换函数:h:host;n:network;l:long32位;s:short16位
3.htonl:将主机的32位主机字节序(ip地址),转换为网络字节序(一列数据)。
问题3:就一个服务器、一个客户端来说,有如下的对应角色说法:
对象first | 对象second |
---|---|
服务器 | 客户端 |
监听者 | 广播者 |
提供服务 | 请求服务 |
解析socket编程整体过程:
建立与删除
服务器和客户端通过同一的socket信道通信,而创建一个socket信道,提供socket连接。
intsocket(intdomain,inttype,intprotocol);
domain(域):各个域以AF_XXX命令,意指地址族。决定使用何种的地址类型,确定通信特性:包括地址格式
type:确定套字节的类型,(还)可以自由增加类型。
常用:SOCK_STREAM(即:TCP)和SOCK_DGRAM(即:UDP)
protocol:指定socket使用的传输协议编号,一般直接设置为0即可,以此表示为给定的域和套接字类型选择默认的传输协议。
返回值:正确返回套接字处理代码(我称之为套接字文件描述符),错误返回-1。该数值将存储使用。
服务器和客户端通都可以,关闭socket通信IO
intshutdown(ints,inthow);
s:代表socket_fd,需要关闭的套接字文件描述符
how:为一种方式
shutdown是使socket信道处于不活动状态。可以让该信道关闭写端,而继续接收该套接字读端以此确定数据何时结束,然后再使用close来关闭这个信道。
连接关系
创建和销毁或关闭IO之后,需要知道如何标识一个目标通信进程。
原因:网络有多个计算机,某台计算机上运行着多个程序(进程)。下面是两层关系:
1)目标计算机的网络地址
2)目标计算机上的目标进程的所代表的端口号
所以,目前你需要了解到的有下面几点:
1.字节序:直接看上面的问题2即可,简单的转换关系。
2.地址格式:根据不同的因特网地址,在
3.定义地址结构体,根据实际装入数值作为socketAPI实参
4.地址进制转换:对地址进行二进制与文本字符串格式之间的转换。inet_ntop或inet_pton
绑定接着,对于服务端来说,需要绑定(关联)地址和套接字。为给定的sockfd关联一个sockaddr结构数据。只有服务端将套接字绑定在(域)地址上,客户端才能够连接(connect)成功。
intbind(intsockfd,structsockaddr*my_addr,intaddrlen);
sockfd:套接字文件描述符,是socket返回的值
my_addr:(服务器)网络地址信息
返回值:判断是否正确绑定地址和套接字
连接在此之前,我们创建了套接字(socket)、建立连接基础(bind)。那么,就这就是为了在通信之前,将socket信道连接起来。
intconnect(intsockfd,structsockaddr*serv_addr,intaddrlen);
sockfd:套接字文件描述符,是socket返回的值
serv_addr:网络地址信息
返回值:判断是否正确连接,客户端程序必须要能够处理connect返回的错误。
到目前,你或许已经发现了,connect函数的参数类型与个数都跟bind是一样的(他们的值并不一样,我所说的是形式),结合一起去理解,会更好。
毕竟,根据TCPIP协议,需要连接的信息:IP地址,端口号,就已经足够了。至于其余的MAC地址等等,在socket里面,我们不需要理会。
监听需要注意的是,这种连接,服务器还需要确定是哪个客户端请求连接。所以,服务器首先进入运行请求客户端(任意一个)连接的状态,进入listen(监听)状态。使用函数:
intlisten(ints,intbacklog);
s:服务器套接字描述符,是socket返回的值
backlog:指定同时能够处理的最大连接要求
函数返回值:是否正确进入监听状态
连接这时候,服务器已经进入了listen状态,然后紧接着调用:
intaccept(ints,structsockaddr*addr,int*addrlen);
s:服务器套接字描述符,是socket返回的值
addr:某一被连接的客户端的套接字数据
addrlen:某一被连接的客户端的套接字数据长度
返回:某一被连接的客户端的文件描述符
读取与发送数据
到目前为止,服务器和客户端都已经做好了双向通信的基础准备。
send与recv暂时不提及,读者自己去查API
intrecv(ints,void*buf,intlen,unsignedintflags); intsend(ints,constvoid*msg,intlen,unsignedintfalgs);
以下直接与代码相关:
//常用包含头文件andsocket编程的作用 #include// #include // #include //errno错误信息变量 #include // #include // #include //提供socketAPI #include // #include //socketAPI参数的类型定义文件 #include //地址转换函数 #include //字节序函数(宏)、域地址类型定义
客户端代码:
#include#include #include #include #include #include"wrap.h" #defineMAXLINE80 #defineSERV_PORT8000 intmain(intargc,char*argv[]) { structsockaddr_inservaddr; charbuf[MAXLINE]; intsockfd,n; sockfd=Socket(AF_INET,SOCK_STREAM,0); bzero(&servaddr,sizeof(servaddr)); servaddr.sin_family=AF_INET; inet_pton(AF_INET,"192.168.191.6",&servaddr.sin_addr); servaddr.sin_port=htons(SERV_PORT); Connect(sockfd,(structsockaddr*)&servaddr,sizeof(servaddr)); while(fgets(buf,MAXLINE,stdin)!=NULL){ Write(sockfd,buf,strlen(buf)); n=Read(sockfd,buf,MAXLINE); if(n==0) printf("theothersidehasbeenclosed.\n"); else Write(STDOUT_FILENO,buf,n); } Close(sockfd); return0; }
服务器代码:
#include#include #include #include"wrap.h" #defineMAXLINE80 #defineSERV_PORT8000 intmain(void) { structsockaddr_inservaddr,cliaddr; socklen_tcliaddr_len; intlistenfd,connfd; charbuf[MAXLINE]; charstr[INET_ADDRSTRLEN]; inti,n; listenfd=Socket(AF_INET,SOCK_STREAM,0); bzero(&servaddr,sizeof(servaddr)); servaddr.sin_family=AF_INET; servaddr.sin_addr.s_addr=inet_addr("192.168.191.6"); servaddr.sin_port=htons(SERV_PORT); Bind(listenfd,(structsockaddr*)&servaddr,sizeof(servaddr)); Listen(listenfd,20); printf("Acceptingconnections...\n"); while(1){ cliaddr_len=sizeof(cliaddr); connfd=Accept(listenfd, (structsockaddr*)&cliaddr, &cliaddr_len); while(1){ n=Read(connfd,buf,MAXLINE); if(n==0){ printf("theothersidehasbeenclosed.\n"); break; } printf("receivedfrom%satPORT%d\n", (char*)inet_ntop(AF_INET, &cliaddr.sin_addr,str, sizeof(str)), (int)ntohs(cliaddr.sin_port)); for(i=0;i 运行结果:
hhc@my:~/sharefile/socket/tcp$./server& [1]15371 hhc@my:~/sharefile/socket/tcp$Acceptingconnections... hhc@my:~/sharefile/socket/tcp$./client thisisatest! receivedfrom192.168.191.6atPORT53685 THISISATEST!
个人封装的socket接口函数:
#include"wrap.h" voidperr_exit(constchar*s) { perror(s); exit(1); } intAccept(intfd,structsockaddr*sa,socklen_t*salenptr) { intn; again: if((n=accept(fd,sa,salenptr))<0){ if((errno==ECONNABORTED)||(errno==EINTR)) gotoagain; else perr_exit("accepterror"); } returnn; } voidBind(intfd,conststructsockaddr*sa,socklen_tsalen) { if(bind(fd,sa,salen)<0) perr_exit("binderror"); } voidConnect(intfd,conststructsockaddr*sa,socklen_tsalen) { if(connect(fd,sa,salen)<0) perr_exit("connecterror"); } voidListen(intfd,intbacklog) { if(listen(fd,backlog)<0) perr_exit("listenerror"); } intSocket(intfamily,inttype,intprotocol) { intn; if((n=socket(family,type,protocol))<0) perr_exit("socketerror"); returnn; } ssize_tRead(intfd,void*ptr,size_tnbytes) { ssize_tn; again: if((n=read(fd,ptr,nbytes))==-1){ if(errno==EINTR) gotoagain; else return-1; } returnn; } ssize_tWrite(intfd,constvoid*ptr,size_tnbytes) { ssize_tn; again: if((n=write(fd,ptr,nbytes))==-1){ if(errno==EINTR) gotoagain; else return-1; } returnn; } voidClose(intfd) { if(close(fd)==-1) perr_exit("closeerror"); } ssize_tReadn(intfd,void*vptr,size_tn) { size_tnleft; ssize_tnread; char*ptr; ptr=vptr; nleft=n; while(nleft>0){ if((nread=read(fd,ptr,nleft))<0){ if(errno==EINTR) nread=0; else return-1; }elseif(nread==0) break; nleft-=nread; ptr+=nread; } returnn-nleft; } ssize_tWriten(intfd,constvoid*vptr,size_tn) { size_tnleft; ssize_tnwritten; constchar*ptr; ptr=vptr; nleft=n; while(nleft>0){ if((nwritten=write(fd,ptr,nleft))<=0){ if(nwritten<0&&errno==EINTR) nwritten=0; else return-1; } nleft-=nwritten; ptr+=nwritten; } returnn; } ssize_tmy_read(intfd,char*ptr) { staticintread_cnt; staticchar*read_ptr; staticcharread_buf[100]; if(read_cnt<=0){ again: if((read_cnt=read(fd,read_buf,sizeof(read_buf)))<0){ if(errno==EINTR) gotoagain; return-1; }elseif(read_cnt==0) return0; read_ptr=read_buf; } read_cnt--; *ptr=*read_ptr++; return1; } ssize_tReadline(intfd,void*vptr,size_tmaxlen) { ssize_tn,rc; charc,*ptr; ptr=vptr; for(n=(ssize_t)1;n<(ssize_t)maxlen;n++){ if((rc=my_read(fd,&c))==1){ *ptr++=c; if(c=='\n') break; }elseif(rc==0){ *ptr=0; returnn-1; }else return-1; } *ptr=0; returnn; }以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。