操作系统:Linux
编程语言:c语言
应用技术:socket编程(TCP协议)、多线程
实现功能:简单的聊天室(服务器端与客户端可以相互收发消息)
简要概述
如何建立连接,即socket编程的创建流程,请查看socket编程流程
(完整代码在最后)
服务器端与客户端收发数据相同
建立好连接后,创建两个线程
- send_thread:用于发送数据,执行发送函数
- recv_thread:用于接收数据,执行接收函数
然后创建两个函数,分别用于以上两个线程的执行
- send_func:发送函数,实现手动从键盘输入数据,然后发送
- recv_func:接收函数,实现不断检测并接收对方发来的消息
- (每次发送或接收,都会在屏幕上打印出来,见代码)
测试
因为是在同一台电脑上进行测试,所以需要开启两个终端,一个终端运行服务器端,另一个运行客户端
一、编译
由于 pthread 库不是标准的 Linux 库,所以在编译时需要加上 -lpthread
服务器端
gcc service.c -o service -lpthread
客户端
gcc client.c -o client -lpthread
二、执行
先执行服务器端
再执行 客户端
此时服务器端也会显示连接成功
三、发送、接收数据
1、服务器端发送数据 Hello my name is service(回车发送)
此时服务器端会显示
客户端会显示
2、客户端发送数据 Hi my name is client
此时客户端显示
服务器端显示
四、退出聊天室
当双方都输入quit时,会结束聊天
服务器端发送 quit
客户端接收到服务器端发送的quit数据,并也返回发送 quit
此时客户端己经退出,服务器端收到客户端回的消息quit,也已经退出
一次愉快的聊天就这样结束咯!
完整代码
服务器端 (service.c)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <pthread.h>
#define BUFFER_SIZE 1024
void *send_func();
void *recv_func();
/*服务器端*/
int main(){
pthread_t send_thread, recv_thread;
//创建套接字
int serv_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
//将套接字和IP、端口绑定
struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr)); //每个字节都用0填充
serv_addr.sin_family = AF_INET; //使用IPv4地址
serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); //具体的IP地址
serv_addr.sin_port = htons(1258); //端口
bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
//进入监听状态,等待用户发起请求
listen(serv_sock, 20);
printf("等待连接......\n");
//接收客户端请求
struct sockaddr_in clnt_addr;
socklen_t clnt_addr_size = sizeof(clnt_addr);
int clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);
if (clnt_sock == -1)
{
printf("连接失败\n");
exit(1);
} else {
printf("连接成功\n");
printf("----------网络聊天室----------\n");
}
//向客户端发送和接收数据,使用线程方式
int send_result, recv_result;
send_result = pthread_create(&send_thread, NULL, send_func, (void*)(long)clnt_sock);
recv_result = pthread_create(&recv_thread, NULL, recv_func, (void*)(long)clnt_sock);
if (send_result != 0)
{
printf("send_thread create failure\n");
exit(1);
} else {
//printf("send_thread create success\n");
}
if (recv_result != 0)
{
printf("recv_thread create failure\n");
exit(1);
} else {
//printf("recv_thread create success\n");
}
send_result = pthread_join(send_thread, NULL);
if (send_result == 0)
{
//printf("send_thread quit\n");
}
recv_result = pthread_join(recv_thread, NULL);
if (recv_result == 0)
{
//printf("recv_thread quit\n");
}
printf("聊天结束!\n");
//关闭套接字
close(clnt_sock);
close(serv_sock);
return 0;
}
/*发送数据*/
void *send_func(void *arg)
{
int sockfd = (int)(long)arg;
char send_buf[BUFFER_SIZE];
while(1)
{
// 从键盘输入数据
if(fgets(send_buf, BUFFER_SIZE, stdin) == NULL)
{
puts("no message\n");
continue;
} else {
// 发送数据
send(sockfd, send_buf, sizeof(send_buf), 0);
printf("-----> I : %s\n", send_buf);
}
// 当输入quit时退出
if (strncmp(send_buf, "quit", 4) == 0)
{
break;
}
}
pthread_exit(0);
}
/*接收数据*/
void *recv_func(void *arg)
{
int sockfd = (int)(long)arg;
char recv_buf[BUFFER_SIZE];
int recv_result;
while(1)
{
// 接收数据
recv_result = recv(sockfd, recv_buf, sizeof(recv_buf), 0);
if (recv_result > 0)
{
printf("-----> You : %s\n", recv_buf);
}
// 当接收到quit时退出
if (strncmp(recv_buf, "quit", 4) == 0)
{
break;
}
}
pthread_exit(0);
}
客户端(client.c)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <pthread.h>
#define BUFFER_SIZE 1024
void *send_func();
void *recv_func();
/*客户端*/
int main(){
int send_result, recv_result;
int temp_result;
pthread_t send_thread, recv_thread;
//创建套接字
int sock = socket(AF_INET, SOCK_STREAM, 0);
//向服务器(特定的IP和端口)发起请求
struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr)); //每个字节都用0填充
serv_addr.sin_family = AF_INET; //使用IPv4地址
serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); //具体的IP地址
serv_addr.sin_port = htons(1258); //端口
temp_result = connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
if (temp_result == -1)
{
printf("连接失败\n");
exit(1);
} else {
printf("连接成功\n");
printf("----------网络聊天室----------\n");
}
//发送和接收数据,使用多线程
send_result = pthread_create(&send_thread, NULL, send_func, (void*)(long)sock);
recv_result = pthread_create(&recv_thread, NULL, recv_func, (void*)(long)sock);
if (send_result != 0)
{
printf("send_thread create failure\n");
exit(1);
} else {
//printf("send_thread create success\n");
}
if (recv_result != 0)
{
printf("recv_thread create failure\n");
exit(1);
} else {
//printf("recv_thread create success\n");
}
send_result = pthread_join(send_thread, NULL);
if (send_result == 0)
{
//printf("send_thread quit\n");
}
recv_result = pthread_join(recv_thread, NULL);
if (recv_result == 0)
{
//printf("recv_thread quit\n");
}
printf("聊天结束!\n");
close(sock);
return 0;
}
/*发送数据*/
void *send_func(void *arg)
{
int sockfd = (int)(long)arg;
char send_buf[BUFFER_SIZE];
while(1)
{
// 从键盘输入数据
if (fgets(send_buf, BUFFER_SIZE, stdin) == NULL)
{
printf("no message\n");
continue;
} else {
// 发送数据
if(send(sockfd, send_buf, sizeof(send_buf), 0) == -1)
{
printf("send error\n");
continue;
} else {
printf("-----> I : %s\n", send_buf);
}
}
// 当输入quit时退出
if (strncmp(send_buf, "quit", 4) == 0)
{
break;
}
}
pthread_exit(0);
}
/*接收数据*/
void *recv_func(void *arg)
{
int sockfd = (int)(long)arg;
char recv_buf[BUFFER_SIZE];
while (1)
{
// 接收数据
if (recv(sockfd, recv_buf, sizeof(recv_buf), 0) > 0)
{
printf("-----> You : %s\n", recv_buf);
}
// 当接收到quit时退出
if (strncmp(recv_buf, "quit", 4) == 0)
{
break;
}
}
}
总结
以上代码使用多线程编程,实现了一个简单的聊天室功能
有兴趣可以在此基础上增加更多功能,比如界面、登录功能等