这短时间在写C的TCP通信,碰到的坑总结一下
一个socket的网络聊天室,文末贴了源码
- Xcode使用的C++标准中也有bind函数,在using namespace std;之后会调用C++的bind而不是socket.h。即:命名空间不要一刀切,宁愿多写字;
- 生成socketaddr_in时,同步使用memset清空内容。
memset(&cilent_addr, 0, sizeof(cilent_addr));
- Linux中,exit函数需要头文件csdlib.h,memset函数需要string.h
- 服务器端bind函数中将地址设置为任意,即允许所有链接
server_addr.sin_addr.s_addr=htonl(INADDR_ANY);//任意地址的访问
- socket分长链和短链,需要根据实际情况使用。后来才知道应该就是TCP和UDP
- UDP协议不需要connect,listen,accept,其套接字即用即停,短链
- rev-from中,最后一个参数类型为socket_t,使用int或sizeof会报错
- 使用fork函数来创建子进程,子进程的pid为0,根据这个可以用if区分两块代码
pid_t pid = fork();
- 可以用多进程分割tcp的io口(后来才知道可以用多线程),但是在UDP中使用有点问题,原因未知
- 线程创建之后,在线程里处理客户端;在主进程里要对线程进行销毁,有两种方法
/*
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);
- Linux编译线程时候加上参数-lathered
g++ -o test test.cpp -lpthread
- coco2dx 3.x使用的c++标准里没有pthread.h头文件,线程相关的函数改为pthread
- 使用方法如下:
std::thread t1(&HelloWorld::myThread,this);//创建分支线程,回调到myThread函数里
- 使用gets输入一条待发送的信息,来避免该信息中的空格作分割符
fgets(send_buf, package_len, stdin);
- 大坑:分清楚stdio和iostream分别包含的函数,sprintf在本地可以跑,服务器上报错未定义
死锁:有人说你长大了才能谈恋爱,又有人说你谈一场恋爱就长大了。所以你既不会长大也不会谈恋爱,这就是死锁,像极了爱情
下面贴上源码
客户端的:
#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);//解锁临界区
}