1 搭建TCP服务器
1.1 先创建一个链表保存用户的相关信息,如果使用了数据库就可以这些数据保存在数据库里面。
1.1.1 list.h
#ifndef _LIST_H
#define _LIST_H
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
typedef struct Node
{
int id;
int sockfd;
struct Node *next;
}Node;
/*创建链表头节点*/
Node *list_head(void);
int list_empty(Node *head);
/*链表的长度*/
int list_len(Node *head);
/*增加数据*/
int list_add(Node *head,int id,int sockfd);
/*按照sockfd删除链表节点*/
int list_delete(Node *head,int sockfd);
/*根据ID号查找sockfd*/
int list_select_sockfd(Node *head,int id);
/*根据sockfd查找id*/
int list_seletc_id(Node *head,int sockfd);
/*将用户ID放在buf里面*/
int list_user(Node *head,int *id,char *buf);
/*遍历链表*/
int list_show(Node *head);
/*获取随机ID*/
int list_id(Node *head,int n);
#endif
1.1.2 list.c
#include "../inc/list.h"
/*创建链表头节点*/
Node *list_head(void)
{
Node *head=(Node *)malloc(sizeof(Node));
if(head==NULL)
{
perror("malloc");
return NULL;
}
head->id=-1;
head->sockfd=-1;
head->next=NULL;
return head;
}
/*判断链表为空*/
int list_empty(Node *head)
{
Node *q=head;
if(q->next==NULL)
return 1;
return -1;
}
/*链表的长度*/
int list_len(Node *head)
{
int len=0;
Node *q=head->next;
if(list_empty(head)==1)
return 0;
while(q!=NULL)
{
len++;
q=q->next;
}
return len;
}
/*增加数据*/
int list_add(Node *head,int id,int sockfd)
{
Node *q=(Node *)malloc(sizeof(Node));
if(q==NULL)
return -1;
Node *p=head;
while(p->next!=NULL)
{
p=p->next;
}
p->next=q;
q->next=NULL;
q->id=id;
q->sockfd=sockfd;
return 1;
}
/*按照sockfd删除链表节点*/
int list_delete(Node *head,int sockfd)
{
Node *q=head;
while(q->next!=NULL)
{
if(q->next->sockfd==sockfd)
break;
q=q->next;
}
Node *p=q->next;
q->next=p->next;
free(p);
return 1;
}
/*根据ID号查找sockfd*/
int list_select_sockfd(Node *head,int id)
{
Node *q=head->next;
while(q!=NULL)
{
if(q->id==id)
break;
q=q->next;
}
return q->sockfd;
}
/*根据sockfd查找id*/
int list_seletc_id(Node *head,int sockfd)
{
Node *q=head->next;
while(q!=NULL)
{
if(q->sockfd==sockfd)
break;
q=q->next;
}
return q->id;
}
/*将用户ID放在buf里面*/
int list_user(Node *head,int *id,char *buf)
{
int i=0;
Node *q=head->next;
while(q!=NULL)
{
if(q->id!=*id)
{
*(buf+i)=q->id+48;
}
q=q->next;
}
return 1;
}
/*遍历链表*/
int list_show(Node *head)
{
Node *q=head->next;
if(list_empty(head)==1)
{
return -1;
}
while(q!=NULL)
{
printf("用户id为:%d sockfd:%d",q->id,q->sockfd);
q=q->next;
}
return 1;
}
/*获取随机ID*/
int list_id(Node *head,int n)
{
Node *q=head->next;
for(int i=0;i<n;i++)
{
q=head->next;
while(q!=NULL)
{
if(i==q->id)
break;
q=q->next;
}
if(q==NULL)
return i;
}
return 1;
}
1.2 创建TCP服务器的相关API
1.2.1 server.h
#ifndef _SERVER_H
#define _SERVER_H
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
int sockfd_init(int port,int n);
int accept_init(int listen_t);
#endif
1.2.2 server.c
#include "../inc/server.h"
int sockfd_init(int port,int n)
{
struct sockaddr_in server;
int server_len=sizeof(server);
int listen_t=socket(AF_INET,SOCK_STREAM,0);
server.sin_family=AF_INET;
//指定固定IP
server.sin_addr.s_addr=inet_addr("192.168.6.251");
server.sin_port=htons(port);
if(bind(listen_t,(struct sockaddr *)&server,server_len)==-1)
{
perror("bind");
}
listen(listen_t,n);
return listen_t;
}
int accept_init(int listen)
{
struct sockaddr_in client;
socklen_t caddrlen=sizeof(client);
int sockfd=accept(listen,(struct sockaddr *)&client,&caddrlen);
if(sockfd==-1)
{
perror("accept");
return -1;
}
printf("用户端口号为:%d 用户IP为%s\n",ntohs(client.sin_port),inet_ntoa(client.sin_addr));
return sockfd;
}
1.3 当每个客户端进行连接就新建一个线程
1.3.1 pthread_fun.h
#ifndef _PTHREAD_FUN_H
#define _PTHREAD_FUN_H
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "list.h"
typedef struct head_sockfd
{
int sockfd;
Node *head;
pthread_mutex_t *mutex;
}head_sockfd;
void *my_read(void *arg);
void *my_write(void *arg);
#endif
1.3.2 pthread_fun.c
#include "../inc/pthread_fun.h"
void *my_read(void *arg)
{
char buf[512]={0};
char mesbuf[512]={0};
char send_buf[1024]={0};
head_sockfd *hs=NULL;
hs=(head_sockfd *)arg;
printf("hs->sockfd:%d\n",hs->sockfd);
int len=0;
int id=0;
int client_id=0;
int client_sockfd=0;
while(1)
{
bzero(buf,sizeof(buf));
bzero(mesbuf,sizeof(mesbuf));
bzero(send_buf,sizeof(send_buf));
len=read(hs->sockfd,buf,sizeof(buf));
/*判断是否为退出指令*/
if(strstr(buf,"exit")!=NULL||len==0)
{
/*加锁*/
pthread_mutex_lock(hs->mutex);
/*删除用户*/
list_delete(hs->head,hs->sockfd);
/*解锁*/
pthread_mutex_unlock(hs->mutex);
pthread_exit(0);
/* 销毁互斥锁 */
pthread_mutex_destroy(hs->mutex);
}
/*判断是否为显示在线用户的指令*/
else if(strstr(buf,"show")!=NULL)
{
/*根据sockfd获取ID*/
client_id=list_seletc_id(hs->head,hs->sockfd);
/*获取在线用户保存在send_buf里面*/
int flage=list_user(hs->head,&client_id,send_buf);
if(flage==0)
{
write(hs->sockfd,"no",2);
}
else
{
printf("show:%s\n",send_buf);
write(hs->sockfd,send_buf,strlen(send_buf));
}
}
/*群聊信息处理*/
else if(buf[1]!=' ')
{
Node *q=hs->head->next;
/*根据sockfd获取用户ID*/
client_id=list_seletc_id(hs->head,hs->sockfd);
while(q!=NULL)
{
if(q->id!=client_id)
{
sprintf(send_buf,"%d %s",client_id,buf);
write(q->sockfd,send_buf,strlen(send_buf));
}
q=q->next;
}
}
/*私聊信息处理*/
else
{
sscanf(buf,"%d %s",&id,mesbuf);
strcpy(mesbuf,buf+2);
/*根据目标ID获取目标sockfd*/
client_sockfd=list_select_sockfd(hs->head,id);
client_id=list_seletc_id(hs->head,hs->sockfd);
if(client_sockfd==-1)
{
write(hs->sockfd,"no",2);
}else{
sprintf(send_buf,"%d %s",client_id,mesbuf);
write(client_sockfd,send_buf,strlen(send_buf));
}
}
}
return NULL;
}
1.4 主函数
1.4.1 main.c
#include "../inc/server.h"
#include "../inc/list.h"
#include "../inc/pthread_fun.h"
#include <pthread.h>
#define N 5
Node *head=NULL;
char id[2]={0,'\0'};
/* 互斥锁定义 */
pthread_mutex_t lock;
int main(int argc,char *argv[])
{
if(argc<2)
{
printf("输入格式错误:例如 a.out 8888\n");
return -1;
}
int sockfd;
head=list_head();
int listen_t=sockfd_init(atoi(argv[1]),N);
int i=0;
while(1)
{
i=list_id(head,N);
printf("I:%d\n",i);
id[0]=i+48;
/*等待客户端进行连接*/
sockfd=accept_init(listen_t);
/*互斥锁初始化*/
if(pthread_mutex_init(&lock, NULL)!=0)
{
perror("mutex");
}
if(sockfd==-1)
continue;
write(sockfd,id,sizeof(id));
printf("sock:%d\n",sockfd);
/*为新用户开辟空间*/
head_sockfd *hs=(head_sockfd *)malloc(sizeof(head_sockfd));
hs->head=head;
hs->sockfd=sockfd;
hs->mutex=&lock;
/*加锁*/
pthread_mutex_lock(&lock);
/*在用户链表添加用户*/
list_add(head,i,sockfd);
/*解锁*/
pthread_mutex_unlock(&lock);
pthread_t pthread_read;
pthread_create(&pthread_read,NULL,my_read,hs);
/*线程分离,为了避免僵尸线程*/
pthread_detach(pthread_read);
printf("1111\n");
}
return 0;
}
1.5 运行
2 搭建TCP客户端
2.1 客户端读与写分别创建一个线程
2.1.1 pthread_fun.h
#ifndef _PTHREAD_FUN_H
#define _PTHREAD_FUN_H
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
void *cl_write(void *arg);
void *cl_read(void *arg);
#endif
2.1.2 pthread_fun.c
#include "../inc/pthread_fun.h"
char read_buf[1024]={0};
char write_buf[1024]={0};
void show(int a)
{
printf(" 欢迎用户%d登陆\n",a);
printf(" 私聊模式 :id 数据\n");
printf(" 查看用户 :show\n");
printf(" 退出登陆 :exit\n");
printf(" 群聊模式 :数据\n");
}
/*写线程,从终端获取数据转发给服务器*/
void *cl_write(void *arg)
{
int sockfd=*((int *)arg);
while(1)
{
bzero(write_buf,sizeof(write_buf));
/*从键盘获取数据*/
fgets(write_buf,sizeof(write_buf),stdin);
write(sockfd,write_buf,strlen(write_buf));
if(strstr(write_buf,"exit")!=NULL)
{
exit(0);
}
}
}
/*从服务器读取数据然后进行数据解析*/
void *cl_read(void *arg)
{
int sockfd=*((int *)arg);
int id=0;
char buf[1024]={0};
int len=0;
read(sockfd,read_buf,sizeof(read_buf));
len=(int)read_buf[0]-48;
show(len);//显示界面
while(1)
{
bzero(read_buf,sizeof(read_buf));
bzero(buf,sizeof(buf));
len=read(sockfd,read_buf,sizeof(read_buf));
if(len==0)
{
exit(0);
}
else if(strstr(read_buf,"no")!=NULL)
{
printf("没有此用户或者其他用户\n");
}
else if(read_buf[1]!=' ')
{
for(int i=0;i<strlen(read_buf);i++)
printf("用户%d:在线\n",read_buf[i]-48);
}
else
{
/*从接收到的数据里面解析出ID号和数据*/
sscanf(read_buf,"%d %s",&id,buf);
/*将数据保存到buf里面,scanf解析数据会出现错误*/
strcpy(buf,read_buf+2);
printf("用户%d:发送过来的数据为:%s\n",id,buf);
}
}
}
2.2 创建客户端的相关接口
2.2.1 client.h
#ifndef _CLIENT_H
#define _CLIENT_H
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
int connect_init(int port,const char *ip);
#endif
2.2.2 client.c
#include "../inc/client.h"
int connect_init(int port,const char *ip)
{
struct sockaddr_in server;
socklen_t saddrlen=sizeof(server);
/*创建cockfd套接字*/
int connect_t=socket(AF_INET,SOCK_STREAM,0);
if(connect_t<0)
{
perror("socket");
return -1;
}
server.sin_family=AF_INET;
server.sin_port=htons(port);//主机字节序转换为网络字节序
server.sin_addr.s_addr=inet_addr(ip);//将字符串形式的IP转换为网络二进制形式
/*与服务器之间进行连接*/
if(connect(connect_t,(struct sockaddr *)&server,saddrlen)<0)
{
perror("connect");
return -1;
}
return connect_t;
}
2.3 主函数
2.3.1 main.c
#include "../inc/client.h"
#include "../inc/pthread_fun.h"
#include <pthread.h>
#define BUFLEN 1024
void *cl_write(void *arg);
void *cl_read(void *arg);
int main(int argc,char *argv[])
{
if(argc<3)
{
printf("输入格式错误:例如 a.out 8888 192.168.6.251\n");
return -1;
}
int time=0;
void *thread_result;
pthread_t client_read;
pthread_t client_write;
int connect_t=0;
while(1)
{
connect_t=connect_init(atoi(argv[1]),argv[2]);
if(connect_t>0)
{
break;
}
else if(time==0&&connect_t==-1)
{
printf("正在与服务器进行连接!!!!\n");
}
else if(time==10)
{
printf("连接失败 请重试\n");
return 0;
}
sleep(1);
time++;
}
/*发送数据到服务器线程*/
pthread_create(&client_write,NULL,cl_write,&connect_t);
/*从服务器接收数据线程*/
pthread_create(&client_read,NULL,cl_read,&connect_t);
/*等到线程退出返回先关信息,然后释放空间*/
pthread_join(client_read,&thread_result);
pthread_join(client_write,NULL);
return 0;
}
2.4 运行
3 如果用Cmake进行编译,下面附上Cmake的代码
3.1 server
cmake_minimum_required(VERSION 2.8)
project(server)
configure_file(
"${PROJECT_SOURCE_DIR}/config.h.in"
"${PROJECT_SOURCE_DIR}/build/config.h"
)
option(SERVER_OK "服务器开关" ON)
if(SERVER_OK)
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
set(LIST ${PROJECT_SOURCE_DIR}/testFunc/src/list.c)
set(SERVER ${PROJECT_SOURCE_DIR}/testFunc/src/server.c)
set(PTHREAD_FUN ${PROJECT_SOURCE_DIR}/testFunc/src/pthread_fun.c)
add_library(list STATIC ${LIST})
add_library(server STATIC ${SERVER})
add_library(pthread_fun STATIC ${PTHREAD_FUN})
else()
set(SRC_LIST ${PROJECT_SOURCE_DIR}/testFunc/src/main.c)
include_directories(${PROJECT_SOURCE_DIR}/testFunc/inc)
find_library(LIST1 list ${PROJECT_SOURCE_DIR}/lib)
find_library(SERVER1 server ${PROJECT_SOURCE_DIR}/lib)
find_library(PTHREAD_FUN1 pthread_fun ${PROJECT_SOURCE_DIR}/lib)
add_compile_options(-Wall)
add_executable(main ${SRC_LIST})
target_link_libraries(main -pthread)
target_link_libraries(main ${LIST1})
target_link_libraries(main ${SERVER1})
target_link_libraries(main ${PTHREAD_FUN1})
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
endif()
3.2 client
cmake_minimum_required(VERSION 2.8)
project(client)
configure_file (
"${PROJECT_SOURCE_DIR}/config.h.in"
"${PROJECT_BINARY_DIR}/build/config.h"
)
#增加编译选项
option(MYDEBUG "enable debug compilation" OFF)
if(MYDEBUG)
set(CLIENT ${PROJECT_SOURCE_DIR}/myfun/src/client.c)
set(PTHREAD_FUNC ${PROJECT_SOURCE_DIR}/myfun/src/pthread_fun.c)
add_library(client STATIC ${CLIENT})
add_library(pthread_func STATIC ${PTHREAD_FUNC})
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
else()
set(SRC_LIST ${PROJECT_SOURCE_DIR}/myfun/src/main.c)
include_directories(${PROJECT_SOURCE_DIR}/myfun/inc)
find_library(CLIENT1 client ${PROJECT_SOURCE_DIR}/lib)
find_library(PTHREAD_FUNC pthread_func ${PROJECT_SOURCE_DIR}/lib)
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
add_compile_options(-Wall)
add_executable(main ${SRC_LIST})
target_link_libraries(main -pthread)
target_link_libraries(main ${CLIENT1})
target_link_libraries(main ${PTHREAD_FUNC})
endif()
可能会因为环境的不同,从而会出现不同的错误。