蟹老板阿 发表于 2020-12-28 22:38

C/C++ linux下的多线程socket通信

分为C语言版和C++版
C语言版为简单的单向socket通信
C++版本为socket的聊天室通信版本
使用到多线程,编译的时候加上参数 -lpthread
以下是代码

c语言版本
#include <iostream>
// 文件名:client.cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <string>
#define MAX 4096
int main()
{
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in ser_addr;
    char recvline,sendline;

    if (sockfd == -1)
    {
      std::cout << "create socket error" << std::endl;
      printf("error");
      return 0;
    }

    std::cout << "create successful" << std::endl;
    memset(&ser_addr,0,sizeof(ser_addr));
    ser_addr.sin_family = AF_INET;
    ser_addr.sin_port = htons(7788);
    ser_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    //inet_pton(AF_INET,"192.168.0.2",&ser_addr.sin_addr);

    std::cout << "create successful" << std::endl;
    int temp = connect(sockfd,(const struct sockaddr*)&ser_addr,sizeof(ser_addr));
    std::cout << "create successful" << std::endl;

    if(temp < 0)
    {
      std::cout << "connect error" << std::endl;
        return 0;
    }
   
    std::cout << "Msg you can send:" << std::endl;
    //std::cin >> sendline;
   
    while(1)
    {
      std::cin >> sendline;
      if(sendline == 'q' && sendline == '\0')
      {
            break;
      }
      if (send(sockfd,(const void*)&sendline,strlen((const char*)&sendline),0) < 0)
      {
      std::cout << "send message error" << std::endl;
      return 0;
      }
      memset(&sendline,0,sizeof(sendline));


    }
   
//    if (send(sockfd,(const void*)&sendline,strlen((const char*)&sendline),0) < 0)
//   {
//        std::cout << "send message error" << std::endl;
//        return 0;
//    }
    close(sockfd);
    return 0;
}



#include <iostream>
//文件名:service.cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <string>
#include <thread>

#define MAX 4096

int main()
{
    int listenfd,connfd;
    struct sockaddr_in ser_addr;
    char buff;
    int n;
    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    if (listenfd == -1)
    {
      std::cout << "create socket error" << std::endl;
                //printf("create socket error: %s(errno: %d)\n",strerror(errno),errno);
      return 0;
    }
    std::cout << "create successful" << std::endl;   
    memset(&ser_addr,0,sizeof(ser_addr));
    ser_addr.sin_family = AF_INET;
    ser_addr.sin_addr.s_addr = htonl(INADDR_ANY);
        //ser_addr.sin_addr.s_addr = inet_addr("192.168.0.1");
    ser_addr.sin_port = htons(7788);

    if(bind(listenfd, (struct sockaddr*)&ser_addr, sizeof(ser_addr)) == -1)
    {
        std::cout << "bind socket error" << std::endl;
        return 0;
    }

    if(listen(listenfd,10) == -1)
    {
        std::cout << "failed to listen the socket" << std::endl;
        return 0;
    }
    std::cout << "listen success" << std::endl;
    int socketlen = sizeof(struct sockaddr_in);
    struct sockaddr_in client_addr;
   
    if( (connfd = accept(listenfd,(struct sockaddr*)&client_addr,(socklen_t*)&socketlen)) == -1)   
    {
      std::cout << "accept socket error" << std::endl;
    }


    while(1)
    {
//        if( (connfd = accept(listenfd,(struct sockaddr*)&client_addr,(socklen_t*)&socketlen)) == -1)
//        {
//                std::cout << "accept socket error" << std::endl;
//                continue;
//        }
      
        n = recv(connfd,buff,MAX,0);
       
      if(n > 0)
      {
            buff = '\0';
            std::cout << buff << std::endl;
      }
      
        //close(connfd);
        memset(&buff,0,sizeof(buff));
    }
    close(listenfd);
    return 0;
}




-----------------------分割线,以下为cpp的聊天室版本------------------------------------


#include <iostream>
//文件名:C_client.cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <string>
#include <thread>
#define MAX 4096
using namespace std;
class Client
{
public:
    Client();
    ~Client();

    char* get_sendbuff();
    void created_socket();
    int connect_socket();
    int send_socket(int,char*);
    void close_socket();
    int get_sockfd();
    void recv_socket();
    struct msgData
    {
      int id;
      char msg;
    };
private:
    int sendfd;
    int socketfd;
    struct sockaddr_in ser_addr;
    char recvbuff;
    char sendbuff;
    int connfd;;      //定义connect句柄
    int recvnum;      //定义recv句柄
};

