一、客户端升级(二)
发送结构化网络消息
这里是指使用结构体传输数据。
需要考虑字节序对齐,如果客户端和服务器编程语言相同,且在同一环境下,通常字节是相同的,但是可能存在字节长度不一致而导致消息传输与接收不一致的额情况发生。
新增内容
新增包头:描述本次传输数据的大小和作用。
服务器和客户端新增对登陆登出的状态描述
网络报文定义
报文包括两个部分,是网络传输的基本消息单元
包头:描述本次数据包的大小和作用
包体:传输的数据
端口复用
设置Socket属性:SO_REUSEADDR:允许在bind过程中本地地址重复使用
定义如下(仅供参考,需要拓展)
#define LOGIN_IN 1
#define LOGIN_OUT 2
typedef struct head
{
int fun; //定义数据包头类型
int dataLength; //描述数据长度
}HEAD_T;
typedef struct user //定义用户数据包体
{
char UserName[32];
char PassWord[32];
}USER_T;
typedef struct ret //定义服务器应答包体
{
int flag; //定义用户登录返回状态
char name[32]; //返回用户名
}RET_T;
登录界面绘制
登录按钮槽函数
进行登录数据包的组装
并发服务器设计流程
服务器修正代码
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#define PORT_NUMBER 8888
#define BACKLOG 10
typedef struct head
{
int fun; //定义数据包头
int dataLength; //描述数据长度
}HEAD_T;
typedef struct user //定义用户数据包体
{
char UserName[32];
char PassWord[32];
}USER_T;
typedef struct ret //定义服务器应答包体
{
int flag; //定义用户登录返回状态
char name[32]; //返回用户名
}RET_T;
/* socket->bind->listen->accept->send/recv->close*/
int main(int argc, char** argv)
{
int sock_fd, new_fd;
struct sockaddr_in server_addr;
struct sockaddr_in client_addr;
int ret;
socklen_t addr_len;
int recv_len;
unsigned char recv_buf[1000];
int client_num = -1;
signal(SIGCHLD, SIG_IGN);
/* socket */
sock_fd = socket(AF_INET, SOCK_STREAM, 0);//AF_INET:IPV4;SOCK_STREAM:TCP
if (-1 == sock_fd)
{
fprintf(stderr, "socket error:%s\n\a", strerror(errno));
exit(1);
}
//设置Socket属性:SO_REUSEADDR:允许在bind过程中本地地址重复使用
int iSockopt = 1;
if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR,
(const char*)&iSockopt, sizeof(int)) < 0)
{
close(sock_fd);
exit(1);
}
/* set server sockaddr_in */
memset(&server_addr, 0, sizeof(struct sockaddr_in));//clear
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);//INADDR_ANY:This machine all IP
server_addr.sin_port = htons(PORT_NUMBER);
/* bind */
ret = bind(sock_fd, (struct sockaddr*)(&server_addr), sizeof(struct sockaddr));
if (-1 == ret)
{
fprintf(stderr, "bind error:%s\n\a", strerror(errno));
close(sock_fd);
exit(1);
}
/* listen */
ret = listen(sock_fd, BACKLOG);
if (-1 == ret)
{
fprintf(stderr, "listen error:%s\n\a", strerror(errno));
close(sock_fd);
exit(1);
}
printf("server start!!!\n");
//数据包定义
char BackData[128] = { 0 };
HEAD_T* read_head = (HEAD_T*)malloc(sizeof(HEAD_T)); //用户数据包头
USER_T* read_user = (USER_T*)malloc(sizeof(USER_T)); //用户数据包体
HEAD_T* ret_head = (HEAD_T*)malloc(sizeof(HEAD_T)); //服务应答数据包头
RET_T* ret_info = (RET_T*)malloc(sizeof(RET_T));
/* accept */
while (1)
{
addr_len = sizeof(struct sockaddr);
new_fd = accept(sock_fd, (struct sockaddr*)&client_addr, &addr_len);
if (-1 == new_fd)
{
fprintf(stderr, "accept error:%s\n\a", strerror(errno));
close(sock_fd);
exit(1);
}
client_num++;
fprintf(stderr, "Server get connetion form client%d: %s\n", client_num, inet_ntoa(client_addr.sin_addr));
if (!fork())
{
/* Child process */
while (1)
{
/* recv */
recv_len = recv(new_fd, recv_buf, 999, 0);
if (recv_len <= 0)
{
fprintf(stderr, "recv error:%s\n\a", strerror(errno));
close(new_fd);
exit(1);
}
else
{
memcpy(read_head, recv_buf, sizeof(HEAD_T));
memcpy(read_user, recv_buf + sizeof(HEAD_T), sizeof(USER_T));
printf("nbytes is %d\n", recv_len);
printf("fun is %d\n", read_head->fun);
printf("UserName is %s \nPassWord is %s\n", read_user->UserName, read_user->PassWord);
memset(read_head, 0, sizeof(read_head));
}
}
close(new_fd);
}
}
/* close */
close(sock_fd);
exit(0);
}
运行效果如下
下一篇:
C++高并发服务器设计(四)