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 {
//发送消息成功
}
}
}
}
结果展示: