一、客戶端升級(二)
發送結構化網路訊息
這里是指使用結構體傳輸資料,
需要考慮位元組序對齊,如果客戶端和服務器編程語言相同,且在同一環境下,通常位元組是相同的,但是可能存在位元組長度不一致而導致訊息傳輸與接收不一致的額情況發生,
新增內容
新增包頭:描述本次傳輸資料的大小和作用,
服務器和客戶端新增對登陸登出的狀態描述
網路報文定義
報文包括兩個部分,是網路傳輸的基本訊息單元
包頭:描述本次資料包的大小和作用
包體:傳輸的資料
埠復用
設定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);
}
運行效果如下

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/332137.html
標籤:其他
