分享
分销 收藏 举报 申诉 / 34
播放页_导航下方通栏广告

类型套接字编程基本原理讲解.doc

  • 上传人:快乐****生活
  • 文档编号:2626900
  • 上传时间:2024-06-03
  • 格式:DOC
  • 页数:34
  • 大小:210.04KB
  • 下载积分:12 金币
  • 播放页_非在线预览资源立即下载上方广告
    配套讲稿:

    如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。

    特殊限制:

    部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。

    关 键  词:
    套接 编程 基本原理 讲解
    资源描述:
    套接字编程基本原理讲解 ———————————————————————————————— 作者: ———————————————————————————————— 日期: 34 个人收集整理 勿做商业用途 套接字编程原理 一、客户机/效劳器模式 在网络中最常用的通信模式是客户机/效劳器模式(Client/Server模式或C/S模式)。 效劳器方要先启动,并监听指定端口,等待客户端的请求,根据客户端的请求提供相应效劳。 二、根本套接字 一般来说,要进展网络通信,必须要在网络的每一端都要建立一个套接字,两个套接字之间是可以建立连接的,也是可以无连接的,并通过对套接字的“读〞、“写〞操作实现网络通信功能。类似于文件的翻开、读、写、关闭的方式。 套接字有三种类型: 数据流套接字〔SOCK_STREAM〕:对应TCP协议。 数据报套接字〔SOCK_DGRAM):对应UDP协议。 原始套接字〔SOCK_RAW〕。通过使用原始套接字,可以将网卡设为混杂模式。并且可以捕获到的数据包不仅仅是单纯的数据信息,而是包含有 IP头、 TCP头等信息头的最原始的数据信息,这些信息保存了它在网络传输时的原貌,通过对这些在低层传输的原始信息的分析可以得到更多网络的信息。 一个完整的网间通信需要一个五元组来标识: 〔协议,本地地址,本地端口号,远地地址,远地端口号) 三、根本套接字系统调用 为了更好地说明套接字编程原理,下面给出几个根本套接字系统调用说明。 1. 创立套接字──socket(〕  应用程序在使用套接字前,首先必须拥有一个套接字,系统调用socket(〕向应用程序提供创立套接字的手段,其调用格式如下: SOCKET PASCAL FAR socket(int af, int type, int protocol);  参数af:指定通信发生的区域,UNIX系统支持的地址族有:AF_UNIX、AF_INET、AF_NS等,而DOS、WINDOWS中仅支持AF_INET,它是互连网区域。 参数type:描述要建立的套接字的类型。 参数protocol:说明该套接字使用的特定协议,如果调用者不希望特别指定使用的协议,那么置为0,使用默认的协议。 根据这三个参数建立一个套接字,并将相应的资源分配给它,同时返回一个整型套接字号。 socket()系统调用实际上指定了相关五元组中的“协议〞这一元。  2. 指定本地地址──bind(〕 将本地主机地址和本地端口与所创立的套接字号联系起来,其调用格式如下: int PASCAL FAR bind(SOCKET s, const struct sockaddr FAR * name, int namelen〕;   参数s:是由socket()调用返回的套接字描述符(套接字号)。 参数name:是赋给套接字s的本地地址,其长度可变,构造随通信域的不同而不同,TCP/IP协议使用的地址构造如下: struct sockaddr_in{ short sin_family; /*AF_INET*/ u_short sin_port; /*16位端口号,网络字节顺序*/  struct in_addr sin_addr; /*32位IP地址,网络字节顺序*/   char sin_zero[8]; /*保存*/ }  网络字节顺序:不同的计算机存放多字节值的顺序不同,有的机器在起始地址先存放低位字节,有的先存高位字节。为保证数据的正确性,在网络协议中须指定网络字节顺序。TCP/IP协议使用16位整数和32位整数的高价先存格式,它们均含在协议头文件中。 参数namelen:说明了name的长度。 如果没有错误发生,bind〔〕返回0。否那么返回值SOCKET_ERROR。 3. 建立套接字连接──connect(〕与accept(〕 connect()用于建立连接。无连接的套接字进程也可以调用connect(),这样就不必每次都指定目的地址。而accept〔〕用于使效劳器等待来自某客户进程的实际连接。 int PASCAL FAR connect(SOCKET s, const struct sockaddr FAR * name, int namelen);   参数s:是欲建立连接的本地套接字描述符。 参数name:为指向对方套接字地址构造的指针。 参数namelen:对方套接字地址构造长度。 如果没有错误发生,connect()返回0。否那么返回值SOCKET_ERROR。 SOCKET PASCAL FAR accept〔SOCKET s, struct sockaddr FAR* addr, int FAR* addrlen);   参数s:为本地套接字描述符,在用做accept()调用的参数前应该先调用过listen()。 addr :指向客户方套接字地址构造的指针,用来接收连接实体的地址。 Addrlen:为客户方套接字地址构造的长度。 如果没有错误发生,accept〔)返回一个SOCKET类型的值,表示接收到的套接字的描述符。否那么返回值INVALID_SOCKET。 accept()用于面向连接效劳器。参数addr和addrlen存放客户方的地址信息。调用前,参数addr 指向一个初始值为空的地址构造,而addrlen 的初始值为0;调用accept()后,效劳器等待从编号为s的套接字上承受客户连接请求,而连接请求是由客户方的connect()调用发出的。当有连接请求到达时,accept〔)调用将请求连接队列上的第一个客户方套接字地址及长度放入addr 和addrlen,并创立一个与s有一样特性的新套接字号。新的套接字可用于处理效劳器并发请求。  四个套接字系统调用,socket(〕、bind()、connect〔)、accept(),可以完成一个完全五元相关的建立。 socket()指定五元组中的协议元。 bind()指定五元组中的本地二元,即本地主机地址和端口号,其用法与是否面向连接有关: 在效劳器方,无论是否面向连接,均要调用bind〔); 在客户方,假设采用面向连接,那么可以不调用bind(〕,而通过connect()自动完成。假设采用无连接,客户方必须使用bind()以获得一个唯一的地址。  4. 监听连接──listen() 此调用用于面向连接效劳器,说明它愿意接收连接。listen()需在accept(〕之前调用,其调用格式如下:  int PASCAL FAR listen(SOCKET s, int backlog);  参数s:标识一个本地已建立、尚未连接的套接字号,效劳器愿意从它上面接收请求。 Backlog:表示请求连接队列的最大长度,用于限制排队请求的个数,目前允许的最大值为5。 如果没有错误发生,listen()返回0。否那么它返回SOCKET_ERROR。   5. 数据传输──send(〕与recv() 当一个连接建立以后,就可以传输数据了。常用的系统调用有send(〕和recv(〕。 int PASCAL FAR send(SOCKET s, const char FAR *buf, int len, int flags); 参数s:为已连接的本地套接字描述符。 buf :指向存有发送数据的缓冲区的指针,其长度由len 指定。 Flags:指定传输控制方式,如是否发送带外数据等。 如果没有错误发生,send(〕返回总共发送的字节数。否那么它返回SOCKET_ERROR。 int PASCAL FAR recv(SOCKET s, char FAR *buf, int len, int flags〕;  参数s:为已连接的套接字描述符。 Buf:指向接收输入数据缓冲区的指针,其长度由len 指定。 Flags:指定传输控制方式,如是否接收带外数据等。 如果没有错误发生,recv〔)返回总共接收的字节数。如果连接被关闭,返回0。否那么它返回SOCKET_ERROR。 ﻬ**  Flags参数可以是0或者是以下的组合: MSG_DONTROUTE:不查找路由表。是send函数使用的标志,这个标志告诉IP协议,目的主机在本地网络上面,没有必要查找路由表。 MSG_OOB:表示可以接收和发送带外的数据。 MSG_PEEK:是recv函数的使用标志,表示只是从系统缓冲区中读取内容,而不去除系统缓冲区的内容。这样下次读的时候,仍然是一样的内容。一般在有多个进程读写数据时可以使用这个标志。 MSG_WAITAL:是recv函数的使用标志,表示等到所有的信息到达时才返回。使用这个标志的时候recv会一直阻塞,直到指定的条件满足,或者是发生了错误。 1〕当读到了指定的字节时,函数正常返回,返回值等于len。 2〕当读到了文件的结尾时,函数正常返回,返回值小于len。 3)当操作发生错误时,返回-1,且设置错误为相应的错误号(errno)。 ﻬ6.数据传输——sendto〔)与recvfrom〔)    Sendto()用于在无连接套接字上发送消息。Recvfrom()可以记录发送者的地址,该地址与sendto所指定的地址构造完全一样。 int sendto(int sockfd,const void *msg,int len,unsigned int flags,struct sockaddr *to,int tolen)  int recvfrom(int sockfd,void *buf,int len,unsigned int flags,struct sockaddr * from,int *fromlen) sockfd: 表示套接字描述符。 buf: 发送或接收的缓冲区。 len: 缓冲区及大小。 recvfrom负责从sockfd接收数据,如果from不是NULL,那么在from里面存储了信息来源的情况,如果对信息的来源不感兴趣,可以将from和fromlen设置为NULL。 sendto负责向to发送信息,此时在to里面存储了接收信息方的详细信息。 7.数据传输——recvmsg〔)和sendmsg() recvmsg和sendmsg的功能类似于recvfrom和sendto,只不过将一些信息放入了一个构造中。 int recvmsg〔int sockfd,struct msghdr *msg,int flags) int sendmsg(int sockfd,struct msghdr *msg,int flags)  struct msghdr { void *msg_name;       int msg_namelen;       struct iovec *msg_iov;          int msg_iovlen;       void *msg_control; int msg_controllen;       int msg_flags; }  struct iovec    { void *iov_base; /* 缓冲区开场的地址 */    size_t iov_len; /* 缓冲区的长度   */ }  msg_name和 msg_namelen当套接字是非面向连接时(UDP),它们存储接收和发送方的地址信息。msg_name实际上是一个指向struct sockaddr的指针,msg_name是构造的长度。当套接字是面向连接时,这两个值应设为NULL. msg_iov和msg_iovlen指出承受和发送的缓冲区内容。msg_iov是一个构造指针,msg_iovlen指出这个构造数组的大小。 msg_control和msg_controllen这两个变量是用来接收和发送控制数据时的 msg_flags指定承受和发送的操作选项,和recv,send的选项一样。 8.关闭套接字──closesocket〔) 与Shutdown() closesocket〔)关闭套接字s,并释放分配给该套接字的资源;如果s涉及一个翻开的TCP连接,那么该连接被释放。closesocket〔〕的调用格式如下:   BOOL PASCAL FAR closesocket(SOCKET s〕; 参数s:待关闭的套接字描述符。 如果没有错误发生,closesocket()返回0。否那么返回值SOCKET_ERROR。  int shutdown(int sockfd,int howto)  TCP连接是双向的〔是可读写的),当我们使用close时,会把读写通道都关闭,有时侯我们希望只关闭一个方向,这个时候我们可以使用shutdown。针对不同的howto,系统回采取不同的关闭方式. howto=0这个时候系统会关闭读通道,但是可以继续写。 howto=1关闭写通道,但是可以继续读。 howto=2关闭读写通道。 9.输入/输出多路复用──select() select()调用用来检测一个或多个套接字的状态。对每一个套接字来说,这个调用可以请求读、写或错误状态方面的信息。请求给定状态的套接字集合由一个fd_set构造指示。在返回时,此构造被更新,以反映那些满足特定条件的套接字的子集,同时, select()调用返回满足条件的套接字的数目,其调用格式如下: int PASCAL FAR select(int nfds, fd_set FAR * readfds, fd_set FAR * writefds, fd_set FAR * exceptfds, const struct timeval FAR * timeout); 参数nfds:指明被检查的套接字描述符的值域,此变量一般被忽略。   参数readfds:指向要做读检测的套接字描述符集合的指针,调用者希望从中读取数据。 参数writefds:指向要做写检测的套接字描述符集合的指针。 Exceptfds:指向要检测是否出错的套接字描述符集合的指针。 Timeout:指向select(〕函数等待的最大时间,如果设为NULL那么为阻塞操作。 select(〕返回包含在fd_set构造中已准备好的套接字描述符的总数目,或者是发生错误那么返回SOCKET_ERROR。 四、数据转换和网络信息函数  1、字节顺序转换函数 在网络上面有着许多类型的机器,这些机器在表示数据的字节顺序是不同的, 比方i386芯片是低字节在内存地址的低端,高字节在高端,而alpha芯片却相反。 为了统一起来,有专门的字节顺序转换函数。 unsigned long int htonl〔unsigned long int hostlong) unsigned short int htons(unisgned short int hostshort) unsigned long int ntohl(unsigned long int netlong) unsigned short int ntohs(unsigned short int netshort) 在这四个转换函数中,h 代表host, n 代表 network。s 代表short, l 代表long,第一个函数的意义是将本机器上的long数据转化为网络上的long, 其它几个函数的意义类似。 2、IP和域名的转换 在网络上标志一台机器可以用IP或者是用域名,那么怎么进展转换呢?    struct hostent *gethostbyname〔const char *hostname) struct hostent *gethostbyaddr(const char *addr,int len,int type) 在中有struct hostent的定义 struct hostent{   char *h_name;   /* 主机的正式名称  */   char *h_aliases; /* 主机的别名 */   int  h_addrtype;/* 主机的地址类型 AF_INET*/ int   h_length; /* 主机的地址长度  对于IP4 是4字节32位*/   char **h_addr_list;     /* 主机的IP地址列表 */  } #define h_addr h_addr_list[0]  /* 主机的第一个IP地址*/ gethostbyname可以将机器名(如 linux.yessun.com〕转换为一个构造指针.在这个构造里面储存了域名的信息 gethostbyaddr可以将一个32位的IP地址(C0A80001)转换为构造指针.   这两个函数失败时返回NULL 且设置h_errno错误变量,调用h_strerror()可以得到详细的出错信息 3、字符串的IP和32位的IP转换. 在网络上面IP地址都用点分十进制数字表示(如:192.168.0.1〕,而在struct in_addr构造中用的是32位的IP,为了转换我们可以使用下面两个函数: int inet_aton(const char *cp,struct in_addr *inp〕  char *inet_ntoa(struct in_addr in) 函数里面a代表ascii,n代表network。第一个函数表示将a.b.c.d的IP转换为32位的IP,,存储在 inp指针里面。第二个是将32位IP转换为a.b.c.d的格式。   4、效劳信息函数 在网络程序里面我们有时候需要知道端口、IP和效劳信息,这个时候可以使用以下几个函数 int getsockname(int sockfd,struct sockaddr *localaddr,int *addrlen)  int getpeername〔int sockfd,struct sockaddr *peeraddr, int *addrlen)  struct servent *getservbyname(const char *servname,const char *protoname)  struct servent *getservbyport〔int port,const char *protoname)  struct servent      {      char *s_name; /* 正式效劳名 */       char **s_aliases;  /* 别名列表 */      int s_port;    /* 端口号 */      char *s_proto;  /* 使用的协议 */   } 五、典型调用时序图 1 面向连接的套接字的系统调用时序图 ﻬ2 无连接协议的套接字调用时序图  六、附件:例如程序: 1.VC++ Socket编程 简单的Tcp/ip效劳器 #include <windows.h> #include <iostream.h> #include <winsock.h> #define NO_FLAGS_SET 0 #define PORT (u_short〕 44965 #define MAXBUFLEN 256 INT main(VOID) { WSADATA Data;   SOCKADDR_IN serverSockAddr;  SOCKADDR_IN clientSockAddr; SOCKET serverSocket; SOCKET clientSocket; int addrLen=sizeof(SOCKADDR_IN);   int status; int numrcv;  char buffer[MAXBUFLEN]; /* initialize the Windows Socket DLL */   status=WSAStartup〔MAKEWORD(1, 1), &Data); /*初始化Winsock DLL if (status != 0)   cerr << "ERROR: WSAStartup unsuccessful" << endl;   /* zero the sockaddr_in structure */ memset(&serverSockAddr, 0,sizeof(serverSockAddr)〕; /* specify the port portion of the address */   serverSockAddr.sin_port=htons(PORT); /* specify the address family as Internet */ serverSockAddr.sin_family=AF_INET; /* specify that the address does not matter */ /*INADDR_ANY 的具体含义是,绑定到0.0.0.0。此时,对所有的地址都将是有效的 serverSockAddr.sin_addr.s_addr=htonl(INADDR_ANY〕;  /* create a socket */  serverSocket=socket(AF_INET, SOCK_STREAM, 0〕;   if (serverSocket == INVALID_SOCKET)   cerr << "ERROR: socket unsuccessful" << endl; /* associate the socket with the address */ status=bind〔serverSocket, 〔LPSOCKADDR) &serverSockAddr,   sizeof(serverSockAddr));  if 〔status == SOCKET_ERROR)   cerr << "ERROR: bind unsuccessful" << endl;  /* allow the socket to take connections */   status=listen(serverSocket, 1); if (status == SOCKET_ERROR) cerr << "ERROR: listen unsuccessful" << endl; /* accept the connection request when one is received */   clientSocket=accept(serverSocket,  (LPSOCKADDR) &clientSockAddr,   &addrLen);  cout << "Got the connection..." << endl; while〔1〕 {     numrcv=recv(clientSocket, buffer,     MAXBUFLEN, NO_FLAGS_SET); if (〔numrcv == 0〕 || (numrcv == SOCKET_ERROR〕)   { cout << "Connection terminated." << endl;      status=closesocket(clientSocket);      if (status == SOCKET_ERROR)   cerr << "ERROR: closesocket unsuccessful"   << endl;      status=WSACleanup〔〕;    if (status == SOCKET_ERROR)   cerr << "ERROR: WSACleanup unsuccessful"      << endl;    return(1); }    cout << buffer << endl;   } /* while */ 2.VC++ Socket编程 简单的Tcp/ip客户端 #include <windows.h> #include <iostream.h> #include <winsock.h> #define NO_FLAGS_SET 0 #define PORT (u_short〕 44965 #define DEST_IP_ADDR "192.168.10.158" //Server address INT main(VOID) {   WSADATA Data; SOCKADDR_IN destSockAddr; SOCKET destSocket; unsigned long destAddr;   int status; int numsnt;  char *toSendtxt="Test String";   /* initialize the Windows Socket DLL */ status=WSAStartup(MAKEWORD(1, 1), &Data); if (status != 0)   cerr << "ERROR: WSAStartup unsuccessful"   << endl; /* convert IP address into in_addr form */  destAddr=inet_addr(DEST_IP_ADDR〕; /* copy destAddr into sockaddr_in structure */   memcpy(&destSockAddr.sin_addr, &destAddr, sizeof〔destAddr〕〕; /* specify the port portion of the address */ destSockAddr.sin_port=htons〔PORT〕;   /* specify the address family as Internet */ destSockAddr.sin_family=AF_INET;   /* create a socket */ destSocket=socket(AF_INET, SOCK_STREAM, 0〕; if (destSocket == INVALID_SOCKET) {    cerr << "ERROR: socket unsuccessful" << endl;   status=WSACleanup〔);   if 〔status == SOCKET_ERROR)   cerr << "ERROR: WSACleanup unsuccessful"   << endl;     return〔1); } cout << "Trying to connect to IP Address: " << DEST_IP_ADDR << endl; /* connect to the server */ status=connect〔destSocket,   (LPSOCKADDR) &destSockAddr,   sizeof(destSockAddr〕); if (status == SOCKET_ERROR) { cerr << "ERROR: connect unsuccessful" << endl;   status=closesocket(destSocket〕;   if (status == SOCKET_ERROR)  cerr << "ERROR: closesocket unsuccessful"     << endl; status=WSACleanup〔); if 〔status == SOCKET_ERROR)   cerr << "ERROR: WSACleanup unsuccessful"       << endl; return(1);   }   cout << "Connected..." << endl; while(1〕   {   cout << "Sending..." << endl;    numsnt=send(destSocket, toSendtxt,   strlen(toSendtxt) + 1, NO_FLAGS_SET);   if (numsnt != 〔int〕strlen〔toSendtxt) + 1)   { cout << "Connection terminated" << endl;      status=closesocket〔destSocket);    if (status == SOCKET_ERROR) cerr << "ERROR: closesocket unsuccessful"      << endl; status=WSACleanup〔);     if (status == SOCKET_ERROR)     cerr << "ERROR: WSACleanup unsuccessful"       << endl;     return〔1);    }  /* Wait before sending the message again */ Sleep〔4800);  } /* while */ }
    展开阅读全文
    提示  咨信网温馨提示:
    1、咨信平台为文档C2C交易模式,即用户上传的文档直接被用户下载,收益归上传人(含作者)所有;本站仅是提供信息存储空间和展示预览,仅对用户上传内容的表现方式做保护处理,对上载内容不做任何修改或编辑。所展示的作品文档包括内容和图片全部来源于网络用户和作者上传投稿,我们不确定上传用户享有完全著作权,根据《信息网络传播权保护条例》,如果侵犯了您的版权、权益或隐私,请联系我们,核实后会尽快下架及时删除,并可随时和客服了解处理情况,尊重保护知识产权我们共同努力。
    2、文档的总页数、文档格式和文档大小以系统显示为准(内容中显示的页数不一定正确),网站客服只以系统显示的页数、文件格式、文档大小作为仲裁依据,个别因单元格分列造成显示页码不一将协商解决,平台无法对文档的真实性、完整性、权威性、准确性、专业性及其观点立场做任何保证或承诺,下载前须认真查看,确认无误后再购买,务必慎重购买;若有违法违纪将进行移交司法处理,若涉侵权平台将进行基本处罚并下架。
    3、本站所有内容均由用户上传,付费前请自行鉴别,如您付费,意味着您已接受本站规则且自行承担风险,本站不进行额外附加服务,虚拟产品一经售出概不退款(未进行购买下载可退充值款),文档一经付费(服务费)、不意味着购买了该文档的版权,仅供个人/单位学习、研究之用,不得用于商业用途,未经授权,严禁复制、发行、汇编、翻译或者网络传播等,侵权必究。
    4、如你看到网页展示的文档有www.zixin.com.cn水印,是因预览和防盗链等技术需要对页面进行转换压缩成图而已,我们并不对上传的文档进行任何编辑或修改,文档下载后都不会有水印标识(原文档上传前个别存留的除外),下载后原文更清晰;试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓;PPT和DOC文档可被视为“模板”,允许上传人保留章节、目录结构的情况下删减部份的内容;PDF文档不管是原文档转换或图片扫描而得,本站不作要求视为允许,下载前可先查看【教您几个在下载文档中可以更好的避免被坑】。
    5、本文档所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用;网站提供的党政主题相关内容(国旗、国徽、党徽--等)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
    6、文档遇到问题,请及时联系平台进行协调解决,联系【微信客服】、【QQ客服】,若有其他问题请点击或扫码反馈【服务填表】;文档侵犯商业秘密、侵犯著作权、侵犯人身权等,请点击“【版权申诉】”,意见反馈和侵权处理邮箱:1219186828@qq.com;也可以拔打客服电话:0574-28810668;投诉电话:18658249818。

    开通VIP折扣优惠下载文档

    自信AI创作助手
    关于本文
    本文标题:套接字编程基本原理讲解.doc
    链接地址:https://www.zixin.com.cn/doc/2626900.html
    页脚通栏广告

    Copyright ©2010-2025   All Rights Reserved  宁波自信网络信息技术有限公司 版权所有   |  客服电话:0574-28810668    微信客服:咨信网客服    投诉电话:18658249818   

    违法和不良信息举报邮箱:help@zixin.com.cn    文档合作和网站合作邮箱:fuwu@zixin.com.cn    意见反馈和侵权处理邮箱:1219186828@qq.com   | 证照中心

    12321jubao.png12321网络举报中心 电话:010-12321  jubao.png中国互联网举报中心 电话:12377   gongan.png浙公网安备33021202000488号  icp.png浙ICP备2021020529号-1 浙B2-20240490   


    关注我们 :微信公众号  抖音  微博  LOFTER               

    自信网络  |  ZixinNetwork