Linux C语言 C/S程序,客户端发送的数据和服务器端接收到的数据不一样,求解

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux C语言 C/S程序,客户端发送的数据和服务器端接收到的数据不一样,求解相关的知识,希望对你有一定的参考价值。

TCP长连接。
客户端开了40byte的buffer:char sendbuff[40];

循环交替发送33和35byte的数据:sendnum = send(socketfd, sendbuff, sendsize, 0);
sendsize交替为33和35.
服务器端同样40byte的buffer:char recvbuff[40];
一直循环接收数据:recvnum = recv(socketfd, recvbuff, sizeof(recvbuff), 0);
但是接收到的字节数并不是期望的交替33和35,而是乱的,从而导致数据全部乱掉。
log文件中可以看到后一条数据的前几个字节跑到前一条数据的尾部了。
仔细研究了recv()和send()函数,发现send()没有问题,问题出在recv()上。
是不是因为发的太快,收的太慢,没有同步?
socket通信是如何控制发送和接收缓存的大小的?
有没有办法在不返信的前提下解决这一问题?
急求大神解答,分太少了,全拿了。

估计你是用的tcp socket,导致“videlord”网友说的情况:对于tcp socket,send与recv不是对等的,recv时只要缓冲有数据就会收上来。简单说就是你send 4次,比如分别为10 10 10 10字节,对端可以一次recv到这40字节数据,也可以recv 40次、每次1字节。


解决办法有两种:

    改用udp socket,send/recv自然对等了

    继续用tcp socket,自己进行数据分段:比如自行约定在数据前约定4个字节用于描述数据长度,这样发送时,send 4+33字节,send 4+35字节;接收时,先获取描述长度的4字节获得长度,再按照长度接收数据(可能需要多次recv凑齐指定长度)。

参考技术A message based socket or stream based socket?
recv的返回值多少?追问

本人小白,不懂message based socket ,就是发送字节流,感觉应该是stream based socket。recv的返回值是乱的 40,9,11,13,……

追答

查看你socket的定义

如果stream based, message boundary是被忽略的,只要数据有了,就会返回给用户;所以你的recv会接受所有available data.

C语言 Linux网络编程(C/S架构) 在线词典

项目介绍

描述:
通过C/S架构实现在线词典,用户在客户端可以注册,登陆,然后可以查询单词,并且保存自己的单词查询记录。

知识点:

  • c语言进阶 Linux基础
  • C/S架构
  • 进程
  • sqlite3数据库
  • 时间函数
  • Makefile

效果图:


客户端

创建一个dict_client文件夹,存放客户端代码

client.h

#ifndef CLIENT_H
#define CLIENT_H
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

#define N 32

#define R 1 //user - register
#define L 2 //user - login
#define Q 3 //user - query
#define H 4 //user - history

//定义通信双方的信息结构体
typedef struct
    int type;
    char name[N];
    char data[256]; //password或word信息
MSG;

int do_register(int sockfd, MSG *msg);
int do_login(int sockfd, MSG *msg);
int do_query(int sockfd, MSG *msg);
int do_history(int sockfd, MSG *msg);
int client_connect(char *ip, int port);
#endif

cli_dict.c

#include "client.h"

int do_register(int sockfd, MSG *msg)
	msg->type = R;
    printf("Input name:");
    scanf("%s", msg->name);
    getchar();
    printf("Input password:");
    scanf("%s",msg->data);

    if(send(sockfd,msg,sizeof(MSG),0)<0)
    
        printf("fail to send.\\n");
        return -1;
    

    if(recv(sockfd,msg,sizeof(MSG),0)<0)
    
        printf("Fail to recv.\\n");
        return -1;
    
    //ok or user alread exist
    printf("%s\\n", msg->data);
    return 0;

