问几个初级的概念性问题,关于tcp socket的?

林石公 2011-06-10 03:52:38
是用delphi的,一直用的都是那些封装好的控件,所以对一些基础性的问题非常不清晰,概念模糊,所以来请教大家
起初是想实现这样的功能:客户端连接到服务器上,然后由客户端发送操作指令,服务器端根据指令执行,然后返回结果,很简单的一个过程(我在业务层面保证服务器和客户之间是一呼一应的).期间我有以下几个问题:

1 指令和结果都有可能是比较大的数据包,比如数十K的数据,将这样的数据包send时,是需要我们自己来手动分包发送,还是直接交给系统(也就是直接send一个20K的数据包)?
如果手动分包,是否要管理好各包之间的关系,比如总共几个包,总共的数据大小,当前第几个,当前包长度,以方便另一端的接收
如果直接交由系统处理,在实际发送过程中,是不是根据相关协议,自动在后台分包发送,我们调用者不用考虑;还是系统有能力直接发送一个大的数据包

2 对应于发送,在接收时:
对应于手动分包,根据在发送端的分包情况逆向逐步接收,再将数据包还原?是这样么
对应于一个大的send,在接收端直接用一个recv把数据完整的接收下来

一直没怎么接触这方面,所以问题比较幼稚,请有空的兄弟帮忙解一下惑,谢谢
...全文
144 15 打赏 收藏 转发到动态 举报
写回复
用AI写文章
15 条回复
切换为时间正序
请发表友善的回复…
发表回复
林石公 2011-06-13
  • 打赏
  • 举报
回复
多谢诸位,回答的这么详细
无水先生 2011-06-12
  • 打赏
  • 举报
回复
给你个UDP例子,VC下直接调通,此为广播通信,IP地址随意


