收藏 分享(赏)

套接字编程原理讲义.doc

上传人:dzzj200808 文档编号:2262985 上传时间:2018-09-08 格式:DOC 页数:27 大小:138.50KB
下载 相关 举报
套接字编程原理讲义.doc_第1页
第1页 / 共27页
套接字编程原理讲义.doc_第2页
第2页 / 共27页
套接字编程原理讲义.doc_第3页
第3页 / 共27页
套接字编程原理讲义.doc_第4页
第4页 / 共27页
套接字编程原理讲义.doc_第5页
第5页 / 共27页
点击查看更多>>
资源描述

1、套 接 字 编 程 原 理一 、 客 户 机 /服 务 器 模 式在 网 络 中 最 常 用 的 通 信 模 式 是 客 户 机 /服 务 器 模 式(Client/Server 模 式 或 C/S 模 式 )。服 务 器 方 要 先 启 动 , 并 监 听 指 定 端 口 , 等 待 客 户 端 的 请 求 ,根 据 客 户 端 的 请 求 提 供 相 应 服 务 。二 、 基 本 套 接 字一般来说,要进行网络通信,必须要在网络的每一端都要建立一个套接字,两个套接字之间是可以建立连接的,也是可以无连接的,并通过对套接字的“ 读” 、 “写”操作实现网络通信功能。类似于文件的打开、读、写、关

2、闭的方式。套接字有三种类型:数据流套接字(SOCK_STREAM):对应 TCP 协议。数据报套接字(SOCK_DGRAM):对应 UDP 协议。原始套接字(SOCK_RAW) 。通过使用原始套接字,可以将网卡设为混杂模式。并且可以捕获到的数据包不仅仅是单纯的数据信息,而是包含有 IP 头、 TCP 头等信息头的最原始的数据信息,这些信息保留了它在网络传输时的原貌,通过对这些在低层传输的原始信息的分析可以得到更多网络的信息。一个完整的网间通信需要一个五元组来标识: (协议,本地地址,本地端口号,远地地址,远地端口号)三、基本套接字系统调用 为了更好地说明套接字编程原理,下面给出几个基本套接字系

3、统调用说明。 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:说明该套接字使用的特定协议,如果调用者不希望特别指定使用的协议,则置为

4、0,使用默认的协议。根据这三个参数建立一个套接字,并将相应的资源分配给它,同时返回一个整型套接字号。socket()系统调用实际上指定了相关五元组中的“协议”这一元。 2 指定本地地址bind() 将本地主机地址和本地端口与所创建的套接字号联系起来,其调用格式如下: int PASCAL FAR bind(SOCKET s, const struct sockaddr FAR * name, int namelen); 参数 s:是由 socket()调用返回的套接字描述符(套接字号)。参数 name:是赋给套接字 s 的本地地址,其长度可变,结构随通信域的不同而不同,TCP/IP 协议使用的

5、地址结构如下:struct sockaddr_in short sin_family; /*AF_INET*/ u_short sin_port; /*16 位端口号,网络字节顺序*/ struct in_addr sin_addr; /*32 位 IP 地址,网络字节顺序*/ char sin_zero8; /*保留*/ 网络字节顺序:不同的计算机存放多字节值的顺序不同,有的机器在起始地址先存放低位字节,有的先存高位字节。为保证数据的正确性,在网络协议中须指定网络字节顺序。TCP/IP 协议使用 16 位整数和 32 位整数的高价先存格式,它们均含在协议头文件中。参数 namelen:表明了

6、 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:为指向对方套接字地址结构的指针。参数 namel

7、en:对方套接字地址结构长度。 如果没有错误发生,connect()返回 0。否则返回值SOCKET_ERROR。 SOCKET PASCAL FAR accept(SOCKET s, struct sockaddr FAR* addr, int FAR* addrlen); 参数 s:为本地套接字描述符,在用做 accept()调用的参数前应该先调用过 listen()。addr :指向客户方套接字地址结构的指针,用来接收连接实体的地址。Addrlen:为客户方套接字地址结构的长度。如果没有错误发生,accept()返回一个 SOCKET 类型的值,表示接收到的套接字的描述符。否则返回值 I

8、NVALID_SOCKET。 accept()用于面向连接服务器。参数 addr 和 addrlen 存放客户方的地址信息。调用前,参数 addr 指向一个初始值为空的地址结构,而addrlen 的初始值为 0;调用 accept()后,服务器等待从编号为 s 的套接字上接受客户连接请求,而连接请求是由客户方的 connect()调用发出的。当有连接请求到达时,accept()调用将请求连接队列上的第一个客户方套接字地址及长度放入 addr 和 addrlen,并创建一个与 s 有相同特性的新套接字号。新的套接字可用于处理服务器并发请求。 四个套接字系统调用,socket()、bind()、c

9、onnect()、accept(),可以完成一个完全五元相关的建立。socket()指定五元组中的协议元。bind()指定五元组中的本地二元,即本地主机地址和端口号,其用法与是否面向连接有关:在服务器方,无论是否面向连接,均要调用 bind();在客户方,若采用面向连接,则可以不调用 bind(),而通过connect()自动完成。若采用无连接,客户方必须使用 bind()以获得一个唯一的地址。 4 监听连接listen() 此调用用于面向连接服务器,表明它愿意接收连接。listen()需在accept()之前调用,其调用格式如下: int PASCAL FAR listen(SOCKET s

10、, 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

11、 :指向存有发送数据的缓冲区的指针,其长度由 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。

12、* Flags 参数可以是 0 或者是以下的组合:MSG_DONTROUTE:不查找路由表。是 send 函数使用的标志,这个标志告诉 IP 协议,目的主机在本地网络上面,没有必要查找路由表。MSG_OOB:表示可以接收和发送带外的数据。MSG_PEEK:是 recv 函数的使用标志,表示只是从系统缓冲区中读取内容,而不清除系统缓冲区的内容。这样下次读的时候,仍然是一样的内容。一般在有多个进程读写数据时可以使用这个标志。MSG_WAITAL:是 recv 函数的使用标志,表示等到所有的信息到达时才返回。使用这个标志的时候 recv 会一直阻塞,直到指定的条件满足,或者是发生了错误。 1)当读到

