经过好几天的日夜奋斗,总算把这个聊天室给做出来了,虽然说不上多好,但也是这几天从早到晚劳动的成功,所以就写这篇博文来记录一下啦。别的不敢说,确保能用就是了,完整代码在最后哦~
当然啦,如果有幸被转发,还请注明来处哈~

一、功能

这个Linux下C版本的多人网络聊天室具备以下几个基本功能(或者说需求):
(一)C/S模式,IPv4的TCP通信;
(二)客户端登录需要账号密码,没有账号需要注册;
(三)服务器每接收到一个客户端的连接,就产生一个线程为其服务;
(四)进入聊天室后,客户端可以选择群聊(发给所有人),也可以选择私聊(发给指定账号的人);
(五)服务器可以限定同时登录聊天室的账号的最大数量;
(六)用sqlite3创建保存用户账号信息的数据库;
(七)客户端(服务器端要的话应该也可以)自动获取本地主机未被占用的端口号以连接服务器。

由于时间关系,原本设定的用户登录或退出时通知所有人和保存聊天记录等功能,笔者就没能添加进去。若有兴趣,读者可参考以下想法进行添加:“用户登录或退出时通知所有人”这点可以在用户确认登录后但尚未进入聊天室时通过send/recv实现,而“保存聊天记录”这点就像保存用户账号信息一样保存到数据库(当然更简单的方法是保存到普通文件中)。另外,也有一大点笔者心有余而力不足,有大佬路过时还请指教指教,就是多线程之间交互的问题,例如主线程如何等待所有子线程正常退出等,这一块我不熟悉~

二、主要功能实现分析

(一)网络通信基本步骤
简单来讲就是:客户端——创建套接字,配置网络结构体,绑定,请求连接,正常通信;服务器——创建套接字,配置网络结构体,绑定,监听,接受连接,正常通信。至于网络通信基本原理详细点的介绍可以看我这篇博文介绍——网络通信基本原理。
在这里要注意以下两点:
一个是配置网络结构体,这点其实可以在man手册看到相关(结构体)说明,具体来说就是设定端口号、IP协议(IPv4/IPv6)以及通信对象的IP地址,配置网络结构体是为了后续bind服务的。

 //配置网络结构体struct sockaddr_in myaddr; myaddr.sin_port = htons(50000);    //应用层端口号myaddr.sin_family = AF_INET;       //IPv4协议myaddr.sin_addr.s_addr = inet_addr("0.0.0.0");//通信对象的IP地址,0.0.0.0表示所有IP地址,也可以指定IP

另一个是所谓的“自动获取本地主机未被占用的端口号”。由于笔者只是在自己一台电脑上开多个虚拟终端来运行client,连接的服务器在本地主机(127.0.0.1),而我又不想手动地一个客户端给他传递一个端口号,所以就想到了这个方法。具体来讲,就是从50001开始随便一个一个去尝试bind,绑定失败就下一个,绑定成功的话那就它了。至于为什么是50001开始呢,一个依据是《计算机网络》中说到客户端使用的端口号为49152~65535,然后我就在这个范围内选个中意的范围了。

portnum = 50001;while(1){if(portnum > 65535){printf("bind error because of no port number available!\n");return -1;}(.......配置网络结构体)ret = bind(sockfd,(struct sockaddr *)&myaddr,sizeof(myaddr));if(ret == -1) {continue;}else{printf("bind successfully!\n");break;}}

(二)用户账号信息如何验证、存储
讲到这点,我们得先看看数据库方面的知识了,这里我用的是SQLite,其操作简单讲就是增、删、查、改这些,具体地C接口在这个程序中我使用的有sqlite3_open_v2()、sqlite3_exec()、sqlite3_get_table()。其实说到数据库,我也是在做这个小项目过程中现学现卖的,所以就不多班门弄斧了,不过一些基本操作咱可以看这个网站。在这里我就介绍下我怎么存储怎么验证。
(1)存储
说到存储,在我这个小项目里那就只能是在注册过程才会用到了,而注册是在客户端进行,所以要先获取ID信息(昵称和密码),然后发送给服务器服务器读取用户账号信息表格并分配一个尚未使用的ID,然后将账号信息(ID,name,passwd)插入到数据库,而插入成功了也就是说注册成功了,那就要返回一个注册成功标志给客户端

