WinSock-EchoServ程序学习
来源:互联网 发布:java向导被中断 编辑:程序博客网 时间:2024/06/05 02:19
//写在前面,这一部分是和之前的EchoCInt程序学习对应。
//希望通过这两个程序学习初步建立起基于winsock的计算机网络框架模型
Echo服务器程序:
服务器程序EchoServ,完成等待客户的连接,接收客户端发送来的任何数据,并将它们简单地发回客户端。服务器程序设定最多一个参数,即服务器侦听的端口号。
头、库文件引入与常量定义:
#include <stdio.h>#include <winsock2.h>#pragma comment(lib,"ws2_32")#define ECHO_DEF_PORT 7#define ECHO_BUF_SIZE 256
这一部分与之前的EchoCInt客户端程序相同。完成头文件包含、库文件链接与常量的宏定义。
命令行参数:
if (argc == 2) { port = atoi(argv[1]);}
这里进行命令行参数检查,服务器程序可以有一个参数,用来指定服务器的端口号。如果参数缺失,则使用默认端口号。
这里额外要说,这两个程序涉及到许多关于命令行的知识,我有许多欠缺,在完成这部分学习内容,应该去补充一下。
初始化WinSock、创建Socket与绑定地址和端口号:
WSAStartup(MAKEWORD(2, 0), &wsa_data);echo_soc = socket(AF_INET, SOCK_STREAM, 0);serv_addr.sin_family = AF_INET;serv_addr.sin_port = htons(port);serv_addr.sin_addr.s_addr = INADDR_ANY;result = bind(echo_soc, (struct sockaddr *)&serv_addr,sizeof(serv_addr));if (result == SOCKET_ERROR) { printf("[Echo Server] bind erro: %d\n", WSAGetLastError()); closesocket(echo_soc); return -1; }
头两句与EchoCint类似,这里要注意的是sin_addr.s_addr,即服务器地址值为INADDR_ANY,也就是说如果主机有多个网络接口,允许服务器在任意网络接口上接受客户的链接。
这里result是返回函数bind()的值,函数bind把服务器的地址和端口绑定到了socket套接口上。bind()函数接受三个参数:
1.SOCKET s:待捆绑Socket;
2.struct sockaddr far *name:赋予Socket的主机地址标识;
3.int len:name的长度,一般也是struct sockaddr_in类型的服务器地址长度。
bind()函数调用成功,返回0.返回值-1说明调用失败;Error的值有表格可查询bind()函数介绍。
侦听:
listen(echo_soc, SOMAXCONN);
listen()函数要求socket接受到达的连接,第二个参数规定了可以接受的未完成的最大连接数量,SOMAXCONN要求底层来确定合理的最大连接数量。侦听listen()只适用于面向连接的socket,比如SOCK_STREAM,该函数将socket转变到被动模式。
处理客户连接:
printf("[Echo Server] is running ... ... \n");while (1) { acpt_soc = accept(echo_soc, (struct sockaddr *)&clnt_addr, &addr_len); if (acpt_soc == INVALID_SOCKET) { printf("[Echo Server] accept error: %d\n", WSAGetLastError()); break; } result = recv(acpt_soc,recv_buf, ECHO_BUF_SIZE, 0); if (result > 0) { recv_buf[result] = 0; printf("[Echo Server] receives :\"%s\",from %s\r\n", recv_buf, inet_ntoa(clnt_addr.sin_addr)); result = send(acpt_soc, recv_buf, result, 0); } closesocket(acpt_soc);}
while(1)是一个无限循环,处理客户的连接请求,并完成与客户的通信。服务器调用accpet()函数后处于休眠状态,等待客户的连接请求。连接的过程要经过三次握手过程。注意!这里涉及到了三次握手的概念,好像是计算机网络中非常重要的概念。只有当我握手完成时,函数accept()才会返回。返回值是一个新的套接口描述符。accept()函数的第二、第三参数是对方的地址信息,地址的格式由建立连接时的地址族确定。
后面先调用recv接收客户程序发送来的数据,数据要求长度小于ECHO_BUF_SIZE字节。没有错误,就可以将结果打印出来。然后将从客户程序收到的数据原样发回给客户端。最后关闭与客户端的连接。
关闭服务器:
closesocket(echo_soc);WSACleanup();
这里要注意和客户程序不一样,这里是要程序出错才会走到这里的。关闭服务器的socket,释放WinSock的资源。
运行结果:
结合前一篇博客——EchoCInt程序学习的问题,我终于搞明白了书中是如何运行这两个程序了。
首先打开EchoServ,F5或者Ctrl+F5都可以,就能得到上一个运行结果图,然后打开cmd命令行窗口,切换到EchoCInt.exe文件所在目录中。然后输入EchoCInt.exe 127.0.0.1。这就是输入的两个参数,一个是程序名,一个是服务器地址,还可以输入服务器端口号,但是这里没有,则采用默认的服务器端口号。然后就可以如下结果。
EchoCInt接收结果:
EchoServ接收结果:
写在最后:
#include <stdio.h>#include <winsock2.h>#pragma comment(lib,"ws2_32")#define ECHO_DEF_PORT 7#define ECHO_BUF_SIZE 256int main(int argc, char **argv) { WSADATA wsa_data; SOCKET echo_soc = 0, acpt_soc = 0; struct sockaddr_in serv_addr, clnt_addr; unsigned short port = ECHO_DEF_PORT; int result = 0; int addr_len = sizeof(struct sockaddr_in); char recv_buf[ECHO_BUF_SIZE]; if (argc == 2) { port = atoi(argv[1]); } WSAStartup(MAKEWORD(2, 0), &wsa_data); echo_soc = socket(AF_INET, SOCK_STREAM, 0); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(port); serv_addr.sin_addr.s_addr = INADDR_ANY; result = bind(echo_soc, (struct sockaddr *)&serv_addr,sizeof(serv_addr)); if (result == SOCKET_ERROR) { printf("[Echo Server] bind erro: %d\n", WSAGetLastError()); closesocket(echo_soc); return -1; } listen(echo_soc, SOMAXCONN); printf("[Echo Server] is running ... ... \n"); while (1) { acpt_soc = accept(echo_soc, (struct sockaddr *)&clnt_addr, &addr_len); if (acpt_soc == INVALID_SOCKET) { printf("[Echo Server] accept error: %d\n", WSAGetLastError()); break; } result = recv(acpt_soc,recv_buf, ECHO_BUF_SIZE, 0); if (result > 0) { recv_buf[result] = 0; printf("[Echo Server] receives :\"%s\",from %s\r\n", recv_buf, inet_ntoa(clnt_addr.sin_addr)); result = send(acpt_soc, recv_buf, result, 0); } closesocket(acpt_soc); } closesocket(echo_soc); WSACleanup(); return 0;}
通过这两个程序的实践初步明白了如果利用winsock进行echo的网络通信测试。但是很多细节部分没有展开细说。此外,涉及到C++编程知识,尤其是命令行的输入与int main(int argc, char **argv){}的两个参数的关系值得我进一步探究。这些内容将放到后面的博客中进一步讨论。
- WinSock-EchoServ程序学习
- WinSock-EchoCInt程序学习
- Winsock学习 ----- Winsock初始化
- WinSock网络编程学习(一)Echo客户/服务器程序
- WinSock网络编程学习(二)计算校验和程序
- WinSock网络编程学习(三)判断主机字节序程序
- WinSock网络编程学习笔记(八):测试bind程序
- WinSock网络程序
- 第一个winSock程序
- Winsock使用之Winsock服务器程序
- Winsock学习笔记1:Winsock基本函数
- 转贴:WinSock学习笔记
- WinSock学习笔记
- WinSock学习笔记
- WINSOCK API 学习笔记
- WinSock学习笔记
- WinSock学习笔记
- WinSock学习笔记
- p219 14.输入10个同学的5门课成绩,分别用函数实现。。。
- java调用Neo4j和ES接口的一些使用笔记
- 大整数的加法
- 通向架构师的道路(第一天)之Apache整合Tomcat
- div标签、html/css基本样式、canvas画布
- WinSock-EchoServ程序学习
- 【LeetCode】228.Summary Ranges(Medium)解题报告
- 最省心的Python版本和第三方库管理——初探Anaconda
- 淘淘商城内容服务发布
- Oracle操作字符串常用函数记录
- 指针和引用(int*、int&、int*&、int&*、int**)
- Ajax+jquery 的前后台交互学习
- C++ const 修饰成员函数
- Codeforces 888E(折半查找)