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 <winsock2.h>
#include <stdio.h>
#include <stdlib.h>
#include <WS2tcpip.h>
#include <iostream>
#include<string>
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 <stdio.h>  
#include <winsock2.h>  
#include <WS2tcpip.h>
#include <iostream>
#include<fstream>
#include <sstream> 
#include<string>
#include <windows.h>
#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;
}
  • 0
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: C语言实现基于TCP协议的服务器和客户端通信,需要使用Socket编程。下面是一些基本步骤: 1. 建立服务器 首先需要建立服务器端的socket。通过调用socket()函数创建服务器socket,指定socket的协议族(通常是AF_INET,即IPv4),socket类型(通常是SOCK_STREAM,即TCP流式套接字),和端口号。之后调用bind()函数把服务器socket绑定到指定的地址和端口。接下来就可以调用listen()函数开始监听客户端连接请求。 2. 连接客户端 客户端需要调用socket()函数创建socket,同样指定协议族、socket类型和端口号(这里可以随机指定一个未占用的端口号)。之后调用connect()函数连接服务器的地址和端口号即可。 3. 通信 一旦客户端和服务器端建立连接,就可以通过读写socket进行通信。服务器端需要调用accept()函数接受客户端连接请求,返回一个新的socket描述符用于和客户端进行通信。之后可以使用send()函数向客户端发送数据,使用recv()函数从客户端接收数据。客户端同样可以使用send()和recv()函数进行通信。 4. 结束连接 通信结束后,服务器和客户端需要分别调用close()函数关闭链接。 以上是基于TCP协议的服务器和客户端通信的基本步骤,具体实现过程需要详细的代码实现。在实际开发,还需要注意处理错误和异常情况,以保证程序的稳定性和安全性。 ### 回答2: 基于TCP协议的服务器和客户端通信是一种常见的网络通讯方式。服务器在网络上侦听特定端口,接收客户端的连接请求。当连接建立后,服务器和客户端之间可以进行数据传输。 实现基于TCP协议的服务器和客户端通信,需要遵循以下步骤: 1. 创建服务器端的套接字(socket)并绑定IP地址和端口号。 2. 监听客户端的连接请求,等待客户端连接。 3. 接受客户端的连接请求,创建一个与客户端通信的套接字。 4. 使用套接字进行数据传输,包括从客户端接收请求和向客户端发送响应。 5. 当通信完成后,关闭连接并释放资源。 对于客户端,需要以下步骤: 1. 创建客户端的套接字。 2. 连接服务器套接字。 3. 发送请求数据给服务器。 4. 接收服务器响应数据。 5. 关闭连接并释放资源。 在实现过程,还需要注意以下方面: 1. 使用正确的IP地址和端口号进行通信。 2. 服务器端需要使用多线程或多进程进行并发处理,以支持多个客户端同时连接。 3. 通信过程需要加入一定的数据校验和错误处理机制,以提高通讯的可靠性。 总之,基于TCP协议的服务器和客户端通信是一种灵活、可靠的网络通讯方式,可以广泛应用于各种网络场景,例如打印、文件传输、远程控制等。 ### 回答3: C 11是一种编程语言,可以用来实现基于TCP的服务器和客户端通信TCP是传输控制协议的缩写,它提供了一种可靠的数据传输方式,被广泛用于互联网上的通信。 要实现基于TCP的服务器和客户端通信,需要用C 11语言编写两个程序:一个服务器程序和一个客户端程序。服务器程序在运行时监听一个指定的端口,等待客户端程序的连接请求。当客户端请求连接时,服务器程序接受连接请求,并创建一个新的进程或线程用于处理这个连接。 在服务器程序和客户端程序之间进行数据传输时,需要使用TCP协议提供的套接字接口。服务器程序和客户端程序都可以通过套接字接口创建一个套接字,用于进行数据传输。服务器程序可以使用accept函数来接受连接请求,而客户端程序可以使用connect函数来连接服务器。 一旦连接建立,服务器程序和客户端程序之间就可以通过套接字进行数据传输了。服务器程序可以使用send函数将数据发送给客户端程序,而客户端程序可以使用recv函数接收服务器发送的数据。数据传输结束后,服务器程序和客户端程序都可以使用close函数关闭套接字。 总之,用C 11语言编写基于TCP的服务器和客户端通信程序需要了解TCP协议、套接字接口和相应的函数,熟练掌握C 11编程语言,并具有相应的开发经验和编程能力。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值