//这个代码段是用来计算用户账号信息表中有多少条记录的,将这个数量加上起始的100的结果作为账号分配,就能使得账号不重复了。
int j = 100;char *sql1 = "select * from hwy_id_sheet;";sqlite3_get_table(db,sql1,&azResult,&nrow,&ncolumn,&errmsg);j = j+ nrow;memset(ack_ID_info,0,4);sprintf(ack_ID_info,"%d",j);//---itoa

(2)验证登录
验证起始也跟前面“存储”差不多,就是:客户端获取登录信息(ID,passwd)–发送给服务器–服务器在用户账号信息表中查找是否有相关记录,有则代表登录成功,没有就代表登录失败–返回登录状态–若登录成功则也返回(name)。

sprintf(sql,"select * from hwy_id_sheet where id = '%s' and passwd = '%s';",client_ID_info.id,client_ID_info.passwd);sqlite3_get_table(db,sql,&azResult,&nrow,&ncolumn,&errmsg);

(三)群聊OR私聊
这好办,程序中,当写入的聊天信息中“目标ID”为“999”时发给所有人,也就是所谓的群聊;当写入的聊天信息中“目标ID”为其他ID如“101”时,则发给对应用户。

void SendMsgToAll(char *msg)
{int i;for(i=0;i<CLIENT_MAX;i++){if(id_to_fd[i].client_fd != 0){printf("sendto%s\n",id_to_fd[i].client_id);printf("%s\n",msg);send(id_to_fd[i].client_fd,msg,strlen(msg),0);}}
}
void SendMsgToSb(int destfd,char *msg)
{int i;for(i=0;i<CLIENT_MAX;i++){if(id_to_fd[i].client_fd == destfd ){printf("sendto%s\n",id_to_fd[i].client_id);printf("%s\n",msg);send(destfd,msg,strlen(msg),0);break;}}
}

(四)服务器端与客户端对应的fd和ID如何绑定?
因为服务器端accept客户端后会产生client_sockfd,之后服务器就用这个client_sockfd来与客户端通信,然而我们私聊时是无法得知服务器给某个客户端分配了哪一个client_sockfd的,只能通过ID来指定通信用户,所以就产生了这个问题了。至于怎么绑定,看下面结构体就知道了~

//id--fd结构体类型
typedef struct
{int client_fd;char client_id[4];
}client_id_to_fd;

(1)服务器每接收到客户端连接时就会产生一个对应的fd,这事写进client_fd中;
(2)服务器验证客户端登录账号成功时会获取一个正确的ID,这时再写进去。
由此也就实现了这一绑定了,之后怎么通信,看上面步骤(三)。

三、完整代码

(1)头文件–hwy_network_chat.h

#ifndef __HWY_NETWORK_CHAT_H__
#define __HWY_NETWORK_CHAT_H__//聊天室中能注册登陆的最大人员数量
#define  PEOPLE_NUM_MAX   10
//聊天信息(发送结构体)长度
#define  CHAT_STRUCT_SIZE (POSITION_CONTENT+128)
//客户端最大连接数量
#define  CLIENT_MAX       3#define  POSITION_SELFID  0
#define  POSITION_NAME    (4+POSITION_SELFID)
#define  POSITION_DESTID  (4+POSITION_NAME)
#define  POSITION_TIME    (4+POSITION_DESTID)
#define  POSITION_CONTENT (26+POSITION_TIME)//聊天室中人员身份信息
typedef struct
{char id[4];     //登陆帐号char name[4];     //昵称char passwd[8];//登陆密码
}hwy_people_t;//聊天消息之发送的格式
typedef struct
{char self_id[4];   //自身IDchar name[4];     //自身昵称char dest_id[4];  //目标IDchar time[26];    //发送时间char content[128];    //发送内容
}hwy_send_msg_t;//聊天消息之接收的格式
typedef struct
{char who[4];       //发送者char time[26]; //发送时间char content[128];    //发送内容
}hwy_recv_msg_t;#endif

