目錄
一、運行效果
1、分別編譯客戶端和服務端代碼
2、運行
3、使用效果
?二、代碼
chat.h
服務端代碼?
客戶端代碼
一、運行效果
1、分別編譯客戶端和服務端代碼
gcc client.c -o C -lpthread
gcc server.c -o S -lpthread
2、運行
先運行服務器端,8888為端口號
./S 8888
?再運行客戶端,這里創(chuàng)建兩個客戶端,端口號要和服務端的一樣
./C 127.0.0.1 8888
?????????可以看到,左下的窗口運之后,就會進入注冊界面;而服務器也會提示有客戶端的ip連接進來,這個時候再用右邊的窗口運行客戶端
??進入兩個主頁之后,服務器就會有不同的port對應不同的客戶端
3、使用效果
注冊
?????????上圖可以看到,左下進行了一個注冊之后就會提示注冊成功,服務器上面也會有記錄,下面進行登錄
?????????上圖左下登錄之后會提示登錄成功,服務器也會有記錄,而左下的窗口只要再按下回車就能進入到功能主頁。下面再用左下的窗口注冊一個用戶2
?????????上圖可以看到,用戶2注冊成功登錄之后,進入功能主頁的左下用戶也會有提示說右下用戶上線了,服務器也會記錄。下面左下用戶進行“公聊”,只要是在線的用戶都會收到信息
?? ? ? ? 上圖坐下用戶輸入數(shù)字3,選擇功能之后,就會提示輸入,輸入之后按回車,右下用戶就出現(xiàn)了信息(藍色字體),如下?
? ? ? ? ?上圖可以看到右下用戶出現(xiàn)了“公聊信息”藍色字體,下面用右邊的用戶對左邊用戶進行“私聊”
? ? ? ? ?輸入4選擇功能之后,會詢問對誰發(fā)信息,接著就輸入用戶的名字,在輸入發(fā)送的消息,按下回車就可以發(fā)送,如下
? ? ? ? 可以看到坐下的用戶已經(jīng)收到私聊的信息,這樣就不會像公聊那樣誰都能看到,輸入5可以查看有誰在線,下面先把右下的用戶退出
上圖可以看到只查詢到自己用戶1的名字在線,下面用第二個用戶查詢
?上圖可以看到,用戶2上線之后,查到兩個用戶在線
?二、代碼
chat.h
#ifndef __CHAT_H
#define __CHAT_H
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#define SERVER_PORT 8888
#define SIZE 32
/*最大的用戶數(shù)量*/
#define MAX_USER_NUM 64
/*在線用戶*/
struct ONLINE{
int fd; /*描述符,-1:該用戶下線 >0:該用戶已經(jīng)登錄,對應的套接字*/
int flage; /*注冊標志,-1 該條目沒有用戶信息 1:該條目有用戶注冊信息*/
char name[SIZE]; /*姓名*/
char password[SIZE]; /*密碼*/
};
/*C/S通信的結(jié)構(gòu)體*/
struct protocol{
int cmd; /*命令*/
int state; /*存儲命令返回信息*/
char name[SIZE]; /*用戶姓名*/
char data[MAX_USER_NUM]; /*數(shù)據(jù)*/
};
/*cmd(命令類型)*/
#define BROADCAST 0X00000001 /*廣播數(shù)據(jù)*/
#define PRIVATE 0X00000002 /*私聊*/
#define REGISTE 0X00000004 /*注冊*/
#define LOGIN 0X00000008 /*登錄*/
#define ONLINEUSER 0X00000010 /*顯示在線用戶*/
#define LOGOUT 0X00000020 /*退出*/
/*return code(服務器處理結(jié)果返回值)*/
#define OP_OK 0X80000000 /*操作成功*/
#define ONLINEUSER_OK 0X80000001 /*顯示在線用戶,未結(jié)束*/
#define ONLINEUSER_OVER 0X88888888 /*顯示在線用戶,已經(jīng)發(fā)送完畢*/
#define NAME_EXIST 0X80000003 /*注冊信息,用戶名已經(jīng)存在*/
#define NAME_PWD_NMATCH 0X80000004 /*登錄時,輸入的用戶名密碼不對 */
#define USER_LOGED 0X80000005 /*登錄時,提示該用戶已經(jīng)在線*/
#define USER_NOT_REGIST 0X80000006 /*登錄時,提示用戶沒有注冊*/
#endif
服務端代碼?
#include "chat.h"
#include <stdio.h>
#include <unistd.h>
struct ONLINE online[MAX_USER_NUM];
/*將用戶注冊信息存儲到在線用戶列表中*/
int add_user(int socket_fd, struct protocol *msg) {
int i, index = -1;
char buf[128] = {0};
/*歷在線用戶列表數(shù)組,從第一個開始找沒有被占用的位置,將用戶的注冊信息存儲到該位置*/
for (i = 0; i < MAX_USER_NUM; i++) { /*判斷當前位置是否已經(jīng)被占用*/
if (online[i].flage == -1) { /* 標記該位置為已占用*/
online[i].flage = 1;
strcpy(online[i].name, msg->name);
strcpy(online[i].password, msg->data);
printf("regist %s to %d \n", msg->name, i);
index = i;
return index; /* 返回存儲用戶信息的位置*/
}
}
return index;
}
/*從在線列表中刪除
*在聊天室程序中將在線用戶列表中指定位置的客戶端信息刪除,
*并向所有在線客戶端發(fā)送某用戶下線通知
*/
void del_user_online(int index) {
int i;
char buf[128] = {0};
/*通過比較傳入的 index 變量與 0 的大小,來判斷傳入?yún)?shù)是否合法
*如果 index 小于 0,代表傳入?yún)?shù)無效,直接返回
*/
if (index < 0)
return;
/*刪除在線用戶列表中相應位置的客戶端信息
*將在線用戶列表中指定位置的客戶端信息的 fd 字段設置為 -1,表示該客戶端已下線
*/
online[index].fd = -1;
/*通知所有客戶端,某個用戶下線了
*遍歷在線用戶列表中的每個客戶端,fd 不為 -1
*的客戶端,向其發(fā)送某用戶下線的通知消息buf
*/
sprintf(buf, "%s offline\n", online[index].name);
for (i = 0; i < MAX_USER_NUM; i++) {
if (online[i].fd == -1) {
continue;
}
write(online[i].fd, buf, strlen(buf));
}
return;
}
/*向所有在線用戶廣播消息*/
void broadcast(int index, struct protocol *msg) {
int i;
char buf[128] = {0};
sprintf(buf, "\033[0;34m\t%s say:%s\33[0m\n", online[index].name, msg->data);
for (i = 0; i < MAX_USER_NUM; i++) {
if ((online[i].fd == -1) || (i == index)) /*跳過不在線的和自己*/
{
continue;
}
write(online[i].fd, buf, strlen(buf));
}
}
/*用于判斷準備登錄的用戶是否已經(jīng)登錄,并返回其在列表中的索引*/
int find_dest_user_online(int socket_fd, int *index, struct protocol *msg) {
int i;
/*遍歷在線用戶列表數(shù)組*/
for (i = 0; i < MAX_USER_NUM; i++) {
if (online[i].flage == -1) {
continue;
}
/*如果用戶名和密碼都匹配*/
if ((strcmp(msg->name, online[i].name) == 0) &&
(strcmp(msg->data, online[i].password) == 0))
{/*不在線的先建立服務器連接*/
if (online[i].fd == -1) {
online[i].fd = socket_fd;
*index = i;
return OP_OK;
} else {/*在線的打印已經(jīng)登陸在線*/
printf("%s had login\n", online[i].name);
return USER_LOGED;/*用戶已經(jīng)登陸*/
}
}
}/*遍歷完所有在線用戶仍然沒有找到匹配的用戶*/
return NAME_PWD_NMATCH;
}
/*查找在線用戶列表 online 中指定用戶名*/
int find_dest_user(char *name) {
int i;
for (i = 0; i < MAX_USER_NUM; i++) {
if (online[i].flage == -1) {
continue;
}
if (strcmp(name, online[i].name) == 0) {
return i;
}
}
return -1;
}
/*私聊功能*/
void private(int index, struct protocol *msg) {
int dest_index;
char buf[128];
/*查找目標用戶在在線用戶列表 online 中的索引*/
dest_index = find_dest_user(msg->name);
if (dest_index == -1) {
/*向發(fā)送私聊請求的用戶發(fā)送一條提示消息,告訴其該用戶不在線*/
sprintf(buf, "there is no user :%s \n", msg->name);
write(online[index].fd, buf, strlen(buf));
return;
} else {
sprintf(buf, "\033[0;34m\t%s say to %s:%s\33[0m\n", online[index].name,
online[dest_index].name,
msg->data);
write(online[dest_index].fd, buf, strlen(buf));
return;
}
}
/*列出在線用戶*/
void list_online_user(int index) {
int i;
struct protocol msg;
for (i = 0; i < MAX_USER_NUM; i++) {
/*如果該套接字已經(jīng)關閉,則繼續(xù)下一個循環(huán)*/
if (online[i].fd == -1) {
continue;
}
memset(&msg, 0, sizeof(msg));
msg.cmd = ONLINEUSER;
msg.state = ONLINEUSER_OK;
strcpy(msg.name, online[i].name);
printf("list online[%d].name =%s \n",i, msg.name);
/*向客戶端發(fā)送在線用戶結(jié)構(gòu)體的數(shù)據(jù)*/
write(online[index].fd, &msg, sizeof(msg));
sleep(1);
}
/*表示在線用戶列表已經(jīng)全部發(fā)送完畢*/
msg.cmd = ONLINEUSER;
msg.state = ONLINEUSER_OVER;
/*用于通知客戶端當前任務已完成,并結(jié)束本次傳輸*/
write(online[index].fd, &msg, sizeof(msg));
}
/*注冊*/
void registe(int socket_fd, int *index, struct protocol *msg) {
int dest_index;
char buf[128];
struct protocol msg_back;
msg_back.cmd = REGISTE;
/*查找該用戶名是否已經(jīng)被其他用戶注冊*/
dest_index = find_dest_user(msg->name);
if (dest_index == -1) {
*index = add_user(socket_fd, msg);
online[*index].flage = 1;
msg_back.state = OP_OK;
printf("user %s regist success!\n", msg->name);
write(socket_fd, &msg_back, sizeof(msg_back));
return;
} else {/*用戶名已存在*/
msg_back.state = NAME_EXIST;
printf("user %s exist!\n", msg->name);
write(socket_fd, &msg_back, sizeof(msg_back));
return;
}
}
/*登錄*/
void login(int socket_fd, int *index, struct protocol *msg) {
int i, ret;
char buf[128];
struct protocol msg_back;
msg_back.cmd = LOGIN;
/*查找該用戶名是否已經(jīng)在線*/
ret = find_dest_user_online(socket_fd, index, msg);
if (ret != OP_OK) {/*不等于表示該用戶名不存在或者該用戶未登錄*/
msg_back.state = ret;
strcpy(buf, " no this user\n");
printf("user %s login fail!\n", msg->name);
write(socket_fd, &msg_back, sizeof(msg_back));
return;
} else {/*登錄成功*/
msg_back.state = OP_OK;
strcpy(msg_back.data, "login success\n");
printf("user %s login success!index =%d \n", msg->name, *index);
write(online[*index].fd, &msg_back, sizeof(msg_back));
}
// 通知所有客戶端,某個用戶上線了
sprintf(buf, "%s online\n", online[*index].name);
for (i = 0; i < MAX_USER_NUM; i++) {
if (online[i].fd != -1) {
write(online[i].fd, buf, strlen(buf));
}
}
}
void *func(void *arg) {
int socket_fd = *((int *)arg);
char buf[64];
int len;
int index = -1;
struct protocol msg;
free(arg);
/*進入聊天*/
while (1) {
/*read()函數(shù)和recv()函數(shù)都可以用于從套接字中讀取數(shù)據(jù),都會阻塞
*小于等于0說明未能成功讀取任何數(shù)據(jù)或連接已關閉,因此用戶被視為離線
*/
if ((len = read(socket_fd, &msg, sizeof(msg))) <= 0) {
printf("%s offline\n", online[index].name);
del_user_online(index);
close(socket_fd);
return;
}
switch (msg.cmd) {
case REGISTE:
registe(socket_fd, &index, &msg);
break;
case LOGIN:
login(socket_fd, &index, &msg);
break;
case BROADCAST:
broadcast(index, &msg);
break;
case PRIVATE:
private(index, &msg);
break;
case ONLINEUSER:
list_online_user(index);
break;
default:
break;
}
}
}
int main(int argc, char *argv[]) {
int ls_fd, new_fd;
int addr_len, clientaddr_len;
struct sockaddr_in my_addr;
struct sockaddr_in client_addr;
char buf[64] = {0};
pthread_t pid;
int *arg, i, port_number, ret;
/*判斷輸入命令行參數(shù)數(shù)量是否正確*/
if (argc != 2) {
fprintf(stderr, "Usage: %s [port_number]>5000\a\n", argv[0]);
exit(-1);
}
/*將第二個參數(shù)轉(zhuǎn)換為整數(shù)類型并賦值給變量,然后判斷輸入的端口參數(shù)是否小于0*/
if ((port_number = atoi(argv[1])) < 5000) {
fprintf(stderr, "Usage: %s [port_number]>5000\a\n", argv[0]);
exit(-1);
}
/*原型:int socket(int domain, int type, int protocol);
*創(chuàng)建一個網(wǎng)絡通信端點(打開一個網(wǎng)絡通信),成功則返回一個網(wǎng)絡文件描述符;調(diào)用失敗,則會返回-1
*domain:AF_INET,協(xié)議族名字,代表IPv4互聯(lián)網(wǎng)協(xié)議;如果用IPV6在后面加個6即可
*type:SOCK_STREAM,這是用于 TCP 協(xié)議
*protocol:0,表示為給定的通信域和套接字類型選擇默認協(xié)議
*/
if ((ls_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("socket() fail\n");
return;
}
/*將 server_addr 結(jié)構(gòu)體變量所占用的內(nèi)存區(qū)域全部清零。*/
memset(&my_addr, 0, sizeof(struct sockaddr_in));
/*指定套接字服務器的地址信息*/
/*表示使用 IPv4 地址*/
my_addr.sin_family = AF_INET;
/*端口號設置并將主機字節(jié)順序轉(zhuǎn)換為網(wǎng)絡字節(jié)順序*/
my_addr.sin_port = htons(port_number);
/*使用了宏 INADDR_ANY
* 來指定監(jiān)聽所有可用的網(wǎng)絡接口,從而使得服務器能夠接受到所有網(wǎng)絡接口上的連接請求*/
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr_len = sizeof(struct sockaddr_in);
/*int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
*將參數(shù) sockfd 指定的套接字與一個地址 addr 進行綁定,成功返回
*0,失敗情況下返回-1 sockfd:網(wǎng)絡文件描述符 addr:服務器結(jié)構(gòu)體地址
*addrlen:addr參數(shù)所指向的結(jié)構(gòu)體對應的字節(jié)長度
*/
if (bind(ls_fd, (struct sockaddr *)(&my_addr), addr_len) == -1) {
perror("bind() fail\n");
exit(-1);
}
/*int listen(int sockfd, int backlog);
*讓服務器進程進入監(jiān)聽狀態(tài),監(jiān)聽sockfd描述符并創(chuàng)建一個等待連接隊列,若執(zhí)行成功則返回0,否則返回-1
*sockfd:要設置為監(jiān)聽狀態(tài)的套接字描述符
*backlog:5,指定允許的連接數(shù)為
*5。如果有更多的客戶端連接請求到達,它們將被服務器拒絕或忽略。
*/
if (listen(ls_fd, 5) == -1) {
fprintf(stderr, "listen error:%s\n\a", strerror(errno));
return;
}
clientaddr_len = sizeof(struct sockaddr_in);
/*初始化online結(jié)構(gòu)體*/
for (i = 0; i < 64; i++) {
online[i].fd = -1;
online[i].flage = -1;
}
while (1) {
/*int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
*調(diào)用成功返回一個新的
*socket_fd描述符,用于與客戶端通信的。如果發(fā)生錯誤或者連接被中斷,則函數(shù)返回
*-1 函數(shù)成功返回時,addr 所指的緩沖區(qū)將被填充上客戶端的地址信息,而 addrlen
*則會被更新為實際的地址信息長度
*調(diào)用時沒有客戶端請求連接(等待連接隊列中也沒有等待連接的請求),
*accept()會進入阻塞狀態(tài),直到客戶程序建立連接
*/
if ((new_fd = accept(ls_fd, (struct sockaddr *)(&client_addr),
&clientaddr_len)) == -1) {
fprintf(stderr, "Accept error:%s\n\a", strerror(errno));
return;
}
/*char *inet_ntoa(struct in_addr in);
*將32位IP地址轉(zhuǎn)換為點分十進制形式的字符串表示
*/
printf("Client-ip: %s\tport: %d\t\n", inet_ntoa(client_addr.sin_addr),
client_addr.sin_port);
/*用堆空間單獨存放new_fd,防止高并發(fā)狀態(tài)覆蓋原??臻g的new_fd*/
arg = malloc(sizeof(int));
*arg = new_fd;
/*pthread_create(pthread_t *thread,
const pthread_attr_t *attr,
void *(*start_routine) (void *),
void *arg);
*創(chuàng)建一個新的線程,返回 0
表示成功,否則返回一個非零的錯誤碼,表示創(chuàng)建線程失敗 *thread:線程標識符pid
*attr:指定線程的屬性,如果傳遞了 NULL,則表示使用默認屬性
*start_routine:是一個函數(shù)指針,用于指定線程啟動時要執(zhí)行的函數(shù)
*arg:傳遞給該函數(shù)的參數(shù)
*/
if ((ret = pthread_create(&pid, NULL, func, (void *)arg)) != 0) {
perror("pthread_create err");
return;
}
}
close(new_fd);
close(ls_fd);
exit(0);
}
客戶端代碼
#include "chat.h"
#include <bits/types/timer_t.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
int socket_fd, addrlen;
struct sockaddr_in server_addr;
pthread_t pid;
int login_f = -1;
void *func(void *arg) {
int len;
char buf[128] = {0};
struct protocol *msg;
while (1) {
if (login_f != 1) {
continue;
}
memset(buf, 0, sizeof(buf));
if ((len = read(socket_fd, buf, sizeof(buf))) <= 0) {
close(socket_fd);
return;
}
msg = (struct protocol *)buf;
/*顯示在線用戶*/
if ((msg->state == ONLINEUSER_OK) && (msg->cmd == ONLINEUSER)) {
printf("%s\t", msg->name);
continue;
}
if ((msg->state == ONLINEUSER_OVER) && (msg->cmd == ONLINEUSER)) {
printf("\n");
continue;
}
buf[len] = '\0';
printf("%s\n", buf);
}
}
/*廣播信息*/
void broadcast(int fd) {
struct protocol msg;
msg.cmd = BROADCAST;
printf("say:\n#");
scanf("%s", msg.data);
write(fd, &msg, sizeof(msg));
}
/*私聊信息*/
void private(int fd) {
struct protocol msg, msgback;
msg.cmd = PRIVATE;
printf("input name you want to talk:\n\t#");
scanf("%s", msg.name);
printf("\n#say:\n");
scanf("%s", msg.data);
write(fd, &msg, sizeof(msg));
}
/*顯示在線人數(shù)*/
void list_online_user(int socket_fd) {
struct protocol msg;
msg.cmd = ONLINEUSER;
write(socket_fd, &msg, sizeof(msg));
while (1) {
read(socket_fd, &msg, sizeof(msg));
if (msg.state != ONLINEUSER_OK) {
getchar();
getchar();
break;
} else
printf("%s\r\n", msg.name);
}
}
/*注冊*/
int registe(int fd) {
struct protocol msg, msgback;
msg.cmd = REGISTE;
printf("input your name\n");
scanf("%s", msg.name);
printf("input your passwd\n");
scanf("%s", msg.data);
write(socket_fd, &msg, sizeof(msg));
read(socket_fd, &msgback, sizeof(msgback));
if (msgback.state != OP_OK) {
printf("\033[0;31m\tName had exist,try again!\n");
getchar();
getchar();
return -1;
} else {
printf("\033[0;31m\tRegist success!\n");
getchar();
getchar();
return 0;
}
}
/*登陸*/
int login(int fd) {
struct protocol msg, msgback;
msg.cmd = LOGIN;
printf("input your name\n");
scanf("%s", msg.name);
printf("input your passwd\n");
scanf("%s", msg.data);
write(socket_fd, &msg, sizeof(msg));
read(socket_fd, &msgback, sizeof(msgback));
if (msgback.state != OP_OK) {
printf("\033[0;31m\tName had exist,maybe the password is wrong,try "
"again!\33[0m\n");
getchar();
getchar();
login_f = -1;
return NAME_PWD_NMATCH;
} else {
printf("\033[0;31m\tLogin success!\33[0m\n");
getchar();
getchar();
login_f = 1;
return OP_OK;
}
}
/*退出*/
int logout(int fd) {
close(fd);
login_f = -1;
}
int main(int argc, char *argv[]) {
int sel, ret;
int port_number;
int min_sel, max_sel;
struct protocol msg;
/* 檢測參數(shù)個數(shù)*/
if (argc != 3) {
fprintf(stderr, "Usage:%s hostname portnumber\a\n", argv[0]);
exit(-1);
}
/*argv2 存放的是端口號 ,讀取該端口,轉(zhuǎn)換成整型變量*/
if ((port_number = atoi(argv[2])) < 5000) {
fprintf(stderr, "Usage:%s hostname [portnumber]>5000\a\n", argv[0]);
exit(-1);
}
/*創(chuàng)建一個 套接字*/
if ((socket_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
fprintf(stderr, "Socket Error:%s\a\n", strerror(errno));
return;
}
/*填充結(jié)構(gòu)體,ip和port必須是服務器的*/
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
/*轉(zhuǎn)換為網(wǎng)絡字節(jié)序*/
server_addr.sin_port = htons(port_number);
/*點分十進制表示的字符串形式轉(zhuǎn)換成二進制 Ipv4 或 Ipv6 地址*/
server_addr.sin_addr.s_addr = inet_addr(argv[1]);
addrlen = sizeof(struct sockaddr_in);
if (connect(socket_fd, (struct sockaddr *)(&server_addr), addrlen) == -1) {
fprintf(stderr, "Connect Error:%s\a\n", strerror(errno));
return;
}
/*創(chuàng)建線程*/
if ((ret = pthread_create(&pid, NULL, func, NULL)) != 0) {
perror("pthread_create err");
return;
}
while (1) {
system("clear");
if (login_f == -1) {
printf(
"\033[0;33m+---------------------------------------------+\033[0m\n");
printf("\33[0;31m\t 1、 注冊 \n\33[0m");
printf("\33[0;31m\t 2、 登錄 \33[0m\n");
} else if (login_f == 1) {
printf(
"\033[0;33m+---------------------------------------------+\033[0m\n");
printf("\33[0;31m\t 3、 公聊\33[0m\n");
printf("\33[0;31m\t 4、 私聊\33[0m\n");
printf("\33[0;31m\t 5、 在線用戶\33[0m\n");
}
printf("\33[0;31m\t 0、 退出\033[0m\n");
printf(
"\033[0;33m+---------------------------------------------+\033[0m\n");
fflush(stdin);
scanf("%d", &sel);
if (sel == 0) {
break;
}
if (login_f == 1) {
min_sel = 3;
max_sel = 5;
} else if (login_f == -1) {
min_sel = 1;
max_sel = 2;
}
if (sel < min_sel || sel > max_sel) {
printf("輸入的數(shù)字不在范圍內(nèi),請重新輸入\n");
continue;
}
switch (sel) {
case 1:
registe(socket_fd);
break;
case 2:
login(socket_fd);
break;
case 3:
broadcast(socket_fd);
break;
case 4:
private
(socket_fd);
break;
case 5:
list_online_user(socket_fd);
break;
case 0:
logout(socket_fd);
break;
default:
break;
}
if (sel == 0) {
exit(0);
}
}
}
本項目學習于從0實現(xiàn)基于Linux socket聊天室-多線程服務器模型-1_一口Linux的博客-CSDN博客
從0實現(xiàn)基于Linux socket聊天室-多線程服務器一個很隱晦的錯誤-2_一口Linux的博客-CSDN博客從0實現(xiàn)基于Linux socket聊天室-多線程服務器一個很隱晦的錯誤-2_一口Linux的博客-CSDN博客
?從0實現(xiàn)基于Linux socket聊天室-實現(xiàn)聊天室的公聊、私聊功能-4_一口Linux的博客-CSDN博客
?文章來源:http://www.zghlxwxcb.cn/news/detail-816014.html
一口Linux博主實力強悍、經(jīng)驗豐富、知識領域廣,筆下文章內(nèi)容豐富,大家可以點點關注文章來源地址http://www.zghlxwxcb.cn/news/detail-816014.html
到了這里,關于Linux socket聊天室的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!