Socket网络聊天室源码和学习中遇到的一些坑

这短时间在写C的TCP通信,碰到的坑总结一下

一个socket的网络聊天室,文末贴了源码

  1. Xcode使用的C++标准中也有bind函数,在using namespace std;之后会调用C++的bind而不是socket.h。即:命名空间不要一刀切,宁愿多写字;
  2. 生成socketaddr_in时,同步使用memset清空内容。
memset(&cilent_addr, 0, sizeof(cilent_addr));
  1. Linux中,exit函数需要头文件csdlib.h,memset函数需要string.h
  2. 服务器端bind函数中将地址设置为任意,即允许所有链接
server_addr.sin_addr.s_addr=htonl(INADDR_ANY);//任意地址的访问
  1. socket分长链和短链,需要根据实际情况使用。后来才知道应该就是TCP和UDP
  2. UDP协议不需要connect,listen,accept,其套接字即用即停,短链
  3. rev-from中,最后一个参数类型为socket_t,使用int或sizeof会报错
  4. 使用fork函数来创建子进程,子进程的pid为0,根据这个可以用if区分两块代码
pid_t pid = fork();
  1. 可以用多进程分割tcp的io口(后来才知道可以用多线程),但是在UDP中使用有点问题,原因未知
  2. 线程创建之后,在线程里处理客户端;在主进程里要对线程进行销毁,有两种方法

/*
   pthread_detach(pid_t_send);
   pthread_detach(pid_t_rev);  //这两个线程结束代表整个服务结束,使用join等待线程结束
    */
   pthread_join(pid_t_send, &pthread_return);
   pthread_join(pid_t_rev, &pthread_return);
  1. Linux编译线程时候加上参数-lathered
g++ -o test test.cpp -lpthread
  1. coco2dx 3.x使用的c++标准里没有pthread.h头文件,线程相关的函数改为pthread
  2. 使用方法如下:
std::thread t1(&HelloWorld::myThread,this);//创建分支线程,回调到myThread函数里
  1. 使用gets输入一条待发送的信息,来避免该信息中的空格作分割符
fgets(send_buf, package_len, stdin);
  1. 大坑:分清楚stdio和iostream分别包含的函数,sprintf在本地可以跑,服务器上报错未定义
  2. 死锁:有人说你长大了才能谈恋爱,又有人说你谈一场恋爱就长大了。所以你既不会长大也不会谈恋爱,这就是死锁,像极了爱情

下面贴上源码
客户端的:

#include <iostream>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#define Host_Port 20000
#define package_len 50
#define ip "127.0.0.1"//152.136.72.146
pthread_mutex_t mutx;
char send_buf[package_len];
char rev_buf[package_len];

struct sockaddr_in cilent_addr;
void error_report(const char *message)
{     std::cout<<message<<std::endl;
  exit(-1);
}

void *cilent_send(void *arg)
{
  int cilent_sock = *((int *)arg);
  while (1)
  {
      std::cout<<"请输入要发送的文本:"<<std::endl;
      fgets(send_buf, package_len, stdin);
      write(cilent_sock, send_buf, package_len);
  //std::cout<<"已发送给服务器:"<<send_buf<<std::endl;
  }
  return NULL;
}
void *cilent_rev(void *arg)
{
  int cilent_sock = *((int *)arg);
  while (1)
  {
      read(cilent_sock,rev_buf, package_len);
      std::cout<<rev_buf;
  }
  return NULL;
}

int main()
{
  void *pthread_return;//线程返回值指针
  pthread_t pid_t_send,pid_t_rev;
  std::cout<<"欢迎使用xxx网络即时通讯,请稍后。。。"<<std::endl;
  memset(send_buf, 0, sizeof(send_buf));
  memset(rev_buf, 0, sizeof(rev_buf));
  memset(&cilent_addr, 0, sizeof(cilent_addr));
  
  int client_socket = socket(AF_INET, SOCK_STREAM, 0);
  if(client_socket == -1)
  {
      error_report("套接字创建失败!");
  }
  std::cout<<"套接字创建成功!"<<std::endl;

  cilent_addr.sin_family=AF_INET;
  cilent_addr.sin_port=htons(Host_Port);
  cilent_addr.sin_addr.s_addr=inet_addr(ip);
  
  if (connect(client_socket, (sockaddr*)&cilent_addr, sizeof(cilent_addr))==-1)
  {
      error_report("套接字连接失败!");
  }
  std::cout<<"服务器连接成功!等待发送数据……"<<std::endl;
  
  pthread_create(&pid_t_send, NULL, cilent_send, &client_socket);
  pthread_create(&pid_t_rev, NULL, cilent_rev, &client_socket);
  /*
  pthread_detach(pid_t_send);
  pthread_detach(pid_t_rev);  //这两个线程结束代表整个服务结束,使用join等待线程结束
   */
  pthread_join(pid_t_send, &pthread_return);
  pthread_join(pid_t_rev, &pthread_return);
  
  close(client_socket);
  return 0;
}

