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凑齐指定长度)。
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#winform开发的C/S结构的程序,怎样实现自动升级的功能!望高手指教!