13、了指定的字节时,函数正常返回,返回值等于 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 s

14、ockfd,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()recv

15、msg 和 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

16、; 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 和

17、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 shu

18、tdown(int sockfd,int howto) TCP 连 接 是 双 向 的 (是 可 读 写 的 ), 当 我 们 使 用 close 时 , 会把 读 写 通 道 都 关 闭 , 有 时 侯 我 们 希 望 只 关 闭 一 个 方 向 , 这 个 时候 我 们 可 以 使 用 shutdown。 针 对 不 同 的 howto, 系 统 回 采 取 不同 的 关 闭 方 式 . howto=0 这 个 时 候 系 统 会 关 闭 读 通 道 , 但 是 可 以 继 续 写 。howto=1 关 闭 写 通 道 , 但 是 可 以 继 续 读 。 howto=2 关 闭 读 写 通

19、 道 。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 F

20、AR * timeout); 参数 nfds:指明被检查的套接字描述符的值域,此变量一般被忽略。 参数 readfds:指向要做读检测的套接字描述符集合的指针,调用者希望从中读取数据。参数 writefds:指向要做写检测的套接字描述符集合的指针。Exceptfds:指向要检测是否出错的套接字描述符集合的指针。Timeout:指向 select()函数等待的最大时间,如果设为 NULL 则为阻塞操作。select()返回包含在 fd_set 结构中已准备好的套接字描述符的总数目,或者是发生错误则返回 SOCKET_ERROR。 四 、 数 据 转 换 和 网 络 信 息 函 数 1、 字 节

21、顺 序 转 换 函 数在 网 络 上 面 有 着 许 多 类 型 的 机 器 ,这 些 机 器 在 表 示 数 据 的 字节 顺 序 是 不 同 的 , 比 如 i386 芯 片 是 低 字 节 在 内 存 地 址 的 低 端 ,高字 节 在 高 端 ,而 alpha 芯 片 却 相 反 。 为 了 统 一 起 来 , 有 专 门 的 字节 顺 序 转 换 函 数 。 unsigned long int htonl(unsigned long int hostlong) unsigned short int htons(unisgned short int hostshort) unsigne

22、d 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 或 者

23、 是 用 域 名 , 那 么 怎 么进 行 转 换 呢 ? 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*/ in

24、t h_length; /* 主 机 的 地 址 长 度 对 于 IP4 是 4 字 节32 位 */ char *h_addr_list; /* 主 机 的 IP 地 址 列 表 */ #define h_addr h_addr_list0 /* 主 机 的 第 一 个 IP 地 址 */ gethostbyname 可 以 将 机 器 名 (如 )转 换 为 一个 结 构 指 针 .在 这 个 结 构 里 面 储 存 了 域 名 的 信 息 gethostbyaddr 可 以 将 一 个 32 位 的 IP 地 址 (C0A80001)转 换为 结 构 指 针 . 这 两 个 函 数 失

25、败 时 返 回 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_nto

26、a(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

27、*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; /* 别 名 列 表

28、 */int s_port; /* 端 口 号 */char *s_proto; /* 使 用 的 协 议 */ 五 、 典 型 调 用 时 序 图1 面 向 连 接 的 套 接 字 的 系 统 调 用 时 序 图2 无 连 接 协 议 的 套 接 字 调 用 时 序 图六 、 附 件 : 示 例 程 序 :1 VC+ Socket 编 程 简 单 的 Tcp/ip 服 务 器#include #include #include #define NO_FLAGS_SET 0#define PORT (u_short) 44965#define MAXBUFLEN 256INT main(VOI

29、D)WSADATA Data;SOCKADDR_IN serverSockAddr;SOCKADDR_IN clientSockAddr;SOCKET serverSocket;SOCKET clientSocket;int addrLen=sizeof(SOCKADDR_IN);int status;int numrcv;char bufferMAXBUFLEN;/* initialize the Windows Socket DLL */status=WSAStartup(MAKEWORD(1, 1), /*初始化 Winsock DLLif (status != 0)cerr #incl

30、ude #include #define NO_FLAGS_SET 0#define PORT (u_short) 44965#define DEST_IP_ADDR “192.168.10.158“ /Server addressINT 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 */

31、status=WSAStartup(MAKEWORD(1, 1), 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(/* specify the port portion of the address */destSockAddr.sin_port=htons(PORT);

32、/* 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;

33、return(1);cout “Trying to connect to IP Address: “ DEST_IP_ADDR endl;/* connect to the server */status=connect(destSocket,(LPSOCKADDR) if (status = SOCKET_ERROR)cerr “ERROR: connect unsuccessful“ endl;status=closesocket(destSocket);if (status = SOCKET_ERROR)cerr “ERROR: closesocket unsuccessful“ end

34、l;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 */

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高等教育 > 大学课件

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报