Client::Client()
{
    socketfd = socket(AF_INET, SOCK_STREAM, 0);
    memset((void*)&ser_addr,0,sizeof(ser_addr));
    ser_addr.sin_family = AF_INET;
    ser_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    ser_addr.sin_port = htons(7788);
    memset(sendbuff,0,sizeof(sendbuff));
}

Client::~Client()
{

}

void Client::created_socket()
{
    if(socketfd == -1)
    {
      std::cout << "create socket error! " << std::endl;
    }
    else
    {
      std::cout << "create socket success..." << std::endl;
    }
}

int Client::connect_socket()
{
    connfd = connect(socketfd,(const struct sockaddr*)&ser_addr,sizeof(ser_addr));
    if (connfd < 0)
    {
      std::cout << "connect socket error" << std::endl;
      return -1;
    }
    else
    {
      std::cout << "connfd is " << connfd << std::endl;
      std::cout << "connect socket success " << std::endl;
      return 0;
    }
}

int Client::send_socket(int sockfd,char* sendline)
{
    sendfd = send(sockfd,(const void*)sendline,strlen((const char*)sendline),0);
    if (sendfd < 0)
    {
      std::cout << "send message failed..." << std::endl;
      return -1;
    }
}

void Client::recv_socket()
{
    while(1)
    {
      recvnum = recv(socketfd,recvbuff,MAX,0);
      if (recvnum > 0)
      {
            recvbuff = '\0';
            std::cout << recvbuff << std::endl;
            memset(recvbuff,0,sizeof(recvbuff));
      }
    }
}

char* Client::get_sendbuff()
{
    return sendbuff;
}

void Client::close_socket()
{
    close(socketfd);
}

int Client::get_sockfd()
{
    return socketfd;
}

int main()
{
    cout << "enter your name please:";
    char name;
    cin >> name;
   
    char* psend;
    Client socking;
    psend = socking.get_sendbuff();
    socking.created_socket();
    if (socking.connect_socket() == -1)
    {
      return 0;
    }
    std::thread t1(&Client::recv_socket,&socking);
    while(1)
    {
      cin.getline(psend,200);
      if (psend == 'q' && psend == '\0')
      {
            break;
      }
      char s;
      sprintf(s,"%s%s%s",name,":",psend);
      if (socking.send_socket(socking.get_sockfd(),s) == -1)
      {
            std::cout << "send messages failed..." << std::endl;
            return 0;
      }
    }
    socking.close_socket();
    t1.detach();
}



#include <iostream>


//文件名: C_service.cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <string>
#include <thread>
#include <mutex>
#include <pthread.h>
#include <vector>
#include <unistd.h>
#include <algorithm>
#include <map>
#include <set>
#define MAX 4096
using namespace std;
std::set<int> clientfd;   //全局的set容器clientfd,存放不同客户端的connfd(句柄)
std::mutex mtx;             //多线程的锁
class Service               //服务端类
{
public:
    Service();
    ~Service();
    void   created_socket();            //创建socket套接字
    void   bind_socket();               //绑定服务器端口用于监听和接收message
    void   listen_socket();             //监听端口
    void   accept_socket();             //接受客户端连接
    void   receive_socket();            //接收message
    void   close_socket();            //关闭套接字
private:
    int    socketfd;                  //定义套接字
    int    connfd;                      //定义一个accept的句柄,用于recv函数的参数
    struct sockaddr_in ser_addr;      //定义服务端地址的结构体
    char   recvbuff;               //接收的数据
    int    recvnum;                     //创建recv句柄
    struct sockaddr_in client_addr;   //定义客户端地址的结构体
    int    socketlen;                   //定义结构体长度(这个定义有点重复了)
    int    reuse;                     //定义端口重用
};


Service::Service()
{
    socketfd = socket(AF_INET, SOCK_STREAM, 0);                           //套接字
    memset((void*)&ser_addr,0,sizeof(ser_addr));
    ser_addr.sin_family = AF_INET;                                          //定义服务端地址
    ser_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    ser_addr.sin_port = htons(7788);
    socketlen = sizeof(struct sockaddr_in);
    memset(recvbuff,0,sizeof(recvbuff));
    reuse = 1;
    setsockopt(socketfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse));      //使端口可重用
}