服务器端的:

#include <iostream>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <string.h>
#include <cstdlib>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <stdio.h>

#define Host_Port 20000
#define package_len 50
#define cilent_count_max 20//最大连接数量10

struct sockaddr_in cilent_addr;//储存连接客户端的ip信息
struct sockaddr_in server_addr;//储存连接客户端的ip信息
int cilent_count = 0;//当前连接的客户端数量
int cilent_sockets[cilent_count_max];//储存当前连接的客户端文件描述符
pthread_mutex_t mutx;//临界区锁

void *handle_cilent(void *arg);//线程程序,处理传入的客户端
void error_report(const char *message);//报错函数,显示错误信息
void send_to_all(const char *send_buf);//客户端广播信息


int main()
{
    int server_socket;//储存服务器端
    int cilent_socket = 0;//储存临时的k客户端
    
    pthread_t pid_t;
    char send_buf[package_len];
    memset(send_buf, 0, sizeof(send_buf));
    char rev_buf[package_len];
    memset(rev_buf, 0, sizeof(rev_buf));
    
    server_socket = socket(AF_INET, SOCK_STREAM, 0);
    if(server_socket == -1)
    {
        error_report("套接字创建失败!");
    }
    std::cout<<"套接字创建成功!"<<std::endl;
    
    memset(&server_addr, 0, sizeof(server_addr));
    memset(&cilent_addr, 0, sizeof(cilent_addr));
    
    server_addr.sin_family=AF_INET;
    server_addr.sin_port=htons(Host_Port);
    server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
    
    if(bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1)
    {
        error_report("分配ip地址失败!");
    }
    std::cout<<"分配ip地址成功"<<std::endl;
    std::cout<<"端口号:"<<Host_Port<<std::endl;
    
    if (listen(server_socket, 10)== -1)
    {
        error_report("监听连接失败!");
    }
    std::cout<<"监听成功!"<<std::endl;
    
    socklen_t cilent_addr_lenth = sizeof(cilent_addr);
    while (1)
    {
        cilent_socket = accept(server_socket, (sockaddr*)&cilent_addr, &cilent_addr_lenth);
       //cilent_socket = accept(server_socket, (sockaddr*)NULL, NULL);//从队列读取一个连接
        
       // int cilent_addr_sz = sizeof(cilent_addr);
       // int cilent_socket1 = accept(server_socket, &cilent_addr, &cilent_addr_sz);
    std::cout<<"新的客户端已连接!IP:"<<inet_ntoa(cilent_addr.sin_addr)<<std::endl;
        pthread_mutex_lock(&mutx);//锁住临界区
        cilent_sockets[cilent_count++]=cilent_socket;//将新连接保存
        pthread_mutex_unlock(&mutx);//解锁临界区
        
        pthread_create(&pid_t, NULL, handle_cilent, (void *)&cilent_socket);
        pthread_detach(pid_t);//销毁线程
        
    }
    return 0;
}


void error_report(const char *message)//报错信息
{
    std::cout<<message<<std::endl;
    exit(-1);
}

void *handle_cilent(void *arg)//线程程序,处理传入的客户端
{
    int cilent_sock = *((int *)arg);
    char rev_buf [package_len];
    memset(rev_buf, 0, package_len);
    char name_rev_buf[package_len];
    memset(name_rev_buf, 0, package_len);
    
    int rev_buf_lenth=0;
    
    while((rev_buf_lenth=read(cilent_sock, &rev_buf, package_len)!=0))
    {
        std::cout<<"接收到一条新信息:"<<rev_buf;
        snprintf(name_rev_buf, package_len, "%s:%s",inet_ntoa(cilent_addr.sin_addr),rev_buf);
        send_to_all(name_rev_buf);
    }
    pthread_mutex_lock(&mutx);//锁临界区
    for (int i=0; i<cilent_count; i++)
    {
        if (cilent_sockets[i] == cilent_sock)
        {
            while (i++<cilent_sock-1)
            {
                cilent_sockets[i]=cilent_sockets[i+1];//从列表删除当前客户端
            }
            break;
        }
    }
    cilent_count--;
    std::cout<<"一个用户已经掉线,IP为:待开发,剩余在线人数:"<<cilent_count<<std::endl;
    pthread_mutex_unlock(&mutx);//解锁临界区
    close(cilent_sock);//关闭临时套接字
    return NULL;
}

void send_to_all(const char *send_buf)//客户端广播信息send_buf
{
    pthread_mutex_lock(&mutx);//锁住临界区
    for (int i = 0; i<cilent_count; i++)
    {
        write(cilent_sockets[i], send_buf, package_len);
        std::cout<<"已经向第"<<i+1<<"个客户端转发信息"<<std::endl;
    }
    pthread_mutex_unlock(&mutx);//解锁临界区
    
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值