01.思维导图
2:使用服务器和客户端的代码,实现服务器和客户端的互相聊天功能
同桌之间互相聊天
第一版本:
client
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <wait.h>
#include <signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/un.h>
typedef struct sockaddr_in addr_in_t;
typedef struct sockaddr addr_t;
typedef struct sockaddr_un addr_un_t;
enum Type{
TYPE_REGIST,
TYPE_LOGIN
};
typedef struct Pack{
int size; // 用来记录打算发送的协议包有多大
enum Type type;
char buf[4096];
int used;
}pack_t;
// 写一个函数,功能为 向pack包中,写入一组数据
// 参数1: & pack包
// 参数2:想要写入的数据本身,暂定为字符串
void pack_append_data(pack_t* pack,const char* val){
char* buf = pack->buf;
// 计算准备写入的数据的长度
short len = strlen(val);
// 从buf里面,取2个字节,用来存放val的长度
*(short*)(buf+pack->used) = len;
// buf + pack->used
// 含义为:偏移掉已经使用的部分,从buf未使用的部分开始存放数据
pack->used += 2;
// 将字符串数据拷贝到紧挨着的内存里面
strcpy(buf+pack->used,val);
pack->used += len;
pack->size = 8 + pack->used;
// 8个字节是什么? size 自己的大小 + type 的大小
// pack->used是什么? pack.buf 实际使用的大小
}
int main(int argc, const char *argv[])
{
if(argc < 2){
printf("请输入端口号\n");
return 1;
}
short port = atoi(argv[1]);// atoi 函数,将字符串类型转换成整形
int client = socket(AF_INET,SOCK_STREAM,0);
struct sockaddr_in addr = {0};
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr("192.168.201.104");
connect(client,(struct sockaddr*)&addr,sizeof(addr));
// 客户端发送账号密码给服务器,实现注册/登录操作
while(1){
char name[16] = "";
char pswd[16] = "";
printf("请输入账号:");
scanf("%s",name);
getchar();
printf("请输入密码:");
scanf("%s",pswd);
getchar();
pack_t pack = {0};
pack_append_data(&pack,name);// 将账号name存入pack包中
pack_append_data(&pack,pswd);// 将密码pswd存入pack包中
printf("packsize = %d\n",pack.size);
write(client,&pack,pack.size);
}
return 0;
}
server
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
int main(int argc, const char *argv[])
{
if(argc<2)
{
printf("请输入端口号!\n");
return 1;
}
short port=atoi(argv[1]);
int server=socket(AF_INET,SOCK_STREAM,0);
struct sockaddr_in addr={0};
addr.sin_family=AF_INET;
addr.sin_port=htons(port);
addr.sin_addr.s_addr=inet_addr("0.0.0.0");
if(bind(server,(struct sockaddr*)&addr,sizeof(addr))==-1)
{
perror("bind error..");
return -1;
}
//监听
listen(server,10);
printf("服务器已启动,等待客户端连接...\n");
//接收
//准备好客户端结构体
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
//接收客户端,返回客户端套接字
int client = accept(server,(struct sockaddr*)&client_addr,&client_len);
if(client == -1) {
perror("accept error");
close(server);
return -1;
}
printf("客户端已连接\n");
while(1)
{
char buf[128]="";
int res=read(client,buf,sizeof(buf));
printf("res=%d\n",res);
if(res==0)
{
printf("客户端断开连接~\n");
break; // 修改:使用break而不是return,避免直接退出程序
}
else if(res == -1) {
perror("read error");
break; // 处理读取错误
}
printf("接收到客户端消息:%s\n",buf);
}
// 新增:关闭客户端和服务器套接字
close(client);
close(server);
return 0;
}
实现聊天功能的代码:
01_server
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <pthread.h>
#define PORT 8888
#define BUFFER_SIZE 1024
// 接收消息线程函数
void *receive_messages(void *arg)
{
int sockfd = *(int *)arg;
char buffer[BUFFER_SIZE];
while (1) {
memset(buffer, 0, BUFFER_SIZE);
ssize_t bytes_received = recv(sockfd, buffer, BUFFER_SIZE - 1, 0);
if (bytes_received <= 0) {
printf("对方已断开连接。\n");
close(sockfd);
exit(0);
}
printf("对方: %s\n", buffer);
}
return NULL;
}
// 发送消息线程函数
void *send_messages(void *arg)
{
int sockfd = *(int *)arg;
char buffer[BUFFER_SIZE];
while (1) {
memset(buffer, 0, BUFFER_SIZE);
fgets(buffer, BUFFER_SIZE - 1, stdin);
buffer[strcspn(buffer, "\n")] = 0; // 去掉换行符
if (send(sockfd, buffer, strlen(buffer), 0) == -1) {
perror("发送消息失败");
close(sockfd);
exit(0);
}
}
return NULL;
}
// 服务器功能函数
void server_function()
{
int server_fd, new_socket;
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof(address);
// 创建套接字
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}
// 设置套接字选项
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
perror("setsockopt");
exit(EXIT_FAILURE);
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);
// 绑定套接字
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
perror("bind failed");
exit(EXIT_FAILURE);
}
// 监听连接
if (listen(server_fd, 3) < 0) {
perror("listen");
exit(EXIT_FAILURE);
}
printf("等待客户端连接...\n");
if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
perror("accept");
exit(EXIT_FAILURE);
}
printf("客户端已连接。\n");
pthread_t receive_thread, send_thread;
// 创建接收和发送消息线程
pthread_create(&receive_thread, NULL, receive_messages, &new_socket);
pthread_create(&send_thread, NULL, send_messages, &new_socket);
pthread_join(receive_thread, NULL);
pthread_join(send_thread, NULL);
close(new_socket);
close(server_fd);
}
// 客户端功能函数
void client_function()
{
int sock = 0;
struct sockaddr_in serv_addr;
// 创建套接字
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("\n 套接字创建错误 \n");
return;
}
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
// 将 IPv4 地址从点分十进制转换为二进制形式
if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {
perror("\n 无效地址/ 地址不支持 \n");
return;
}
// 连接到服务器
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
perror("\n 连接失败 \n");
return;
}
printf("已连接到服务器。\n");
pthread_t receive_thread, send_thread;
// 创建接收和发送消息线程
pthread_create(&receive_thread, NULL, receive_messages, &sock);
pthread_create(&send_thread, NULL, send_messages, &sock);
pthread_join(receive_thread, NULL);
pthread_join(send_thread, NULL);
close(sock);
}
int main()
{
char choice;
printf("选择角色 (s: 服务器, c: 客户端): ");
scanf(" %c", &choice);
getchar(); // 消耗掉换行符
if (choice == 's') {
server_function();
} else if (choice == 'c') {
client_function();
} else {
printf("无效选择。\n");
}
return 0;
}
01_client
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <pthread.h>
#define PORT 8888
#define BUFFER_SIZE 1024
// 接收消息线程函数
void *receive_messages(void *arg)
{
int sockfd = *(int *)arg;
char buffer[BUFFER_SIZE];
while (1) {
memset(buffer, 0, BUFFER_SIZE);
ssize_t bytes_received = recv(sockfd, buffer, BUFFER_SIZE - 1, 0);
if (bytes_received <= 0) {
printf("对方已断开连接。\n");
close(sockfd);
exit(0);
}
printf("对方: %s\n", buffer);
}
return NULL;
}
// 发送消息线程函数
void *send_messages(void *arg)
{
int sockfd = *(int *)arg;
char buffer[BUFFER_SIZE];
while (1) {
memset(buffer, 0, BUFFER_SIZE);
fgets(buffer, BUFFER_SIZE - 1, stdin);
buffer[strcspn(buffer, "\n")] = 0; // 去掉换行符
if (send(sockfd, buffer, strlen(buffer), 0) == -1) {
perror("发送消息失败");
close(sockfd);
exit(0);
}
}
return NULL;
}
// 服务器功能函数
void server_function()
{
int server_fd, new_socket;
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof(address);
// 创建套接字
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}
// 设置套接字选项
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
perror("setsockopt");
exit(EXIT_FAILURE);
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);
// 绑定套接字
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
perror("bind failed");
exit(EXIT_FAILURE);
}
// 监听连接
if (listen(server_fd, 3) < 0) {
perror("listen");
exit(EXIT_FAILURE);
}
printf("等待客户端连接...\n");
if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
perror("accept");
exit(EXIT_FAILURE);
}
printf("客户端已连接。\n");
pthread_t receive_thread, send_thread;
// 创建接收和发送消息线程
pthread_create(&receive_thread, NULL, receive_messages, &new_socket);
pthread_create(&send_thread, NULL, send_messages, &new_socket);
pthread_join(receive_thread, NULL);
pthread_join(send_thread, NULL);
close(new_socket);
close(server_fd);
}
// 客户端功能函数
void client_function()
{
int sock = 0;
struct sockaddr_in serv_addr;
// 创建套接字
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("\n 套接字创建错误 \n");
return;
}
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
// 将 IPv4 地址从点分十进制转换为二进制形式
if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {
perror("\n 无效地址/ 地址不支持 \n");
return;
}
// 连接到服务器
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
perror("\n 连接失败 \n");
return;
}
printf("已连接到服务器。\n");
pthread_t receive_thread, send_thread;
// 创建接收和发送消息线程
pthread_create(&receive_thread, NULL, receive_messages, &sock);
pthread_create(&send_thread, NULL, send_messages, &sock);
pthread_join(receive_thread, NULL);
pthread_join(send_thread, NULL);
close(sock);
}
int main()
{
char choice;
printf("选择角色 (s: 服务器, c: 客户端): ");
scanf(" %c", &choice);
getchar(); // 消耗掉换行符
if (choice == 's') {
server_function();
} else if (choice == 'c') {
client_function();
} else {
printf("无效选择。\n");
}
return 0;
}