int do_login(int sockfd, MSG *msg)
	msg->type = L;
    printf("Input name:");
    scanf("%s", msg->name);
    getchar();
    printf("Input password:");
    scanf("%s",msg->data);
    if(send(sockfd,msg,sizeof(MSG),0) < 0)
    
        printf("fail to send.\\n");
        return -1;
    
    if(recv(sockfd,msg,sizeof(MSG),0)<0)
    
        printf("fail to recv.\\n");
        return -1;
    
    if(strncmp(msg->data,"OK",3)==0)
    
        printf("OK\\n");
        return 1;
        
    else
        printf("%s\\n", msg->data);
    
    return 0;

int do_query(int sockfd, MSG *msg)
	puts("查询--------------");
    char name[10]=0;
    strcpy(name,msg->name);
    while(1)
        msg->type = Q;
        strcpy(msg->name,name);
        printf("Input word[quit:#]:");
        scanf("%s", msg->data);
        //printf("%s %s\\n",msg->name,msg->data);
        //客户端输入# 返回上一级菜单
        if(strncmp(msg->data,"#",1)==0)
            break;
        //将要查询的单词发送给服务器
        if(send(sockfd, msg, sizeof(MSG), 0)<0)
        
            printf("Fail to send.\\n");
            return -2;
        
        //等待接收服务器传递回来的单词注释信息
        if(recv(sockfd, msg, sizeof(MSG), 0) <  0)
        
            printf("Fail to recv.\\n");
            return -2;
        
        printf("%s\\n", msg->data);
    
    return 0;

int do_history(int sockfd, MSG *msg)
	msg->type = H;
    send(sockfd,msg,sizeof(MSG),0);
    printf("%s的查找记录--------\\n", msg->name);
    //接收服务器传递回来的历史记录信息
    while(1)
    
        recv(sockfd,msg,sizeof(MSG),0);
        if('\\0'==msg->data[0])
            break;
        //打印历史记录信息
        printf("%s\\n",msg->data);
    
    return 0;

