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。。??

原文地址:https://www.cnblogs.com/LCCRNblog/p/3838365.html