Service::~Service()
{

}

void Service::created_socket()
{
    if(socketfd == -1)
    {
      std::cout << "create socket error! " << std::endl;
    }
    else
    {
      std::cout << "create socket success..." << std::endl;
    }
}

void Service::bind_socket()
{
    if(bind(socketfd, (struct sockaddr*)&ser_addr, sizeof(ser_addr)) == -1)
    {
      std::cout << "bind socket error" << std::endl;
    }
    else
    {
      std::cout << "bind socket success..." << std::endl;
    }
}

void Service::listen_socket()
{
    if(listen(socketfd,10) == -1)
    {
      std::cout << "failed to listen the socket" << std::endl;
    }
    else
    {
      std::cout << "listen socket success..." << std::endl;
    }
}

void Service::accept_socket()
{
    connfd = accept(socketfd,(struct sockaddr*)&client_addr,(socklen_t*)&socketlen);
    if(connfd == -1)
    {
      std::cout << "accept socket error" << std::endl;
    }
    else
    {
      std::cout << "connfd is " << connfd << std::endl;
      std::cout << "accept socekt success" << std::endl;
    }
    mtx.lock();                                          //多线程的锁,避免多线程同时操作变量
    clientfd.insert(connfd);
    mtx.unlock();
}

void Service::receive_socket()
{
    while(1)
    {
      recvnum = recv(connfd,recvbuff,MAX,0);
      if(recvnum > 0)
      {
            set<int> i = clientfd;
            recvbuff = '\0';
            std::cout << recvbuff << std::endl;
            if (i.size() > 0)
            {
                for (auto iter = i.begin();iter != i.end();++iter)
                {
                  if (*iter == connfd)
                  {
                        continue;
                  }
                  send(*iter,recvbuff,sizeof(recvbuff),0);
                }
            }
      }
      else if(recvnum == 0)
      {
            break;
      }
    }

}


void thread_listen(Service s)
{
    while(1)
    {
      s.listen_socket();
      s.accept_socket();
      s.receive_socket();
    }

}

int main()
{
    Service socking;
    socking.created_socket();
    socking.bind_socket();
    std::thread t1(thread_listen,socking);
    std::thread t2(thread_listen,socking);
    std::thread t3(thread_listen,socking);
    t1.join();
    t2.join();
    t3.join();
}

蟹老板阿 发表于 2020-12-29 16:51

古月不傲 发表于 2020-12-29 00:27
辛苦了,建议成员函数都加上m_, 初始化列表成员该初始化初始化一下
尽量用namespace包裹起来,模块化好读
...

非常感谢,
但是namespace我考虑过,之所以没有加,因为工程太小,名称空间觉得加上去意义不大,基本上成员变量能在构造里写的我都写了,如有遗漏我会检查
后期我会学习您分享的代码,谢谢

古月不傲 发表于 2020-12-29 00:27

本帖最后由 古月不傲 于 2020-12-29 02:45 编辑

辛苦了,建议成员函数都加上m_, 初始化列表成员该初始化初始化一下
尽量用namespace包裹起来,模块化好读
可以参考下:链接: https://pan.baidu.com/s/17SEVyBiZSSZ0jSREmI9jow密码: i90u

184697211 发表于 2020-12-29 00:10

想问一下 用vs怎么编译cpp文件 安卓用的

cao777 发表于 2020-12-29 06:22

古月不傲 发表于 2020-12-29 00:27
辛苦了,建议成员函数都加上m_, 初始化列表成员该初始化初始化一下
尽量用namespace包裹起来,模块化好读
...

真 大佬!成员函数加上m_

BaconOle 发表于 2020-12-29 07:56

客户端一个socket监听,把接收到的socket放到容器里多线程里,可以用epoll模式监听。设置超时检测,一段时间不活跃的socket将他移出容器,减轻遍历压力

wsxzaq 发表于 2020-12-29 08:39

正在学习windows下的网络编程,正好可以参考一下

tlf 发表于 2020-12-29 08:41

lyf9ljh 发表于 2020-12-29 09:00

学习了。学习了。

yjczawyl 发表于 2020-12-29 09:15

占座 流个痕迹!!

jkj 发表于 2020-12-29 09:17

是TCP的。socket的是标准的,和windows的基本相同 。
页: [1] 2
查看完整版本: C/C++ linux下的多线程socket通信