int client_connect(char *ip, int port)
	int sockfd;
    struct sockaddr_in serveraddr;
    //创建流式套接字
    if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        perror("fail to socket.\\n");
        return -1;
    
    //给服务器地址初始化
    bzero(&serveraddr, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = inet_addr(ip);
    serveraddr.sin_port = htons(port);
    //连接服务器
    if(connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0)
        perror("fail to connect");
        return -1;
    
    return sockfd;

main.c

#include "client.h"
int main(int argc, char *argv[])
 
    int n,m;
    char dart1[32],dart2[32];
    MSG msg;
    if(argc != 3)
        printf("Usage:%s serverip port\\n", argv[0]);
        return -1;
    
    int sockfd = client_connect(argv[1], atoi(argv[2]));
    //一级菜单
    while(1)
    	
        printf("##################################################\\n");
        printf("*1.register         2.login               3.quit*\\n");
        printf("##################################################\\n");
        printf("Please choose:");
        scanf("%d", &n);
        getchar();
        switch(n)
            case 1:
                do_register(sockfd, &msg);
                break;
            case 2:
                if(do_login(sockfd, &msg) == 1)
                    goto next;
                
                break;
            case 3:
                close(sockfd);
                exit(0);
                break;
            default:
                printf("Invalid data cmd.\\n");
                n = 0;
                break;	//若没有break,输入aaa会执行3次switch
        
    
//二级菜单
next:  
    while(1)
    	
        printf("###########################################\\n");
        printf("*1.query        2.history           3.quit*\\n");
        printf("###########################################\\n");
        printf("Please choose:");
        
        getchar();
        scanf("%d", &m);
        switch(m)
            case 1:
                do_query(sockfd, &msg);
                break;
            case 2:
                do_history(sockfd, &msg);
                break;
            case 3:
                close(sockfd);
                exit(0);
                break;
            default:
                printf("Invalid data cmd.\\n");
                break;//若没有break,输入aaa会执行3次switch
        
    
    return 0;
 

Makefile

创建Makefile 文件,也可以不做这步,只是多敲几串命令而已,Makefile最大优点就是直接输入make命令就能编译文件,也挺方便。

cli: main.c cli_dict.c
	gcc *.c -o cli

服务器端

sqlite3

需要在linux中安装sqlite3,没有安装的输入这两条命令

sudo apt-get install sqlite3
sudo apt-get install libsqlite3-dev

然后创建数据库 sqlite3 dictionaryOL.db;
在sqlite3 shell中创建表

# 改变user表结构
CREATE TABLE user(id INTEGER PRIMARY KEY,name char unique,pwd char);
# 创建dict 存放字典内容(单词,含义)
CREATE TABLE dict(dict_id int primary key,word char,mean char);
# 统计字典里有多个意思的单词
select word from dict group by word having count(*)>=2;
# 创建record 记录用户所查询过的单词(姓名,时间,单词)
CREATE TABLE record(name char,date char,word char);

dict表需要自己插入单词和含义。

server.h

#ifndef SERVER_H
#define SERVER_H

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sqlite3.h>
#include <signal.h>
//时间函数
#include <time.h>
#include <unistd.h>
#define N 32

#define R 1 //user - register
#define L 2 //user - login
#define Q 3 //user - query
#define H 4 //user - history

//数据库
#define DATABASE "dictionaryOL.db"

//定义通信双方的信息结构体
typedef struct
    int type;
    char name[N];
    char data[256]; //password或word信息
MSG;
int do_client(int acceptfd,sqlite3 *db);
int do_register(int acceptfd, MSG *msg,sqlite3 *db);
int do_login(int acceptfd, MSG *msg,sqlite3 *db);
int do_query(int acceptfd, MSG *msg,sqlite3 *db);
int do_history(int acceptfd, MSG *msg,sqlite3 *db);
int search_callback(void *para,int f_num,char **f_value,char **f_name);
int history_callback(void *para,int f_num,char **f_value,char **f_name);
void get_date(char *date);
int do_client(int acceptfd,sqlite3 *db);
int server_init(char *ip, int port);

#endif

ser_dict.c

#include "server.h"

int do_register(int acceptfd, MSG *msg,sqlite3 *db)
	char *errmsg;
    char sql[512];
    sprintf(sql,"insert into user values(null,\\"%s\\",\\"%s\\");",msg->name,msg->data);
    printf("%s\\n", sql);
    if(sqlite3_exec(db,sql,NULL,NULL,&errmsg)!=SQLITE_OK)
    
        printf("%s\\n", errmsg);
        strcpy(msg->data, "usr name already exist.");
    
    else
        printf("client register ok!\\n");
        strcpy(msg->data,"OK!");
    
    if(send(acceptfd,msg,sizeof(MSG),0)<0)
    
        perror("fail to send");
        return -1;
    
    return 0;

int do_login(int acceptfd, MSG *msg,sqlite3 *db)
	char sql[512] = 0;
    char *errmsg;
    int nrow;
    int ncloumn;
    char **resultp;
    sprintf(sql,
    "select * from user where name='%s' and pwd='%s';",
    msg->name,msg->data);
    printf("%s\\n", sql);
    if(sqlite3_get_table(db,sql,&resultp,
                &nrow,&ncloumn,&errmsg))
    
        printf("%s\\n", errmsg);
        return -1;
    
    if(nrow == 1)
    
        //查询成功,有此用户
        strcpy(msg->data,"OK");
        send(acceptfd,msg,sizeof(MSG),0);
        return 1;
    
    else
    
        //密码或者用户名错误
        strcpy(msg->data,"user/password/worng");
        send(acceptfd,msg,sizeof(MSG),0);
        return 0;
    
    return 1;

int do_query(int acceptfd, MSG *msg,sqlite3 *db)
	printf以上是关于Linux C语言 C/S程序,客户端发送的数据和服务器端接收到的数据不一样,求解的主要内容,如果未能解决你的问题,请参考以下文章

求C++编写的自动升级程序C/S结构

C#winform开发的C/S结构的程序,怎样实现自动升级的功能!望高手指教!

C/C++语言编译生产可执行的二进制文件的过程??求大神详尽解释,

C/S架构 C#winform自动升级问题

C语言写的UDP客户端收不到包

C语言 Linux网络编程(C/S架构) 在线词典