1 / 42

Winsock 编程

Winsock 编程. 中科大软件学院. 实验三 socket 编程实现 SMTP/POP3 协议通信. winsock. Windows Sockets 规范本意在于提供给应用程序开发者一套简单的 API ,并让各家网络软件供应商共同遵守。 Windows Sockets 规范定义并记录了如何使用 API 与 Internet 协议族( IPS ,通常我们指的是 TCP/IP )连接,尤其要指出的是所有的 Windows Sockets 实现都支持流套接口和数据报套接口 . 应用程序调用 Windows Sockets 的 API 实现相互之间的通讯。. Socket ?.

nijole
Download Presentation

Winsock 编程

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Winsock编程 中科大软件学院 实验三 socket编程实现SMTP/POP3协议通信

  2. winsock Windows Sockets规范本意在于提供给应用程序开发者一套简单的API,并让各家网络软件供应商共同遵守。 Windows Sockets规范定义并记录了如何使用API与Internet协议族(IPS,通常我们指的是TCP/IP)连接,尤其要指出的是所有的Windows Sockets实现都支持流套接口和数据报套接口.应用程序调用Windows Sockets的API实现相互之间的通讯。

  3. Socket? Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket将复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。

  4. TCP 服务器端程序示例(一) #include <stdio.h> //引入winsock库 #include <winsock2.h> #pragma comment(lib,"WS2_32")

  5. #pragma comment(lib,"WS2_32") #pragma是标准C/C++的内容,用于向编译器传递特定参数。具体参数则与编译器有关 comment(lib,“WS2_32”)特定于VC的参数,并不通用 #pragma comment(lib,“WS2_32”)实现链接WS2_32.lib

  6. 加载、释放winsock库(WS2_32.DLL) WORD wVersion = MAKEWORD(2, 0); WSADATA wsData; if (WSAStartup(wVersion, &wsData)!=0) { printf("初始化失败!\n"); }

  7. WSAStartup()函数 int WSAStartup( WORD wVersionRequested, LPWSDATA lpWSAData ) wVersionRequested:指定要加载的winsock库的版本,高字节为次版本号,低字节为主版本号 一个指向WSADATA结构的指针,用来返回DLL库的详细信息 wVersionRequested的值可使用宏MAKEWORD(x,y)来建立,x为高字节,y为低字节

  8. 结构体WSADATA typedef struct WSAData{ WORD wVersion; //库文件建议应用程序使用的版本 WORD wHighVersion; //库文件支持的最高版本 //库描述字符串 char szDescription[WSADESCRIPTION_LEN+1]; //系统状态字符串 char szSystemStatus[WSASYS_STATUS_LEN+1]; //同时支持的最大套接字数量 unsigned short iMaxSockets; //以下两个参数在2.0版中已废弃 unsigned short iMaxUdpDg; char FAR* lpVendorInfo; } WSDATA,FAR * LPWSADATA;

  9. TCP 服务器端程序示例(二) //创建套接字 SOCKET sockSrv=socket(AF_INET,SOCK_STREAM,0); if (sockSrv== INVALID_SOCKET) { printf("Failed socket()\n"); return 1; }

  10. Socket()函数 SOCKET socket( //用来指定套接字使用的地址格式,通常使用AF_INET int af, /* 地址家族, AF_xxx */ //指定套接字的类型 int type, //配合type参数使用,指定使用的协议类型 int protocol )

  11. 套接字类型type SOCK_STREAM(RFC-793 ) 流套接字,使用tcp提供的有连接的可靠的传输 SOCK_DGRAM(RFC-768) 数据报套接字,使用udp提供无连接的不可靠的传输 SOCK_RAW 原始套接字,socket不使用特定的协议区封装它,而由程序自行处理数据报及协议首部

  12. 协议类型protocol 配合type使用,值可以是IPPROTO_TCP等 当type指定为SOCK_STREAM或SOCK_DGRAM时,因为系统已明确使用tcp和udp来工作,protocol可指定为0

  13. 填充socket addr_in结构 //填充socket addr_in结构 sockaddr_in addServer,addrClient; addServer.sin_family=AF_INET; addServer.sin_addr.S_un.S_addr=inet_addr(Local_IP); addServer.sin_port=htons(8000);

  14. sockaddr结构体 struct sockaddr{ u_short sa_family; char sa_data[14]; } Winsock为了兼容多个协议,所采用的通用的寻址方式

  15. sockaddr_in结构体 struct sockaddr_in { //地址族(指定地址格式) ,设为AF_INET short sa_family; u_short sin_port; //端口号 struct in_addr sin_addr; //IP地址 char sin_zero[8]; //空子节,设为空 } 用于TCP/IP的sockaddr

  16. sin_port 端口号通常分为三个范围 0 ~ 1023:由IANA(Internet Assigned Numbers Authority)管理,保留为公共的服务使用 1024 ~ 49151:普通用户注册的端口号 49152 ~ 65535:动态或私有的端口号

  17. sin_addr域:struct in_addr struct in_addr{ union{ struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b; struct { u_short s_w1,s_w2; } S_un_w; u_long S_addr; } S_un; };

  18. struct in_addr说明 采用联合体方式提供了32位IP地址的不同表示方法 unsigned long inet_addr(const char* cp); 将由小数点分隔的十进制IP地址字符串转化成由32位二进制数表示的IP地址 char* inet_ntoa(struct in_addr in); 将一个网络字节顺序的32位IP地址转化成字符串

  19. TCP 服务器端程序示例(三) //绑定套接字到一个本地地址 if(bind(sockSrv,(SOCKADDR*)&addServer,sizeof(SOCKADDR))!=0) { printf("端口连接失败!\n"); }

  20. bind()函数 int bind( SOCKET s; //套接字句柄 //要关联的本地地址 const struct sockaddr* name, int namelen //地址的长度 )

  21. bind()函数 错误提示: EACCES:地址受到保护,用户非超级用户。 EADDRINUSE:指定的地址已经在使用。 EBADF:sockfd参数为非法的文件描述符。 EINVAL:socket已经和地址绑定。

  22. TCP 服务器端程序示例(四) //进入监听模式 if (listen(sockSrv,5) == SOCKET_ERROR) { printf("Failed listen()\n"); }

  23. Listen()函数 int listen( //套接字句柄 SOCKET s, //监听队列中允许保持的尚未处理的最大连接数 int backlog )

  24. TCP 服务器端程序示例(五) //接受新连接 addrClient.sin_family=AF_INET; addrClient.sin_addr.S_un.S_addr=INADDR_ANY; addrClient.sin_port=htons(8000); printf("等待客户端连接……\n"); SOCKET sockConn = accept(sockSrv,(SOCKADDR*)&addrClient,&len);

  25. accept()函数 SOCKET accept( //套接字句柄 SOCKET s, //一个指向sockaddr_in结构的指针,用于获取对方地址 struct sockaddr* addr, //一个指向地址长度的指针 int * addrlen )

  26. Recv()函数 int recv( SOCKET s, char FAR* buf, int len, int flags); s:一个标识已连接套接口的描述字。 buf:用于接收数据的缓冲区。 len:缓冲区长度。 flags:指定调用方式。 如:recv(sockCli,recvBuf,len,0);

  27. TCP 服务器端程序示例(六) //向客户端发送数据 send(sockConn,sendBuf,strlen(sendBuf),0); 附:char sendBuf [] = "TCP Server Demo!\r\n";

  28. send()函数 int send( //套接字句柄 SOCKET s, //存储发送数据的缓冲区地址 const char FAR* buf, //缓冲区长度 int len, //指定了调用方式,通常设为0 int flags )

  29. TCP 客户端程序示例(一) if(connect(sockCli,(SOCKADDR*)&addClient,sizeof(SOCKADDR))!=0) { printf("连接服务器失败!\n"); }

  30. connect()函数 int connect( //套接字句柄 SOCKET s, //指向sockaddr_in结构的指针,包含了要连接的服务器的地址信息 const struct sockaddr FAR * name, //sockaddr_in结构的长度 int namelen )

  31. read和write read(int fd,void *buf,size_t nbyte) read函数是负责从fd中读取内容.当读成功时,read返回实际所读的字节数, 如果返回的值是0表示已经读到文件的结束了,小于0表示出现了错误. 如果错误为EINTR说明读是由中断引起的, 如果是ECONNREST表示网络连接出了问题. 和上面一样,我们也写一个自己的读函数. write(int fd,const void *buf,size_t nbytes) write函数将buf中的nbytes字节内容写入文件描述符fd. 成功时返回写的字节数.失败时返回-1. 并设置errno变量. 在网络程序中,当我们向套接字文件描述符写时有俩种可能. 1)write的返回值大于0,表示写了部分或者是全部的数据. 2)返回的值小于0,此时出现了错误.我们要根据错误类型来处理. 如果错误为EINTR表示在写的时候出现了中断错误. 如果为EPIPE表示网络连接出现了问题(对方已经关闭了连接). 为了处理以上的情况,我们自己编写一个写函数来处理这几种情况.

  32. recv和send 和read和write差不多.不过它们提供 了第四个参数来控制读写操作. int recv(int sockfd,void *buf,int len,int flags) int send(int sockfd,void *buf,int len,int flags) 前面的三个参数和read,write一样,第四个参数可以是0或者是以下的组合 ________________________________________ | MSG_DONTROUTE | 不查找路由表 | | MSG_OOB | 接受或者发送带外数据 | | MSG_PEEK | 查看数据,并不从系统缓冲区移走数据 | | MSG_WAITALL | 等待所有数据 | |--------------------------------------------------------------| MSG_DONTROUTE:是send函数使用的标志.这个标志告诉IP协议.目的主机在本地网络上面,没有必要查找路由表.这个标志一般用网络诊断和路由程序里面. MSG_OOB:表示可以接收和发送带外的数据. MSG_PEEK:是recv函数的使用标志,表示只是从系统缓冲区中读取内容,而不清楚系统缓冲区的内容.这样下次读的时候,仍然是一样的内容.一般在有多个进程读写数据时可以使用这个标志. MSG_WAITALL是recv函数的使用标志,表示等到所有的信息到达时才返回.使用这个标志的时候recv回一直阻塞,直到指定的条件满足,或者是发生了错误. 1)当读到了指定的字节时,函数正常返回.返回值等于len 2)当读到了文件的结尾时,函数正常返回.返回值小于len 3)当操作发生错误时,返回-1,且设置错误为相应的错误号(errno) 如果flags为0,则和read,write一样的操作

  33. 实验报告内容: 功能:通过Socket通信,实现POP3及SMTP的邮件通信服务。 代码实现提示,如: send(sockCli,"HELO\r\n",strlen("HELO\r\n"),0); 提交程序(伪)代码 要求:禁止相互拷贝 独立完成

  34. 邮件协议(参考)

  35. 邮件客户端软件(Outlook)

  36. 邮件客户端软件(Foxmail)

  37. 邮件客户端软件(网络例子)

More Related