• Winsock网络编程笔记(2)----基于TCP的server和client


    今天抽空看了一些简单的东西,主要是对服务器server和客户端client的简单实现。

    面向连接的server和client,其工作流程如下图所示:

    服务器和客户端将按照这个流程就行开发。。(个人觉得:通过这个流程图,Server应该要先于Client启动,不然Client的connect函数的执行就会出错啦,不知道我的个人感觉对不对,后面试试就知道了。。O(∩_∩)O~

    注意:上图的Server和Client的工作流程是基于面向有连接通信的工作流程,如果是无连接的通信,则不必调用listen和accept。 在无连接的通信中,Server调用recvfrom函数来接收消息    

    在编写服务器和客户端之前,需要对TCP状态有所了解。。在server和client通信之间,二者都是通过发送/接收不同的信号来改变自己的状态,其tcp状态转换图如下:

    了解了二者的开发流程,就可以通过接口来具体实现。

    简单的server代码实现:

     1 #include"winsock2.h"
     2 #include<iostream>
     3 using namespace std;
     4 //This line is very important
     5 
     6 #pragma comment(lib,"ws2_32.lib")
     7 int main()
     8 {
     9     WSADATA              wsaData;
    10     SOCKET               ListeningSocket;
    11     SOCKET               NewConnection;
    12     SOCKADDR_IN          ServerAddr;
    13     SOCKADDR_IN          ClientAddr;
    14     int                  ClientAddrLen;
    15     int                  Port = 5150;
    16     int                  Ret;
    17     char                 DataBuffer[1024];
    18 
    19     if ((Ret = WSAStartup(MAKEWORD(2,2), &wsaData)) != 0)
    20     {
    21         cout<<"WSAStartup failed with error "<<Ret<<endl;
    22         //here no WSACleanup,because we do not create anything;
    23         return -1;
    24     }
    25 
    26     // Create a new socket to listening for client connections.
    27     ListeningSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    28     if ( INVALID_SOCKET == ListeningSocket)
    29     {
    30         cout<<"Socket failed with error "<<WSAGetLastError()<<endl;
    31         WSACleanup();
    32         return -1;
    33     }
    34 
    35     ServerAddr.sin_family = AF_INET;
    36     ServerAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    37     ServerAddr.sin_port = htons(Port);
    38 
    39     //to bind
    40     if (bind(ListeningSocket, (SOCKADDR *)&ServerAddr, sizeof(ServerAddr)) == SOCKET_ERROR)
    41     {
    42         cout<<"Binding failed with error "<<WSAGetLastError()<<endl;
    43         closesocket(ListeningSocket);
    44         WSACleanup();
    45         return -1;
    46     }
    47 
    48     // Listen for client connections. We used a backlog of 5 which is
    49     // normal for many applications.
    50 
    51     if (listen(ListeningSocket,5) == SOCKET_ERROR)
    52     {
    53         cout<<"Listen failed with error "<<WSAGetLastError()<<endl;
    54         closesocket(ListeningSocket);
    55         WSACleanup();
    56         return -1;
    57     }
    58 
    59     cout<<"** We are waiting for a connection on port "<<Port<<"**"<<endl;
    60 
    61     //accep a connection when one arrives
    62 
    63     NewConnection = accept(ListeningSocket,(SOCKADDR*)&ClientAddr,&ClientAddrLen);
    64     if (INVALID_SOCKET == NewConnection)
    65     {
    66         cout<<"Accept failed with error "<<WSAGetLastError()<<endl;
    67         closesocket(ListeningSocket);
    68         WSACleanup();
    69         return -1;
    70     }
    71 
    72     cout<<"** We successfully got a connection from "<<inet_ntoa(ClientAddr.sin_addr)
    73         <<":port "<<ntohs(ClientAddr.sin_port)<<"!!**"<<endl;
    74 
    75     closesocket(ListeningSocket);
    76     cout<<"** We are waiting for data...**
    ";
    77 
    78     Ret = recv(NewConnection,DataBuffer,sizeof(DataBuffer),0);
    79     if (SOCKET_ERROR == Ret)
    80     {
    81         cout<<"Recv failed with error "<<WSAGetLastError()<<endl;
    82         closesocket(NewConnection);
    83         WSACleanup();
    84         return -1;
    85     }
    86 
    87     cout<<"**We have successfully recieve "<<Ret<<" Byte(s) data!**
    ";
    88 
    89     cout<<"**We are going to close the client connection...**
    ";
    90 
    91     closesocket(NewConnection);
    92     WSACleanup();
    93 
    94     return 0;
    95 }
    Server Code

    客户端的实现:

     1 #include"winsock2.h"
     2 #include<iostream>
     3 using namespace std;
     4 //This line is very important
     5 
     6 #pragma comment(lib,"ws2_32.lib")
     7 int main(int argc, char **argv)
     8 {
     9     WSADATA              wsaData;
    10     SOCKET               s;
    11     SOCKADDR_IN          ServerAddr;
    12     int                  Port = 5150;
    13     int                  Ret;
    14 
    15     if (argc <= 1)
    16     {
    17         cout<<"USAGE: tcpclient <Server IP address>.
    ";
    18         return -1;
    19     }
    20 
    21     // Initialize Winsock version 2.2
    22 
    23     if ((Ret = WSAStartup(MAKEWORD(2,2), &wsaData)) != 0)
    24     {
    25         cout<<"WSAStartup failed with error "<<Ret<<endl;
    26         return -1;
    27     }
    28 
    29     // Create a new socket to make a client connection.
    30 
    31     s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    32     if (INVALID_SOCKET == s)
    33     {
    34         cout << "socket failed with error " << WSAGetLastError()<<endl;
    35         WSACleanup();
    36         return -1;
    37     }
    38 
    39     ServerAddr.sin_family = AF_INET;
    40     ServerAddr.sin_port = htons(Port);    
    41     ServerAddr.sin_addr.s_addr = inet_addr(argv[1]);
    42 
    43     // Make a connection to the server with socket s.
    44 
    45     cout<< "We are trying to connect to " << inet_ntoa(ServerAddr.sin_addr)
    46         << ":" << htons(ServerAddr.sin_port) << "...
    ";
    47 
    48     if (connect(s, (SOCKADDR *) &ServerAddr, sizeof(ServerAddr)) 
    49         == SOCKET_ERROR)
    50     {
    51         cout << "connect failed with error " << WSAGetLastError() << endl;
    52         closesocket(s);
    53         WSACleanup();
    54         return -1;
    55     } 
    56 
    57     cout << "Our connection succeeded.
    ";
    58 
    59 
    60     cout << "We will now try to send a hello message.
    ";
    61 
    62     if ((Ret = send(s, "Hello", 5, 0)) == SOCKET_ERROR)
    63     {
    64         cout << "send failed with error " << WSAGetLastError()<<endl;
    65         closesocket(s);
    66         WSACleanup();
    67         return -1;
    68     }
    69 
    70     cout << "We successfully sent " << Ret << " byte(s).
    ";
    71 
    72     // When you are finished sending and receiving data on socket s,
    73     // you should close the socket.
    74 
    75     cout << "We are closing the connection.
    ";
    76 
    77     closesocket(s);
    78 
    79     // When your application is finished handling the connection, call
    80     // WSACleanup.
    81 
    82     WSACleanup();
    83     return 0;
    84 }
    Client Code

    咦,奇了怪了,按照书上的代码,执行起来居然报错。。这是咋回事啊。。

    运行Server时,出现这样的错误:

    通过报错信息可以知道是accept函数出错了。

    那就对症下药,哪儿错儿改哪儿。。

    上网查找资料,找到accept函数定义:

    SOCKET accept(
    __in SOCKET s,
    __out struct sockaddr *addr,
    __inout int *addrlen
    );

           第一个参数就是套接字描述符,第二个参数是,接受客户端基本信息的结构体,第三参数很重要,是准备接受结构体的大小,上面的程序 int ClientAddrLen;传进去的时候只是把未知的ClientAddrLen的地址传进去,要传进去的应该是接收这些信息的基本大小啊,所以 得加ClientAddrLen = sizeof(SOCKADDR);。这样程序就执行accept。

    注意:客户端需要在服务器运行的时候才能执行,不然会出错的。。

    运行Server和Client(需要ip作为main函数的参数,不会的自己百度怎样在VS2008中给main传递参数),二者可以连接了。。执行还没有实现二者的通信。。。呜呜~~~~(>_<)~~~~ 

    拓展

    观察Server中的recv函数和Client中send函数,我想是否可以利用无限循环的方式在Client端输入字符串,在Server端打印该字符串?----就像聊天一样,你输入一句,我这边就显示这一句。。。只是这样只能是单工方式的传输,不能从Server发送到Client端。。那是否可以考虑多线程呢,一个线程运行Server,一个线程运行Client。。??

  • 相关阅读:
    手把手教你用动软.net生成器
    三层架构的优缺点
    BLL.DAL.表现层作用
    session的属性/方法/事件
    string.IsNullOrEmpty的用法
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="Keywords" content="" />
    var ev = document.all ? window.event : e;
    《C++ Primer 4th》读书笔记 第3章-标准库类型
    《Unix环境高级编程》读书笔记 第7章-进程环境
  • 原文地址:https://www.cnblogs.com/LCCRNblog/p/3838365.html
Copyright © 2020-2023  润新知