c++多线程实现网络中的进程tcp/ip通信

socket 网络编程(windows版)

c++多线程实现网络中的进程tcp/ip通信

进程间通信(IPC)有很多种方式,但可以总结为下面4类:

  • 消息传递(管道、FIFO、消息队列)
  • 同步(互斥量、条件变量、读写锁、文件和写记录锁、信号量)
  • 共享内存(匿名的和具名的)
  • 远程过程调用(Solaris门和Sun RPC)

我们要讨论的是网络中进程之间如何通信?首要解决的问题是如何唯一标识一个进程,否则通信无从谈起!在本地可以通过进程PID来唯一标识一个进程,但是在网络中这是行不通的。其实TCP/IP协议族已经帮我们解决了这个问题,网络层的“ip地址”可以唯一标识网络中的主机,而传输层的“协议+端口”可以唯一标识主机中的应用程序(进程)。这样利用三元组(ip地址,协议,端口)就可以标识网络的进程了,网络中的进程通信就可以利用这个标志与其它进程进行交互。

客户机和服务器的进程实现网络通信的流程

服务器:

  • 创建一个socket套接字,
  • 绑定监听端口;使用bind函数将客户端需要访问的ip地址和端口绑定到socket中
  • 设置监听队列;使用listen函数监听这个socket
  • 循环等待客户端的连接请求;使用accept函数和多线程接收所有的客户端发送过来的连接请求
  • 使用receive函数接收客户端发来的数据
  • 使用send函数由服务器发送数据或者资源给相应的客户机
  • close函数关闭服务端的socket

客户机:

  • 创建一个socket套接字,但这个套接字是用来储存目的IP和目的端口号的(即该客户机想要访问的某一台服务器的某一个IP地址和端口号)
  • 使用connect函数与客户端进行连接操作,
  • close函数关闭客户端的socket

下面贴cpp代码

代码使用指南:

  1. client.cpp端要求输入两个参数,第一个参数为目的ip地址,一般为127.0.0.1,第二个为端口号,固定为61717,因为server端被监听的端口号就是61717
  2. client.cpp文件有一堆是判断输入是否合法的if else分支,可以自动略过
  3. server端使用一个数组和accept方法实现等待队列,用来接收各个客户端的连接申请,同时利用多线程,处理等待队列中的客户端请求,
  4. 关于如何实现模拟多个客户端并发访问一个服务器,只需要在visual.studio中重复运行client.cpp文件即可(注意第二次开始运行时应该使用"添加新实例",否则会报错)

client.cpp

#include 
#include 
#include 
#include 
#include 
#include
using namespace std;
#pragma comment(lib , "ws2_32.lib")

// defines the buffer size
#define BUFSIZE 4096 

// judge whether it is a number or not
bool AllisNum(string str)
{
    for (int i = 0; i < str.size(); i++)
    {
        int tmp = (int)str[i];
        if (tmp >= 48 && tmp <= 57)
        {
            continue;
        }
        else
        {
            return false;
        }
    }
    return true;
}

int main(int argc, char* argv[])
{
    WSADATA wsd;
    SOCKET sClient;
    char Buffer[BUFSIZE];
    int ret;
    struct sockaddr_in server;
    unsigned short port;
    struct hostent* host = NULL;
    // judge whether two args are legal
    if (argc < 3) {
        printf("Usage:%s IP Port \n", argv[0]);
        return -1;
    }
    int portAddr;
    if (AllisNum(argv[2]))
    {
        portAddr = atoi(argv[2]);
        if (portAddr > 65535 || portAddr < 61000 || portAddr < 0)
        {
            cout << "Invalid command line argument detected: "<< argv[2]  << endl;
            cout << "Please check your values and press any key to end the program!" << endl;
            
        }
    }
    else
    {
        cout << "Invalid command line argument detected: "<< argv[2] << endl;
        cout << "Please check your values and press any key to end the program!" << endl;
       
    }


    // load Winsock DLL
    if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0) {
        printf("Winsock    初始化失败!\n");
        
    }

    // create socket
    sClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (sClient == INVALID_SOCKET) {
        printf("socket() 失败: %d\n", WSAGetLastError());
    }
    // bind server listenning addr and port number
    server.sin_family = AF_INET;
    server.sin_port = htons(atoi(argv[2]));
    //server.sin_addr.s_addr = inet_addr(argv[1]);
    
    // this is for the conversion from decimal to binary
    struct in_addr inp = {};
    string ipAddr = argv[1];
    if (ipAddr == "localhost")
        inet_pton(AF_INET, "127.0.0.1", &inp);
    else
        return 3;


    server.sin_addr.s_addr = inp.S_un.S_addr;// could also use inet_pton ...

    
    // build connection with the server
    if (connect(sClient, (struct sockaddr*)&server, sizeof(server)) == SOCKET_ERROR)
    {
        printf("Failed to connect to the server at <%s> on <%d>\n"
            "Please check your values and press any key to end program!", argv[1], atoi(argv[2]));

    }
    else
    {
        // receive and send messages

        for (;;) {
            printf("%s", "Please enter a message:");
            // read data from the standard input function
            gets_s(Buffer, BUFSIZE);
            // send messages to the server
            ret = send(sClient, Buffer, strlen(Buffer), 0);
            if (ret == 0) {
                break;
            }
            else if (ret == SOCKET_ERROR) {
                printf("send() 失败: %d\n", WSAGetLastError());
                break;
            }

            // receive data from the server
            ret = recv(sClient, Buffer, BUFSIZE, 0);
            if (ret == 0) {
                break;
            }
            else if (ret == SOCKET_ERROR) {
                printf("recv() 失败: %d\n", WSAGetLastError());
                break;
            }
            Buffer[ret] = '\0';
            printf("Received: \t%s\n",  Buffer);

        }
    }
    closesocket(sClient);// close the socket
    WSACleanup();        //clean
    system("pause");
    return 0;
}

server.cpp

#include   
#include   
#include 
#include 
#include
#include  
#include
#include 
#define MAXCLIENTNUM 10	
using namespace std;
#pragma comment(lib,"ws2_32.lib")  

typedef struct ClientInf
{
	SOCKET client_socket;
}ClientInf;
string host_names[4];
int counter = 0;
DWORD WINAPI Thread(LPVOID lpParameter)
{
	while (true)// loop
	{
		// receive data
		ClientInf client_inf = *(ClientInf*)lpParameter;
		char rev_data[255] = {};
		int ret = recv(client_inf.client_socket, rev_data, 255, 0);

		// resolve and handle data
		char host[NI_MAXHOST];	// Client's remote name
		char service[NI_MAXSERV]; // service(i.e.port) the client is connect on
	
		if (ret > 1)
		{
			ofstream fout("server.log", ios::app);
			rev_data[ret] = 0x00;
			cout << rev_data << endl;
			fout << rev_data << endl;
			fout.close();

		}
		
		// server informs the client that he receives data
		const char* sendData = "$message has been received by the server\r\n";

		int whetherSent = send(client_inf.client_socket, sendData, (int)strlen(sendData), 0);
		

		cout << whetherSent << endl;
		// detect disconnection
		if (whetherSent == SOCKET_ERROR )
		{
			ofstream fout("server.log", ios::app);
			fout<< "one client named "<< host_names[counter]  << " disconected" << endl;
			fout.close();
			return 0;
		}
		
	}
	return 0;
}

int main(int argc, char* argv[])
{

	//init WSA  
	WORD socketVersion = MAKEWORD(2, 2);//version of winsocket2.2

	WSADATA wsaData;

	if (WSAStartup(socketVersion, &wsaData) != 0)
	{
		return 0;
	}

	// create the socket of server
	SOCKET slisten = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

	if (slisten == INVALID_SOCKET)
	{
		printf("socket error !");
		return 2;
	}

	// bind ip addr and port number to the socket
	sockaddr_in sin;
	sin.sin_family = AF_INET;
	sin.sin_port = htons(61717);
	sin.sin_addr.s_addr = INADDR_ANY;

	// if binding failed, throw error
	if (bind(slisten, (LPSOCKADDR)&sin, sizeof(sin)) == SOCKET_ERROR)//绑定
	{
		printf("bind error!");
	}

	// if listening sth wrong, throw error  
	if (listen(slisten, 5) == SOCKET_ERROR)
	{
		return 3;
	}

	ClientInf client_inf_vector[MAXCLIENTNUM];// an array recieves all clients info
	HANDLE thread_handle_vector[MAXCLIENTNUM];// an array stores all threads 
	int client_num = 0;// the number of clients connected

	// this is the main thread that accepts all connections from clients
	while (true)
	{
		printf("waiting for connection request...\n");

		sockaddr_in remoteClient;

		int nAddrlen = sizeof(remoteClient);
		printf("current num of clients:%d\r\n", client_num);

		SOCKET c_socket = accept(slisten, (SOCKADDR*)&remoteClient, &nAddrlen);// block and wait

		if (client_num < MAXCLIENTNUM)
		{
			if (c_socket == INVALID_SOCKET)
			{
				printf("accept error!");
				continue;
			}
			client_inf_vector[client_num].client_socket = c_socket;
			char sendBuf[255] = { '\0' };
			char host[NI_MAXHOST];	// Client's remote name
			char service[NI_MAXSERV]; // service(i.e.port) the client is connect on
			ZeroMemory(host, NI_MAXHOST);
			// same as memset ( host,0,NI_MAXHOST);
			ZeroMemory(service, NI_MAXSERV); // NI_MAXSERV indicates the length of the array need filling  with 0
		
			ofstream fout("server.log", ios::app);
			if (getnameinfo((sockaddr*)&remoteClient, sizeof(remoteClient), host, NI_MAXHOST, service, NI_MAXSERV, 0) == 0)
			{
				counter = client_num;
				host_names[counter] = host;
				fout << host << " connected on port of client  " << service << endl;
				cout << host << " connected on port of client  " << service << endl;
			}
			else
			{
				inet_ntop(AF_INET, (void*)&remoteClient.sin_addr, sendBuf, sizeof(sendBuf));
				fout << host << " connected on port of client  " << service << endl;
				cout << host << " connected on port of client  " << ntohs(remoteClient.sin_port) << endl;
			}

			fout.close();
			thread_handle_vector[client_num] = CreateThread(

				NULL,
				0,
				Thread,
				&(client_inf_vector[client_num]),
				0,
				NULL

			);
			client_num++;

		}
		else
		{
			printf("connection clients are up to the upper limit:%d\r\n", client_num);
			
		}
	}

	// close all clients
	for (int i = 0; i < client_num; i++)
	{
		closesocket(client_inf_vector[i].client_socket);
	}

	closesocket(slisten);//close the server listening service

	WSACleanup();

	return 0;
}

你可能感兴趣的:(网络,tcp/ip,c++)