基于TCP的服务器/客户机的模型
1.基本特征
面向连接的,可靠的,保证数据完整性和有序性
每个发送都有应答,若在时间窗口内没有收到A的应答,则从A开始重新发送。
编程模型
三次握手四次分手
服务器的实现
服务器的思路是等待客户端的连接,并且实时接收每个连接上来了的客户端发送的消息,并将这个消息包打包发送给其他客户端。
1.创建套接字
int sfd = socket(AF_INET,SOCK_STREAM,0);
//AF_INET: 基于TCP/IPv4(32位IP地址)的网络通信;
//SOCKET_STREAM:数据流协议,即TCP协议;
if(sfd == -1){
perror("socket");
return -1;
}
2.准备通信地址
struct sockaddr_in addr;
/*
struct sockaddr_in
19.{
20. // 地址族
21. sa_family_t sin_family;
22.
23. // 端口号
24. // unsigned short, 0-65535
25. // 逻辑上表示一个参与通信的进程
26. // 使用时需要转成网络字节序
27. // 0-1024端口一般被系统占用
28. // 如:21-FTP、23-Telnet、80-WWW
29. in_port_t sin_port;
30.
31. // IP地址
32. struct in_addr sin_addr;
33.};
*/
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr(ip);
socketlen_t addrlen = sizeof(addr);
3.绑定套接字
int ret = bind(sfd,(const struct sockaddr*)(&addr),addrlen);
if(ret == -1){
perror("bind");
return -1;
}
4.监听套接字
将sockfd参数所标识的套接字标记为被动模式,使之可用于接受连接请求。
if(listen(sfd,10)==-1){
//10表示最大的客户端排队数为10
perror("listen");
return -1;
}
5.接受连接
自定义客户端类型
#define MAX 100
typedef struct Client{
int cfd;
char name[40];
}Client;
Client client[MAX] = {
};
size_t cnt = 0;
对线程互斥锁初始化
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
while(1){
struct sockaddr_in caddr;
socklen_t clen = sizeof(caddr);
printf("等待客户机连接...\n");
int cfd = accept(sfd,(struct sockaddr*)(&caddr),&clen);
/*从sockfd参数所标识套接字的未决连接请求队列中,提取第一个连接请求。
同时创建一个新的套接字,用于在该连接中通信,返回该套接字的描述符。
caddr和clen参数用于输出连接请求发起者的地址信息。
*/
if(cfd == -1){
perror("accept");
return -1;
}
//向其他客户端发送上线消息
char buf[100] = {
};
//把客户端的网名和套接字放在客户端的类型中
recv(cfd,&client[cnt].name,40,0);
client[cnt].cfd = cfd;
strcpy(buf,"您的好友");
strcat(buf,client[cnt].name);
strcat(buf,"上线啦!");
//广播消息
broadcast(buf,client[cnt]);
pthread_t id;
ret = pthread_create(&id,NULL,pthread_run,(void*)(&client[cnt]));
//每当有一个客户机连接上来,变创建一个线程,该线程用于接收这个客户机的消息,并将这些消息加工广播给其他连接上来的客户机。
cnt++;
if(ret != 0){
printf("pthread_create:%s\n",strerror(ret));
continue;
}
printf("有一个客户机成功连接:ip <%s> port [%hu]\n",inet_ntoa