(2)client.c

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <errno.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <pthread.h>
#include "hwy_network_chat.h"
#include <stdlib.h>//登录者帐号信息
static hwy_people_t client_id;/** 功能:建立网络通信的初始化工作,包括创建套接字和绑定可用端口号* 输入参数:无* 输出参数:成功返回用于网络通信的套接字,失败返回-1*/
int sock_init(void)
{int ret;int portnum;//端口号//创建套接字--设置通信为IPv4的TCP通信int sockfd = socket(AF_INET,SOCK_STREAM,0);if(sockfd == -1){perror("socket failed");return -1;}//配置网络结构体/*从50001开始,查找主机中尚未被占用的端口号,然后绑定*/struct sockaddr_in myaddr;portnum = 50001;while(1){if(portnum > 65535){printf("bind error because of no port number available!\n");return -1;}myaddr.sin_port = htons(portnum++);       //应用层端口号myaddr.sin_family = AF_INET;          //IPv4协议myaddr.sin_addr.s_addr = inet_addr("0.0.0.0");//通信对象的IP地址,0.0.0.0表示所有IP地址//绑定套接字和结构体----主机自检端口号是否重复,IP是否准确ret = bind(sockfd,(struct sockaddr *)&myaddr,sizeof(myaddr));if(ret == -1){continue;}else{printf("bind successfully!\n");break;}}return sockfd;
}/**功能:连接服务器*输入参数:套接字*输出参数:连接成功与否的标志,成功返回1,失败返回0* */
int sock_client(int sockfd)
{//连接服务器struct sockaddr_in srcaddr;srcaddr.sin_port = htons(50000);//服务器的应用层端口号srcaddr.sin_family = AF_INET;//服务器的IPv4协议srcaddr.sin_addr.s_addr = inet_addr("127.0.0.1");//服务器的IP地址-本地主机int ret = connect(sockfd,(struct sockaddr*)&srcaddr,sizeof(srcaddr));if(ret == -1){perror("connect failed");return -1;}printf("connect OK\n");return 0;
}/**功能:验证登录或注册帐号信息*输入:客户端套接字,登录/注册选择*输出:登录成功1,注册成功2,任意失败-1* */
int check_id(int sockfd,int choice)
{char ID_info[17];//存储帐号信息以及登录/注册选择int i;char status[16];//存储登录/注册状态int ret;printf("check ID information!\n");memset(ID_info,0,17);//登录,携带ID 和密码if(1 == choice){ID_info[0]='1';memcpy(ID_info+1,client_id.id,4);memcpy(ID_info+9,client_id.passwd,8);}//注册,携带昵称和密码else {ID_info[0]='2';memcpy(ID_info+5,client_id.name,4);memcpy(ID_info+9,client_id.passwd,8);}//发送帐号信息给服务器端进行验证for(i=0;i<16;i++){if(ID_info[i] == '\0'){ID_info[i] = '/';}}ID_info[i] = '\0';ret = send(sockfd, ID_info, strlen(ID_info), 0);if(-1 == ret){perror("send id_info error!");return -1;}//接收帐号验证信息memset(status,0,16);ret = recv(sockfd,status,16,0);if(-1 == ret){perror("recv id_info error!");return -1;}if(memcmp(status,"successfully!",13)==0){//登录成功//printf("login successfully!\n");send(sockfd,"ok",3,0);ret = recv(sockfd,client_id.name,4,0);if(-1 == ret){perror("recv ack_id_info error");return -1;}return 1;}else if(memcmp(status,"sign up",7)==0){//注册成功//printf("sign up successfully!\n");send(sockfd,"ok",3,0);ret = recv(sockfd,client_id.id,4,0);if(-1 == ret){perror("recv ack_id_info error");return -1;}return 2;}else {printf("login or sign up error!\n");return -1;}
}/**功能:登录界面*输入:客户端套接字*输出:成功0,失败-1*注意:登录失败可以重新登录,其他失败会退出*/
int hwy_login(int sockfd )
{int choice;//1--代表登录,2代表注册int login_status;//登录状态,-1代表失败,1代表成功int signup_status;//注册状态,-1代表失败,2代表成功while(1){printf("1------------login\n2------------sign up\n");scanf("%d",&choice);if(1 == choice){//登录,输入ID和密码printf("ID:");scanf("%s",client_id.id);  printf("passwd:");scanf("%s",client_id.passwd);login_status=check_id(sockfd,choice);if(1 == login_status){//登录成功,进入聊天室printf("欢迎登录聊天室~%s\n",client_id.name);break;}else continue;}else if(2 == choice){//注册,输入昵称和密码printf("name:");scanf("%s",client_id.name);printf("passwd:");scanf("%s",client_id.passwd);signup_status = check_id(sockfd,choice);if(2 == signup_status){//注册成功,返回登录界面printf("注册成功\n");printf("你的帐号为:%s\n请重新登录\n",client_id.id);continue;}else {//注册失败return -1;}}else {printf("错误!请输入正确数值!\n");continue;}}return 0;
}/**功能:获取聊天具体内容*输入:保存聊天内容的指针*输出:无* */
//获取聊天具体内容
void get_send_content(char get_send_buffer[CHAT_STRUCT_SIZE])
{int i;char dest[4];//目标帐号char time_buf[26];//时间time_t t;time(&t);memset(get_send_buffer,0,CHAT_STRUCT_SIZE);//发送者memcpy(get_send_buffer+POSITION_SELFID,client_id.id,4);memcpy(get_send_buffer+POSITION_NAME,client_id.name,4);//接收者//printf("你要发给谁?\n");scanf("%s",get_send_buffer+POSITION_DESTID);//发送内容//printf("你要发什么?\n");scanf("%s",get_send_buffer+POSITION_CONTENT);//发送时间memcpy(get_send_buffer+POSITION_TIME,ctime_r(&t,time_buf),26);  for(i=0;i<POSITION_CONTENT;i++){if(get_send_buffer[i] == '\0')get_send_buffer[i] = '/';}
}/**功能:处理接收信息的子线程处理函数** */
void *pthread_recv_func (void *arg)
{int sockfd = *(int *)arg;int ret;int i;hwy_recv_msg_t hwy_msg;//谁发的,什么时候发,发了什么char recv_buffer[CHAT_STRUCT_SIZE];//接收内容缓冲区printf("现在可以聊天了~\n");while(1){memset(recv_buffer,0,CHAT_STRUCT_SIZE);ret=recv(sockfd, recv_buffer,CHAT_STRUCT_SIZE, 0);if(ret == -1){printf("client received error!\n");return;}else {for(i=0;i<POSITION_CONTENT;i++){if(recv_buffer[i]== '/')recv_buffer[i]= '\0';}memcpy(hwy_msg.who,recv_buffer+POSITION_NAME,4);memcpy(hwy_msg.time,recv_buffer+POSITION_TIME,26);memcpy(hwy_msg.content,recv_buffer+POSITION_CONTENT,128);printf("%s:%s\n%s",hwy_msg.who,hwy_msg.content,hwy_msg.time);}}
}void *pthread_send_func (void *arg)
{int sockfd = *(int *)arg;char send_buffer[CHAT_STRUCT_SIZE];//发送内容缓冲区while(1){get_send_content(send_buffer);//输入bye退出聊天室if(memcmp(send_buffer,"bye",3)== 0 ){send(sockfd,"byebye~", strlen(send_buffer), 0);close(sockfd);exit(0);}send(sockfd, send_buffer, strlen(send_buffer), 0);}
}int main(int argc,char *argv[])
{int sockfd = sock_init();if(sockfd == -1){printf("sock_init error!\n");return -1;}int ret = sock_client(sockfd);if(-1 == ret){printf("sock_client error!\n");return -1;}ret = hwy_login(sockfd);if(ret == -1){perror("hwy_login function error!");return -1;}//一个线程负责接收信息,一个线程负责发送信息pthread_t tid1,tid2;pthread_create(&tid2,0,pthread_recv_func,&sockfd);pthread_create(&tid1,0,pthread_send_func,&sockfd);while(1){sleep(9);}close(sockfd);return 0;
}   