#include<stdlib.h>
#include<iostream.h>
#include<conio.h>
#include<stdio.h>
#include<winsock2.h>
#include<windows.h>
#pragma comment(lib,"ws2_32.lib")
/*

HANDLE hr,hl,hbusy;

struct thrpar
{
SOCKET s;
};

struct recpar
{
SOCKET srec;
struct sockaddr_in addr;
};


UINT recthread(LPVOID pp)
{
SOCKET srec,s2;
struct recpar *prec=(struct recpar *)pp;
sockaddr_in recaddr;

srec=prec->srec;
recaddr=prec->addr;
char buf[256];
DWORD ret;

printf("recv thread start!");
ret=recv(srec,buf,256,0);
if(ret==0)
{
printf("connect close!");
closesocket(srec);
return 0;
}
else if(ret==SOCKET_ERROR)
{
printf("connect error!");
closesocket(srec);
return 0;
}
printf("recv data:%s",buf);
return 0;
}


DWORD WINAPI listhread(LPVOID pp)
{
::MessageBox(NULL,"enter the listhread","KK",MB_OK);
struct thrpar *pthr=(struct thrpar*)pp;
SOCKET sl,saccept;
sockaddr_in here, there;
char name[256];
struct hostent *phost;
int ret;

gethostname(name,256);
phost=gethostbyname(name);

here.sin_family=AF_INET;
here.sin_port=htons(1024);
here.sin_addr.S_un.S_un_b.s_b1=phost->h_addr_list[0][0];
here.sin_addr.S_un.S_un_b.s_b2=phost->h_addr_list[0][1];
here.sin_addr.S_un.S_un_b.s_b3=phost->h_addr_list[0][2];
here.sin_addr.S_un.S_un_b.s_b4=phost->h_addr_list[0][3];

sl=pthr->s;

ret=bind(sl,(struct sockaddr *)&here,sizeof(here));
if(ret!=0)
{
printf("bind error");
return 0;
}
ret=listen(sl,5);
if(ret!=0)
{
printf("listen error");
return 0;
}
while(true)
{
int nlen=sizeof(there);
saccept=accept(sl,(struct sockaddr *)&there,&nlen);
if(saccept==INVALID_SOCKET)
{
printf("accept socket invalid");
}
DWORD wret=WaitForSingleObject(hbusy,500);
if(wret==WAIT_TIMEOUT)
{
send(saccept,"\0",1,0);
closesocket(saccept);
continue ;
}
DWORD recid;
struct recpar *prec;
prec->addr=there;
prec->srec=saccept;

hr=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)&listhread,prec,0,&recid);
return 1;
}
return 1;
}

void main(void)
{
SOCKET s;
sockaddr_in addrin;
WSADATA wsdata;
BOOL bsocket;
char *smsg="this is a test";

WSAStartup(0x0202,&wsdata);

s=WSASocket(AF_INET,SOCK_STREAM,0,NULL,0,WSA_FLAG_OVERLAPPED);
hbusy=CreateSemaphore(NULL,0,2,NULL);
struct thrpar *psc;
psc=new struct thrpar;
DWORD lisid;
psc->s=s;

hr=CreateThread(NULL,0,listhread,psc,0,&lisid);
DWORD ret=WaitForSingleObject(hr,10000);
if(ret==WAIT_OBJECT_0)printf("thread time out%d\r\n",lisid);

}*/
void main(void)
{
SOCKET s;
sockaddr_in from,a;
WSADATA wsdata;
BOOL optval;
//启动SOCKET库,版本为2.0
WSAStartup(0x0202,&wsdata);
optval=TRUE;
//然后赋值给两个地址,一个用来绑定套接字,一个用来从网络上的广播地址接收消息;
a.sin_family=AF_INET;
a.sin_addr.s_addr=0;
a.sin_port=htons(5050);

from.sin_family=AF_INET;
from.sin_addr.s_addr=INADDR_BROADCAST;
from.sin_port=htons(5050);

int fromlength=sizeof(SOCKADDR);
//用UDP初始化套接字
s=socket(AF_INET,SOCK_DGRAM,0);
//设置该套接字为广播类型,
setsockopt(s,SOL_SOCKET,SO_BROADCAST,(char FAR *)&optval,sizeof(optval));
bind(s,(sockaddr *)&a,sizeof(sockaddr_in));
char buf[256];
while(1)
{//从广播地址接收消息,注意用来绑定的地址和接收消息的地址是不一样的
recvfrom(s,buf,256,0,(struct sockaddr FAR *)&from,(int FAR *)&fromlength);
Sleep(1000);
printf("%s\n",buf);
ZeroMemory(buf,256);
}
}
wllxe 2011-06-12
  • 打赏
  • 举报
回复
一楼的答案简洁明了!很实用哦~~
jsyren 2011-06-12
  • 打赏
  • 举报
回复
只管发送和接受,至于发送多少次,不用考虑了。
不过发送时候,可以包头,包体分成两次发送,这样条例清晰,另外包头要设置包含包体的大小的量。
接收的时候,也可以采用先接收包头,接收成功,则依据包头中提示的包体的大小,进行包体的接收。


怎么样,我的解释够详细吧!呵呵!
愤怒的泡面 2011-06-11
  • 打赏
  • 举报
回复
太大的包要循环接收。可以用setsockopt设置每次接收的字节数。
wocow3 2011-06-10
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 sonicer 的回复:]

那么,这种思路是否对头:
发送的目的数据:[实际数据]

发送端:
首先封装成:[包长数据]+[实际数据] 总长=实际数据长+包头长度描述占用的位=N
然后循环send,直到send的返回值之和大于等于N,表示发送完成

接收端:
首先接收[包长数据],得到后续包的大小
循环recv,直到recv的返回值之和大于等于N,并将recv的结果拼在一起,还原 [实际数据],……
[/Quote]
自己加包头指定长度是最常用的一种的设计。
“循环send,直到send的返回值之和大于等于N,表示发送完成”一般也采取这种策略,但其实这个一个比较草率的处理。如果你用的阻塞socket,在网络环境比较差的情况下这个循环的send可能会阻塞很长时间(发送缓冲区耗尽),而默认的TCP因为超时而放弃连接时间也是挺长的,所以一个健壮的网络程序你还得自己设计这种异常的处理机制
林石公 2011-06-10
  • 打赏
  • 举报
回复
那么,这种思路是否对头:
发送的目的数据:[实际数据]

