前言
Linux是一个开源系统,也是很多pc系统的“鼻祖”。早期的Linux并没有像现在这样功能强大,也没有这么复杂,随着Linux开发人员几十年如一日的优化,如今的Linux系统几乎涵盖了生活中的各个方面,作为一名软件开发人员,了解Linux系统是非常有必要的。当然,关于Linux系统的知识太多太多,在这里就不献丑了,有兴趣的小伙伴可以私下了解。
网络编程
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。关于TCP/IP协议族的内容在这里就不聊太多了,有兴趣的自行查看。那么今天的项目需要了解的有:TCP/UDP的区别、IPV4/IPV6的区别、什么是IP/域名/端口号、网络字节序、服务器及客户端的区别、网络编程的API、Linux多线程等(哈哈,相信看到这里的人这些都懂),具体内容不再详细说明。
前边的准备工作后差不多就可以写一个网络通信的项目了,既然是通信那肯定是需要多方的,在这个项目里,实现的是多个客户端和服务器的通信(类似vx群聊)。
客户端:
采用多线程思想,主函数初始化客户端套接字,与服务器建立连接,接收消息并显示;消息处理函数接受用户消息,并发送到服务器。
注:ip(点分十进制格式)可通过主函数传参(argv[1])填入,端口号需要与服务器端保持一致,通过argv[2]传入。
#include "main.h"
void snd(void);
#define BUFFSIZE 128
int sockfd;
int main(int argc,char *argv[])
{
pthread_t thread;
struct sockaddr_in serv_addr;
char buf[BUFFSIZE];
// 初始化服务端地址结构
bzero(&serv_addr, sizeof(struct sockaddr_in)); // bzero 清零
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(argv[2]);
inet_pton(AF_INET,argv[1], &serv_addr.sin_addr);
// 创建客户端套接字
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0)
{
perror("fail to socket");
exit(-1);
}
// 与服务器建立连接
printf("connecting... \n");
if(connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) // connect
{
perror("fail to connect");
exit(-2);
}
// 创建发送消息的线程,调用发送消息的函数snd
pthread_create(&thread, NULL, (void *)(&snd), NULL);
// 接收消息
while(1)
{
int len;
if((len=read(sockfd, buf, BUFFSIZE)) > 0)
{
buf[len] = '\0'; // 添加结束符,避免显示缓冲区中残留的内容
printf("\n%s", buf);
fflush(stdout); // fflush 冲洗标准输出,确保内容及时显示
}
}
return 0;
}
// 发送消息的函数
void snd(void)
{
char name[32], buf[BUFFSIZE];
fgets(name, 32, stdin); // fgets会读取输入字符串后的换行符
write(sockfd, name, strlen(name)); // write 写入通信套接字
while(1)
{
fgets(buf, BUFFSIZE, stdin);
write(sockfd, buf, strlen(buf));
if(strcmp(buf, "bye\n") == 0)
exit(0);
}
}
服务器端:
采用多线程思想,主函数负责服务器端的初始化,接受连接;消息处理函数负责客户端的消息处理及转发;quit为退出监测函数,当输入quit时,关闭服务器。
注:由于采用多线程的处理方式,在gcc编译时需要添加 -lpthread(本工程中已经在makefile中添加,正常编译即可)
#include "main.h"
#define PORT 5555
#define MAXMEM 10
#define BUFFSIZE 128
void quit(void);
void rcv_snd(int p);
int listenfd, connfd[MAXMEM];
int main(void)
{
struct sockaddr_in serv_addr, cli_addr;
int i;
time_t ticks;
pthread_t thread;
char buff[BUFFSIZE];
printf("running...\n(Prompt: enter command ""quit"" to exit server)\n");
bzero(&serv_addr, sizeof(struct sockaddr_in));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
listenfd = socket(AF_INET, SOCK_STREAM, 0);
if(listenfd < 0)
{
perror("fail to socket");
exit(-1);
}
if(bind(listenfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
{
perror("fail to bind");
exit(-2);
}
listen(listenfd, MAXMEM);
pthread_create(&thread, NULL, (void *)(quit), NULL);
// 将套接字描述符数组初始化为-1,表示空闲
for(i=0; i<MAXMEM; i++)
connfd[i] = -1;
while(1)
{
int len;
for(i=0; i<MAXMEM; i++)
{
if(connfd[i] == -1)
break;
}
// accept 从listen接受的连接队列中取得一个连接
connfd[i] = accept(listenfd, (struct sockaddr *)&cli_addr, &len);
if(connfd[i] < 0)
{
perror("fail to accept");
}
ticks = time(NULL);
//sprintf(buff, "%.24s\r\n", ctime(&ticks));
printf("%.24s\n\tconnect from: %s, port %d\n",\
ctime(&ticks),\
inet_ntop(AF_INET,\
&(cli_addr.sin_addr),\
buff, BUFFSIZE),
ntohs(cli_addr.sin_port));
/*针对当前套接字创建一个线程,对当前套接字的消息进行处理*/
pthread_create(malloc(sizeof(pthread_t)), NULL, (void *)(&rcv_snd), (void *)i);
}
return 0;
}
void quit(void)
{
char msg[10];
while(1)
{
scanf("%s", msg);
if(strcmp(msg, "quit") == 0)
{
printf("Byebye... \n");
close(listenfd);
exit(0);
}
}
}
void rcv_snd(int n)
{
int len, i;
char name[32], mytime[32], buf[BUFFSIZE];
time_t ticks;
int ret;
// 获取此线程对应的套接字用户的名字
write(connfd[n], "your name: ", strlen("your name: "));
len = read(connfd[n], name, 32);
if(len > 0)
name[len-1] = '\0'; // 去除换行符
strcpy(buf, name);
strcat(buf, "\tjoin in\n\0");
// 把当前用户的加入 告知所有用户
for(i=0; i<MAXMEM; i++)
{
if(connfd[i] != -1)
write(connfd[i], buf, strlen(buf));
}
while(1)
{
char temp[BUFFSIZE];
if((len=read(connfd[n], temp, BUFFSIZE)) > 0)
{
temp[len-1] = '\0';
// 当用户输入bye时,当前用户退出
if(strcmp(temp, "bye") == 0)
{
close(connfd[n]);
connfd[n] = -1;
pthread_exit(&ret);
printf("用户%s已退出\n",name);
}
ticks = time(NULL);
sprintf(mytime, "%.24s\r\n", ctime(&ticks));
//write(connfd[n], mytime, strlen(mytime));
strcpy(buf, name);
strcat(buf, "\t");
strcat(buf, mytime);
strcat(buf, "\r\t");
strcat(buf, temp);
strcat(buf, "\n");
for(i=0; i<MAXMEM; i++)
{
if(connfd[i] != -1)
write(connfd[i], buf, strlen(buf));
}
}
}
}
具体工程请参考这里。