(3)server.c

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <errno.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include "hwy_network_chat.h"
#include "sqlite3.h"
#include <stdlib.h>//id--fd结构体类型
typedef struct
{int client_fd;char client_id[4];
}client_id_to_fd;//将用户帐号和占用的文件描述符一一对应起来,
//方便后续一对一通信
client_id_to_fd id_to_fd[CLIENT_MAX];//数据库的连接指针
static sqlite3 *db = NULL;/**功能:建立网络通信的初始化工作,包括创建套接字和绑定可用端口号*输入:无*输出:成功返回套接字,失败返回-1* */
int sock_init(void )
{int ret;//创建套接字--设置通信为IPv4的TCP通信int sockfd = socket(AF_INET,SOCK_STREAM,0);if(sockfd == -1){perror("socket failed");return -1;}//配置网络结构体struct sockaddr_in myaddr; myaddr.sin_port = htons(50000);   //应用层端口号myaddr.sin_family = AF_INET;       //IPv4协议myaddr.sin_addr.s_addr = inet_addr("0.0.0.0");//通信对象的IP地址,0.0.0.0表示所有IP地址//绑定套接字和结构体----主机自检端口号是否重复,IP是否准确ret = bind(sockfd,(struct sockaddr *)&myaddr,sizeof(myaddr));if(ret == -1){perror("bind failed");return -1;}return sockfd;
}/**功能:把服务器接收的信息发给所有人*输入:聊天信息具体内容*输出:无* */
void SendMsgToAll(char *msg)
{int i;for(i=0;i<CLIENT_MAX;i++){if(id_to_fd[i].client_fd != 0){printf("sendto%s\n",id_to_fd[i].client_id);printf("%s\n",msg);send(id_to_fd[i].client_fd,msg,strlen(msg),0);}}
}/** 功能:把服务器接收的消息发给指定的人* 输入:目标帐号所绑定的fd,具体聊天内容* 输出:无*/
void SendMsgToSb(int destfd,char *msg)
{int i;for(i=0;i<CLIENT_MAX;i++){if(id_to_fd[i].client_fd == destfd ){printf("sendto%s\n",id_to_fd[i].client_id);printf("%s\n",msg);send(destfd,msg,strlen(msg),0);break;}}
}//数据库初始化工作
//连接数据库,创建表格
void hwyDataBase_init(void )
{// 打开hwyhwy.db的数据库,如果数据库不存在,则创建并打开sqlite3_open_v2("hwyhwy.db",&db,SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,//可读可写可创建NULL);if(db==NULL){perror("sqlite3_open_v2 faield\n");exit(-1);}//在数据库中创建表格//ID 昵称 密码,ID为主键char *errmsg = NULL;char *sql = "CREATE TABLE if not exists hwy_id_sheet(id text primary key,\name text not null,passwd text not null);";sqlite3_exec(db, sql, NULL, NULL, &errmsg);
}/**功能:验证客户端传来的ID信息*输入:服务器accept后产生的套接字*输出:验证状态,1登录成功,2注册成功,其他失败* */
int check_recv_id(int fd )
{int ret;int i;hwy_people_t client_ID_info; //帐号信息结构体ID_infochar recv_ID_info[17];//客户端传来的帐号信息其登录/注册选择char ack_ID_info[4];char ack_ok[3];char *errmsg = NULL;char sql[128];int nrow=0;int ncolumn=0;char **azResult=NULL;char status[16];//接收ID_infomemset(recv_ID_info,0,17);memset(ack_ID_info,0,4);memset(sql,0,128);memset(status,0,16);ret = recv(fd, recv_ID_info, 17, 0);if(-1 == ret){perror("recv error!");return -1;}//打印接收到的信息for(i=0;i<17;i++){if(recv_ID_info[i] == '/')recv_ID_info[i] = '\0';}memcpy(client_ID_info.id,recv_ID_info+1,4);memcpy(client_ID_info.name,recv_ID_info+5,4);memcpy(client_ID_info.passwd,recv_ID_info+9,8);//登录,验证输入的ID和passwd是否正确if(recv_ID_info[0] == '1'){sprintf(sql,"select * from hwy_id_sheet where id = '%s' and passwd = '%s';",client_ID_info.id,client_ID_info.passwd);sqlite3_get_table(db,sql,&azResult,&nrow,&ncolumn,&errmsg);if(nrow == 0){//没有匹配项,登录验证失败strcpy(status,"login failed!");send(fd,status,strlen(status),0);return -1;}else {//登录验证成功memset(status,0,16);strcpy(status,"successfully!");send(fd,status,strlen(status),0);recv(fd,ack_ok,3,0);//在这里绑定client_fd---client_idfor(i=0;i<CLIENT_MAX;i++){if(id_to_fd[i].client_fd == fd){memcpy(id_to_fd[i].client_id,client_ID_info.id,4);break;}}//发送用户昵称strcpy(ack_ID_info,azResult[4]);send(fd,ack_ID_info,strlen(ack_ID_info),0);return 1;}}//注册,根据昵称和密码注册、记录帐号信息,并返回帐号else {int j = 100;char *sql1 = "select * from hwy_id_sheet;";sqlite3_get_table(db,sql1,&azResult,&nrow,&ncolumn,&errmsg);j = j+ nrow;memset(ack_ID_info,0,4);sprintf(ack_ID_info,"%d",j);//---itoamemcpy(client_ID_info.id,ack_ID_info,4);sprintf(sql,"insert into hwy_id_sheet values('%s','%s','%s'); ",client_ID_info.id,client_ID_info.name,client_ID_info.passwd);ret = sqlite3_exec(db, sql, NULL, NULL, &errmsg);if(ret == SQLITE_OK){printf("注册成功\n");memset(status,0,16);strcpy(status,"sign up");send(fd,status,strlen(status),0);recv(fd,ack_ok,3,0);//发送用户帐号信息send(fd,ack_ID_info,strlen(ack_ID_info),0);return 2;}else {printf("注册失败\n");memset(status,0,16);strcpy(status,"sign up error");send(fd,status,strlen(status),0);return -1;}}
}//每接收一个客户端的连接,便创建一个线程
void * thread_func (void *arg)
{int fd = *(int *)arg;int ret;int i;hwy_send_msg_t hwy_C_SendMsg;printf("pthread = %d\n",fd);char recv_buffer[CHAT_STRUCT_SIZE];//验证登录/注册信息while(1){ ret = check_recv_id(fd);printf("check_recv_id = %d\n",ret);if(ret == 1){//成功登录printf("登录成功\n");break;}else if(ret == 2){//注册成功,需要重新登录continue;}else {//验证失败,服务器不退出continue;}}//登录成功,处理正常聊天的信息--接收与转发while(1){memset(recv_buffer,0,CHAT_STRUCT_SIZE);ret = recv(fd, recv_buffer, CHAT_STRUCT_SIZE, 0);if(-1 == ret){perror("recv error!");return;}else if(ret>0){printf("接收到的内容为:%s\n",recv_buffer);if(memcmp(recv_buffer+POSITION_DESTID,"999",3)== 0)SendMsgToAll(recv_buffer);else{for(i = 0;i< CLIENT_MAX;i++){if(memcmp(id_to_fd[i].client_id,recv_buffer+POSITION_DESTID,\3)== 0){SendMsgToSb(id_to_fd[i].client_fd,recv_buffer);break;}}}}}
}void service(int sock_fd)
{printf("服务器启动...\n");listen(sock_fd,CLIENT_MAX);while(1){struct sockaddr_in clientaddr;int len = sizeof(clientaddr);int client_sock = accept(sock_fd,(struct sockaddr*)&clientaddr,&len);if(client_sock== -1){printf("accept failed...\n");continue;}printf("accept OK!\n");int i;for(i=0;i<CLIENT_MAX;i++){if(id_to_fd[i].client_fd == 0){id_to_fd[i].client_fd = client_sock;printf("client fd = %d\n",client_sock);//有客户端连接之后,启动线程为此客户端服务pthread_t tid;pthread_create(&tid, 0,thread_func,&client_sock);break;}}if(i == CLIENT_MAX){char * str = "对不起,聊天室已满人!\n";send(client_sock,str,sizeof(str),0);close(client_sock);}}}int main(int argc,char *argv[])
{int sock_fd = sock_init();hwyDataBase_init();service(sock_fd);return 0;
}

(3)数据库相关代码
sqlite3.c sqlite3.h这个可以下载。

(4)编译方法
gcc client.c -lpthread -o client
gcc server.c sqlite.c -ldl -lpthread -o server
(路过的还请赐教下这个怎么写Makefile??~)

Linux C多人网络聊天室相关推荐

  1. 视频教程-多人网络聊天室-Unity3D

    多人网络聊天室 广州市码锋网络有限责任公司创始人,从事游戏开发九年,熟练前后端的各种技术,我很乐意将企业中商用的技术分享给你,帮助你解决工作的各种问题. 官剑铭 ¥39.00 立即订阅 扫码下载「CS ...

  2. Linux C/TCP多线程网络聊天室

    多线程TCP网络聊天室 我们都知道TCP是一种基于连接的传输协议,使用多线程来实现其实步骤很简单. 首先说明一下项目:服务端开启后,直接运行客户端可以加入聊天室,同时其他客户端可以接收到某某客户端进入 ...

  3. 【python】实现多人网络聊天室

    利用socket套接字实现多台计算机之间的连接 server: import socket #socket 是一种套接字,用来描述IP和port import os import threading ...

  4. Java NIO示例:多人网络聊天室完整代码

    服务端:  package cn.zhangxueliang.herostory.chatroom;import java.io.IOException; import java.net.InetSo ...

  5. Java NIO示例:多人网络聊天室

    一个多客户端聊天室,支持多客户端聊天,有如下功能: 功能1: 客户端通过Java NIO连接到服务端,支持多客户端的连接 功能2:客户端初次连接时,服务端提示输入昵称,如果昵称已经有人使用,提示重新输 ...

  6. JAVA 基于TCP的多人网络聊天室 实现过程

    主要实现功能 多人在线聊天 发送私密消息 新用户加入提示 用户离开提示 功能演示 服务器: package dome;import java.io.DataInputStream; import ja ...

  7. 基于linux网络聊天室的设计,参考基于linux网络聊天室的设计.doc

    参考基于linux网络聊天室的设计 长沙理工大学<高级操作系统>课程设计报告学 院 计算机与通信工程 专 业 计算机科学与技术 班 级 学 号 学生姓名 指导教师 课程成绩 完成日期 课程 ...

  8. linux点对点聊天室的实现与设计心得,基于Socket接口的Linux与Windows网络聊天室设计与实现...

    陈洁 孟晓景 摘要:为了实现Linux与Windows跨平台通信,及时共享信息,构建了一个适用于跨平台的网络聊天室通信程序.先搭建跨平台通信环境,然后使用Socket套接字网络编程接口实现通信.整个系 ...

  9. linux下的网络聊天室

    linux下的网络聊天室 一版:多reactor模式实现高并发服务器. sever.c #include "msg.h" #include "pthreadpool.h& ...

最新文章

  1. 最新!中国内地高校ESI排名出炉:342所大学上榜
  2. centosx64 6.3安装视频组件
  3. 10.Ubuntu下的source insight增加使用期限90天
  4. 正则表达式抓取文件内容中的http链接地址
  5. U盘里的文件夹会以应用程序的方式出现解决
  6. Nginx 过滤模块
  7. 说说你对Jdk并发包中的CAS实现的了解?
  8. 2006 年100 款最佳安全工具谱
  9. Naive Bayes text classification
  10. GTD+敏捷=一种新的计划列表理念和方法。
  11. Linux下QT开发
  12. RK Android系统开机启动流程
  13. linux pwm控制蜂鸣器 滴滴_linux pwm实现蜂鸣器
  14. sdn主要包含哪些接口_SDN个人理解
  15. 数据分析的N种特征方法实例
  16. 震撼!自动化|从收货、入库、出库、分拣、输送……
  17. 东软睿驰 bms车载测试工程师
  18. Pet包 资源 Linux,PET文件扩展名 - 什么是.pet以及如何打开? - ReviverSoft
  19. 上海往事之教会宝宝学游泳
  20. 运用String.format格式化方法和消息枚举类型创建消息模板

热门文章

  1. lc谐振计算机网络,LC谐振回路解析,LC谐振回路的选频特性
  2. [024] 11大Java开源中文分词器的使用方法和分词效果对比
  3. 电商数据分析指标体系实例。
  4. foc学习笔记3——电流环
  5. C# Winform添加背景图片后加载的时候控件卡
  6. MATLAB强化学习实战(十二) 创建自定义强化学习算法的智能体
  7. 循环神经网络:用训练好的model写诗歌
  8. Netgear R6220刷潘多拉盒子后的配置,提高网速
  9. 政治的人文素养 Zz
  10. CentOS 7 找回root密码