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

时间:2021-08-17 02:45:20

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

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

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

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

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

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

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

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

简单的server代码实现:

 #include"winsock2.h"
#include<iostream>
using namespace std;
//This line is very important #pragma comment(lib,"ws2_32.lib")
int main()
{
WSADATA wsaData;
SOCKET ListeningSocket;
SOCKET NewConnection;
SOCKADDR_IN ServerAddr;
SOCKADDR_IN ClientAddr;
int ClientAddrLen;
int Port = ;
int Ret;
char DataBuffer[]; if ((Ret = WSAStartup(MAKEWORD(,), &wsaData)) != )
{
cout<<"WSAStartup failed with error "<<Ret<<endl;
//here no WSACleanup,because we do not create anything;
return -;
} // Create a new socket to listening for client connections.
ListeningSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if ( INVALID_SOCKET == ListeningSocket)
{
cout<<"Socket failed with error "<<WSAGetLastError()<<endl;
WSACleanup();
return -;
} ServerAddr.sin_family = AF_INET;
ServerAddr.sin_addr.s_addr = htonl(INADDR_ANY);
ServerAddr.sin_port = htons(Port); //to bind
if (bind(ListeningSocket, (SOCKADDR *)&ServerAddr, sizeof(ServerAddr)) == SOCKET_ERROR)
{
cout<<"Binding failed with error "<<WSAGetLastError()<<endl;
closesocket(ListeningSocket);
WSACleanup();
return -;
} // Listen for client connections. We used a backlog of 5 which is
// normal for many applications. if (listen(ListeningSocket,) == SOCKET_ERROR)
{
cout<<"Listen failed with error "<<WSAGetLastError()<<endl;
closesocket(ListeningSocket);
WSACleanup();
return -;
} cout<<"** We are waiting for a connection on port "<<Port<<"**"<<endl; //accep a connection when one arrives NewConnection = accept(ListeningSocket,(SOCKADDR*)&ClientAddr,&ClientAddrLen);
if (INVALID_SOCKET == NewConnection)
{
cout<<"Accept failed with error "<<WSAGetLastError()<<endl;
closesocket(ListeningSocket);
WSACleanup();
return -;
} cout<<"** We successfully got a connection from "<<inet_ntoa(ClientAddr.sin_addr)
<<":port "<<ntohs(ClientAddr.sin_port)<<"!!**"<<endl; closesocket(ListeningSocket);
cout<<"** We are waiting for data...**\n"; Ret = recv(NewConnection,DataBuffer,sizeof(DataBuffer),);
if (SOCKET_ERROR == Ret)
{
cout<<"Recv failed with error "<<WSAGetLastError()<<endl;
closesocket(NewConnection);
WSACleanup();
return -;
} cout<<"**We have successfully recieve "<<Ret<<" Byte(s) data!**\n"; cout<<"**We are going to close the client connection...**\n"; closesocket(NewConnection);
WSACleanup(); return ;
}

Server Code

客户端的实现:

 #include"winsock2.h"
#include<iostream>
using namespace std;
//This line is very important #pragma comment(lib,"ws2_32.lib")
int main(int argc, char **argv)
{
WSADATA wsaData;
SOCKET s;
SOCKADDR_IN ServerAddr;
int Port = ;
int Ret; if (argc <= )
{
cout<<"USAGE: tcpclient <Server IP address>.\n";
return -;
} // Initialize Winsock version 2.2 if ((Ret = WSAStartup(MAKEWORD(,), &wsaData)) != )
{
cout<<"WSAStartup failed with error "<<Ret<<endl;
return -;
} // Create a new socket to make a client connection. s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (INVALID_SOCKET == s)
{
cout << "socket failed with error " << WSAGetLastError()<<endl;
WSACleanup();
return -;
} ServerAddr.sin_family = AF_INET;
ServerAddr.sin_port = htons(Port);
ServerAddr.sin_addr.s_addr = inet_addr(argv[]); // Make a connection to the server with socket s. cout<< "We are trying to connect to " << inet_ntoa(ServerAddr.sin_addr)
<< ":" << htons(ServerAddr.sin_port) << "...\n"; if (connect(s, (SOCKADDR *) &ServerAddr, sizeof(ServerAddr))
== SOCKET_ERROR)
{
cout << "connect failed with error " << WSAGetLastError() << endl;
closesocket(s);
WSACleanup();
return -;
} cout << "Our connection succeeded.\n"; cout << "We will now try to send a hello message.\n"; if ((Ret = send(s, "Hello", , )) == SOCKET_ERROR)
{
cout << "send failed with error " << WSAGetLastError()<<endl;
closesocket(s);
WSACleanup();
return -;
} cout << "We successfully sent " << Ret << " byte(s).\n"; // When you are finished sending and receiving data on socket s,
// you should close the socket. cout << "We are closing the connection.\n"; closesocket(s); // When your application is finished handling the connection, call
// WSACleanup. WSACleanup();
return ;
}

Client Code

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

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

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

通过报错信息可以知道是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。。??