c语言 - socket - TCP - 网络聊天室练习

c语言, socket, TCP 聊天室练习

服务器端代码 service.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/wait.h>
#include <pthread.h>
#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define ERROR_MSG(msg) do{\
	fprintf(stderr, "line:%d: %s %s", __LINE__, __FILE__, __func__);\
	perror(msg);\
}while(0)

#define EXIT_MSG(msg) do{\
	fprintf(stderr, "line:%d: %s %s", __LINE__, __FILE__, __func__);\
	perror(msg);\
	return -1;\
}while(0)

#define SUCCESS_MSG(msg) do {\
	printf("%s, line:%d \n", msg, __LINE__);\
}while(0)

#define PORT 6666
#define IP "192.168.50.83"

short newfdArr[10] = {0};
int newfdLen = 0;

void addNewfd(short newfd);
void removeNewfd(short newf);
void printfArr();
void childHandler (int sig);
void *msg_handler(void *arg);
void clientDes(char result[], struct sockaddr_in cin);
int sendLoginEventToAll(short loginNewfd, struct sockaddr_in loginInfo);
int sendMsgToAll(short sendClientNewfd, struct sockaddr_in sendClientInfo, char msg[]);


typedef struct {
	int newfd;
	struct sockaddr_in cin;
}sockInfo;

int main(int argc, const char *argv[]) {

	int sfd = socket(AF_INET, SOCK_STREAM, 0);
	if (sfd < 0) { 
		EXIT_MSG("socket"); 
	}
	printf("服务器socket成功 \n");

	int reuse = 1;
	if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) {
		EXIT_MSG("setsockopt");
	}

	struct sockaddr_in sin;
	sin.sin_family = AF_INET;
	sin.sin_port = htons(PORT);
	sin.sin_addr.s_addr = inet_addr(IP);
	socklen_t slen = sizeof(sin);

	if (bind(sfd, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
		EXIT_MSG("bind");
	}
	printf("服务器bind成功 \n");

	if (listen(sfd, 100) < 0) {
		EXIT_MSG("listen");
	}
	printf("服务器bind成功 \n");

	struct sockaddr_in cin;
	socklen_t clen = sizeof(cin);

	while(1) {
		printf("服务器准备accept \n");
		int newfd = accept(sfd, (struct sockaddr *)&cin, &clen);
		if (newfd < 0) {
			EXIT_MSG("accept");
		}
		printf("服务器accept成功, newfd = %d \n", newfd);
		//给所有在线客户端,发送有新用户上线的消息
		sendLoginEventToAll(newfd, cin);
		
		sockInfo info;
		info.newfd = newfd;
		info.cin = cin;

		pthread_t tid = -1;
		if (pthread_create(&tid, NULL, msg_handler, &info)) {
			fprintf(stderr, "pthread_create error");
			return -1;
		}
		pthread_detach(tid);

		addNewfd(newfd);
	}

	close(sfd);

	return 0;
}

//收发信息
void *msg_handler(void *arg) {
	sockInfo *info = (sockInfo *)arg;

	int newfd = info->newfd;
	struct sockaddr_in cin = info->cin;
	char des[40];
	clientDes(des, cin);

	char buf[1024] = "";
	ssize_t recvRes = -1;
	while(1) {
		bzero(buf, sizeof(buf));
		printf("准备接收消息, newfd = %d \n", newfd);
		recvRes = recv(newfd, &buf, sizeof(buf), 0);
		if (recvRes < 0) {
			ERROR_MSG("recv error");
			pthread_exit(NULL);
		} else if (recvRes == 0) {
			printf("用户:%s,下线了 \n", des);
			bzero(buf, sizeof(buf));
			sprintf(buf, "用户%s,下线了 \n", des);
			sendMsgToAll(newfd, cin, buf);
			removeNewfd(newfd);
			pthread_exit(NULL);
		}
		printf("服务器接收%s的数据成功 \n", des);

		//给所有客户端群发消息
		sendMsgToAll(newfd, cin, buf);
	}
}

//给所有在线的客户端,发送有人登录的消息
int sendLoginEventToAll(short loginNewfd, struct sockaddr_in loginInfo) {
	char buf[1024] = "";
	clientDes(buf, loginInfo);
	strcat(buf, "上线了\n");
	
	for (int i=0; i<newfdLen; i++) {
		short newfd = newfdArr[i];
		if (newfd != loginNewfd) {
			if (send(newfd, buf, strlen(buf), 0) < 0) {
				return -1;
			}
		}
	}

	return 0;
}

