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){}的两个参数的关系值得我进一步探究。这些内容将放到后面的博客中进一步讨论。

原创粉丝点击