发送端:
首先封装成:[包长数据]+[实际数据] 总长=实际数据长+包头长度描述占用的位=N
然后循环send,直到send的返回值之和大于等于N,表示发送完成

接收端:
首先接收[包长数据],得到后续包的大小
循环recv,直到recv的返回值之和大于等于N,并将recv的结果拼在一起,还原 [实际数据],完成一次交互

oyljerry 2011-06-10
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 sonicer 的回复:]
引用 2 楼 lijianli9 的回复:

lz使用阻塞性的socket调用send要检查返回值,看是否发送完整,
还要lz要进行必要的封包和解包,也就是一个包头,里面包含有数据长度,和命令字(要执行的操作)后续就发送数据,recv方先接受包头,然后根据包头进行解析数据长度,然后阻塞接受这么长长度的数据,然后提交给业务层进行必要的处理。


比如我发一个20K的数据,send返回告诉……
[/Quote]
接收端也要处理,接收到了部分就处理。直到全部接收完成
kyotrue 2011-06-10
  • 打赏
  • 举报
回复
1、系统会自动分包的,TCP协议层的东西对你是透明的,你只要处理你自己定义的应用层协议就行了。
2、TCP协议就保证了数据是有序的,当然,你一次recv很可能收到的不是完整的包,这个就需要你自己定义应用层协议,在数据头部加上一段表示数据包长度的数据,接收端根据这个判断包的界限。
Eleven 2011-06-10
  • 打赏
  • 举报
回复
比如我发一个20K的数据,send返回告诉我只发送成了12K,然后我应当如何处理,从第12K+1的位置上继续发,还是重发?
继续发的话,是否意味着我要用一个循环的方式发送数据包?
重发的话,在接收端就不太好处理了吧,因为那边不知道从哪个地方开始是重发的吧?
--------------------------------
继续从12K+1的地方发送后续的数据
林石公 2011-06-10
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 lijianli9 的回复:]

lz使用阻塞性的socket调用send要检查返回值,看是否发送完整,
还要lz要进行必要的封包和解包,也就是一个包头,里面包含有数据长度,和命令字(要执行的操作)后续就发送数据,recv方先接受包头,然后根据包头进行解析数据长度,然后阻塞接受这么长长度的数据,然后提交给业务层进行必要的处理。
[/Quote]

比如我发一个20K的数据,send返回告诉我只发送成了12K,然后我应当如何处理,从第12K+1的位置上继续发,还是重发?
继续发的话,是否意味着我要用一个循环的方式发送数据包?
重发的话,在接收端就不太好处理了吧,因为那边不知道从哪个地方开始是重发的吧?

谢谢你的指点
林石公 2011-06-10
  • 打赏
  • 举报
回复
另外,经常看到所谓的缓冲区的大小,比如4096,2048啥的,是起什么作用的,这也是我对是否分包产生疑问的原因:是否一次只能发相应的bufsize的数据呢?
林石公 2011-06-10
  • 打赏
  • 举报
回复
那么会否出现这样的情况,在发送端分了20个包,已经发送了11个包,而接收端就只能收到前11个包了?如果这样的话,如何保证数据完整性?

还是虽然分了20个包,但系统会保证你在接收端recv时20个包的数据是全部已经收到了,否则即使发送端先发了11个包,在后9个包没发完的情况下,接收端是recv不到东西的??

感谢你的回答!
lijianli9 2011-06-10
  • 打赏
  • 举报
回复
lz使用阻塞性的socket调用send要检查返回值,看是否发送完整,
还要lz要进行必要的封包和解包,也就是一个包头,里面包含有数据长度,和命令字(要执行的操作)后续就发送数据,recv方先接受包头,然后根据包头进行解析数据长度,然后阻塞接受这么长长度的数据,然后提交给业务层进行必要的处理。
oyljerry 2011-06-10
  • 打赏
  • 举报
回复
系统会自动帮你分包,你只用发送和接收,然后接收的时候会自动填充到缓冲区,你再处理对应的数据

18,356

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 网络编程
c++c语言开发语言 技术论坛(原bbs)
社区管理员
  • 网络编程
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