//给所有在线的客户端,群发消息
int sendMsgToAll(short sendClientNewfd, struct sockaddr_in sendClientInfo, char msg[]) {
	if (strlen(msg) == 0) {
		printf("消息为空 \n");
		return -1;
	}
	char buf[1024] = "";
	clientDes(buf, sendClientInfo);
	strcat(buf, ":");
	strcat(buf, msg);

	for (int i=0; i<newfdLen; i++) {
		short newfd = newfdArr[i];
		if (newfd != sendClientNewfd) {
			if (send(newfd, buf, strlen(buf), 0) < 0) {
				return -1;
			}
		}
	}

	return 0;
}

//对客户端的描述
void clientDes(char result[], struct sockaddr_in cin) {
	sprintf(result, "%s:%d", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port));
}

//array func
void addNewfd(short newfd) {
	int len = sizeof(newfdArr)/sizeof(short);
	for (int i=0; i<len; i++) {
		if (newfdArr[i] == 0) {
			newfdArr[i] = newfd;
			break;
		}
	}
	newfdLen++;
}

void removeNewfd(short newfd) {
	int len = sizeof(newfdArr)/sizeof(short);
	for (int i=0; i<len; i++) {
		if (newfdArr[i] == newfd) {
			for (int j=i; j<len-1; j++) {
				newfdArr[j] = newfdArr[j+1];
			}
			newfdArr[len-1] = 0;
			break;
		}
	}
	newfdLen--;
}

void printfArr() {
	int len = sizeof(newfdArr)/sizeof(short);
	for (int i=0; i<len; i++) {
		if (newfdArr[i] == 0) {
			break;
		}
		printf("%d ", newfdArr[i]);
	}
	printf("\n");
}

客户端代码 client.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/wait.h>
#include <pthread.h>
#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define ERROR_MSG(msg) do{\
	fprintf(stderr, "line:%d: %s %s", __LINE__, __FILE__, __func__);\
	perror(msg);\
}while(0)

#define SUCCESS_MSG(msg) do {\
	printf("%s, line:%d \n", msg, __LINE__);\
}while(0)
#define PORT 6666
#define IP "192.168.50.83"

void childHandler (int sig);
void *sendMsgHandler(void *arg);
void *recvMsgHandler(void *arg);

int cfd = -1;
int isOnline = 0;

int main(int argc, const char *argv[]) {
	char buf[30];
	pthread_t sendTid, recvTid;
	while(1) {
		if (!isOnline) {
			//非登录状态
			printf("******输入login进行登录****** \n");
			fgets(buf, sizeof(buf), stdin);
			buf[strlen(buf)-1] = '\0';
			if (strcmp(buf, "login") == 0) {
				//创建socket
				cfd = socket(AF_INET, SOCK_STREAM, 0);
				if (cfd < 0) {
					ERROR_MSG("登录失败, socket:");
					return -1;
				}
				//connect
				struct sockaddr_in cin;
				cin.sin_family = AF_INET;
				cin.sin_port = htons(PORT);
				cin.sin_addr.s_addr = inet_addr(IP);

				if (connect(cfd, (struct sockaddr *)&cin, sizeof(cin)) < 0) {
					ERROR_MSG("登录失败, connect:");
					return -1;
				}
				isOnline = 1;
				printf("登录成功 \n");
			}
		} else {
			//登录状态
			//收消息线程
			pthread_create(&recvTid, NULL, recvMsgHandler, NULL);
			//发消息线程
			pthread_create(&sendTid, NULL, sendMsgHandler, NULL);

			pthread_join(recvTid, NULL);
			pthread_join(sendTid, NULL);
		}
	}

	close(cfd);

	return 0;
}

void *recvMsgHandler(void *arg) {
	char buf[1024] = "";
	int res = -1;

	while(1) {
		bzero(buf, sizeof(buf));
		if (isOnline) {
			//客户端在线状态
			res = recv(cfd, buf, sizeof(buf), 0);
			if (res < 0) {
				ERROR_MSG("接收消息失败: recv:");
				exit(EXIT_SUCCESS);
			} else if (res == 0) {
				printf("服务器已下线 \n");
				exit(EXIT_SUCCESS);
			} else {
				//接收数据成功
				printf("[接收消息成功]%s \n", buf);	
			}
		} else {
			//客户端下线状态
			pthread_exit(NULL);
		}
	}
}

void *sendMsgHandler(void *arg) {
	char buf[1024];
	while(1) {
		bzero(buf, sizeof(buf));
		fgets(buf, sizeof(buf), stdin);
		buf[strlen(buf)-1] = '\0';
		if(strcmp(buf, "quit") == 0) {
			//主动退出登录
			exit(EXIT_SUCCESS);
		} else {
			if (send(cfd, buf, strlen(buf), 0) < 0) {
				ERROR_MSG("发送消息失败: send");
			} else {
				//发送消息成功
			}
		}
	}
}

结果展示:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值