Chinaunix

标题: 关于socket上的输入输出 [打印本页]

作者: hsum    时间: 2010-06-02 16:05
标题: 关于socket上的输入输出
本帖最后由 hsum 于 2010-06-02 21:22 编辑

更新2:
问题原因查明了,服务端其实是正确的,每次write()后都传送了消息给客户端,问题在于服务端write()的间隔时间很短,客户端来不及从接收缓冲区读取这一次的信息,服务端就又发送了信息过来,这样客户端一次读取的就很可能是服务端多次发送来的信息了,只要在客户端调用sleep(),延时下,就很容易证明这个原因。。。。

现在想想看怎么解决这个问题呢



更新:
现在的问题是这样,我的需求是:客户端发送一个字符串给服务端,write(sockfd,string,strlen(string)),服务端接收到字符串后作出一些处理,这个处理过程中有一些输出,从一行至多行不等,需要返回给客户端,在客户端上显示.

我的客户端发送和接受是这么写的:

  1. char send[MAX_COMMAND];
  2. char receive[MAX_COMMAND];

  3. while(1){
  4.     bzero(send,MAX_COMMAND);
  5.     bzero(receive,MAX_COMMAND);
  6.    
  7.     printf("$:");
  8.     fgets(send,MAX_COMMAND,stdin);    // 获得输入的字符串
  9.     send[strlen(send)]='\n';
  10.     write(sockfd,send,strlen(send));     //发送给服务端

  11.     while(1){
  12.         bzero(receive,1024);
  13.         if((n=read(sockfd,receive,MAX_COMMAND))<=0){   //如果读取的字符数小于0,判断是否是因为缓冲区问题
  14.              if(errno==EINTR)
  15.                  continue;
  16.              else
  17.                  break;

  18.         }
  19.         if(!strcmp(receive,"\n\n\n"))    //如果服务端发送过来三个回车,说明本次发送完毕,跳出循环
  20.             break;
  21.         printf("%s",receive);    //打印本次返回的结果
  22.         }
  23. }
复制代码
服务端的代码:

  1. .......接受到客户端的指令,并调用相应函数,下面是相应函数中的内容
  2.         char newline[1024];
  3.         bzero(newline,1024);
  4.        
  5.         sprintf(newline,"aaaaaaaaa \n");
  6.         write(sockfd,newline,strlen(newline));
  7. //        sleep(2);

  8. .....都是这种输出格式

  9.         sprintf(newline,"zzzzzzzzzz \n");
  10.         write(sockfd,newline,strlen(newline));
  11. //        sleep(2);

  12.         strcpy(newline,"\n\n\n");           //最后发送三个回车,代表本次返回完毕
  13.         write(sockfd,newline,strlen(newline));
复制代码
问题就在这,并不是每次服务端碰到write(),客户端都输出相应内容,而是客户端一次接收到多个服务端的write内容(我在客户端通过read返回的字节数判断的),所以服务端最后代表结束的三个回车一同添加在前面的输出结果后面了,致使客户端不能接收到单独的三个回车,但奇怪的是,我在服务端每次调用完write()后,调用sleep(),这样就可以像我期望的那样了,服务端调用一次write(),便发送给客户断,客户端便接收到信息(通过客户端read返回的字节数),客户端也能正确识别最后三个回车了,这是为什么呢?

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------






最近的项目中用到客户端服务端,linux环境下c,第一次接触socket编程,一边在看<unix网络编程>,一边在写代码

客户端发送一条字符串给客户端(已经实现)
服务端接收到字符串后经过处理,产生结果,几行至几十行不等,需要回送给客户端显示,


我的方法是在服务端记录接收到的套接字socketfd,然后传入所有有输入的函数,这些函数原来是直接printf()输入,现在改为:
sprintf(string,""");    //先将输出存在一个字符串数组string中
write(socketfd,string,strlen(string));    //再将string输出到这个套接字中


问题:
有的输出很短,一行不到,几十个字符,客户端能够正常接收到并显示。
可是有的输入很长,10多行,几百个字符串,客户端不能正常显示,不如客户端输入命令a后,只显示了应有结果的一行,再随便输入一个命令或者回车,其余的十几行就显示出来了。


很奇怪,感觉是缓冲区的问题,但不晓得怎么搞,请指教!谢谢!
作者: 没本    时间: 2010-06-02 16:19
用现成的libevent库吧,要不然新手+生手,调试花的时间比写程序的时间长N倍吧。
照里面的例子抄一个改改就行了。
作者: hsum    时间: 2010-06-02 16:52
谢谢楼上的,不过还是准备自己写,呵呵。


现在发现一个解决的方法,我原来有问题的输出函数里面是这样的:
.....
sprintf(string,"");
write(socketfd,string,strlen(string));

sprintf(string,"");
write(socketfd,string,strlen(string));

sprintf(string,"");
write(socketfd,string,strlen(string));
.....

也就是说每次一有输出,我就把它写到套接口里面去,这样客户端接受到输入有问题,

现在我把上面的代码改成:
....
char *p;

sprintf(p,"");
p+=(int)strlen(p);

sprintf(p,"");
p+=(int)strlen(p);

sprintf(p,"");
p+=(int)strlen(p);
....
write(socketfd,string,strlen(string));

也就是不是碰到输入就写入套接口,而是全部先放到一个很大的字符串数组中,完了之后再一次写入套接口,这回客户端正常显示了。



有点不解,请高手指教
作者: c/unix    时间: 2010-06-02 17:26
提示: 作者被禁止或删除 内容自动屏蔽
作者: hsum    时间: 2010-06-02 17:37
ls能说的稍微详细点吗

第一次接触socket编程,很多不了解,谢谢
作者: 光速    时间: 2010-06-02 17:40
应该是客户端缓冲区大小的问题
作者: 没本    时间: 2010-06-02 18:33
你得定一个通信协议,比如每个string以\n为结束标记。recv拿到完整的string才处理,否则继续recv并和原来不完整的做下拼接。
作者: okocha-jay    时间: 2010-06-02 19:09
加个包头吧,指示你的数据包长度。比如你发"abc"三个字母,那么就要发5个字节数据,
前2个字节是一个unsigned short,值是3;
作者: hsum    时间: 2010-06-02 19:50
奇怪了,

我在服务端的函数里面让每个write()函数后面sleep(2),客户端就可以正常接受了,怎么会
作者: 没本    时间: 2010-06-02 20:44
TCP_NODELAY and TCP_CORK basically control packet “Nagling,” or automatic concatenation of small packets into bigger frames performed by a Nagle algorithm. John Nagle, after whom this process was named, first implemented this as a way to fight Ford’s network congestion in 1984. (See IETF RFC 896 for more details.) The problem he solved was the so-called silly window syndrome, where congestion occurred simply because widespread terminal applications sent keystrokes one per packet, typically one byte of payload and 40 bytes of header, thus causing 4,000 percent overhead. Nagling became standard and was aggressively implemented over the Internet. It is now considered a default, but as we'll see, there are situations when turning it off is desirable.

参考网址:http://en.wikipedia.org/wiki/Nagle's_algorithm

多个send默认会合并了,你加上这个就不用sleep了,不过如果过了多个网络,IP包可能被分断的,所以稳定的internet程序肯定要接收时重新组包。

  1. int nodly = 1;
  2. setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char*)&nodly, sizeof(int));
复制代码

作者: hsum    时间: 2010-06-02 21:04
回复 10# 没本


  恩,谢谢,我突然想到一个问题,服务端是可以每次write()后马上发送给客户端了,但是客户端很可能来不及每次一个包的取,也就是说,第n个包还没取,第n+1个包就来了,这样在缓冲区里n和n+1个包又在一起了,这样的话怎么办呢?
作者: t3gonline    时间: 2010-06-02 21:32
没明白
作者: 没本    时间: 2010-06-02 21:37
见7、8楼。网络传输需要制定传输协议,广为人知的有HTTP FTP DNS等等,你需要制定你自己的传输协议。
作者: 16887126    时间: 2010-06-02 22:27
或者用标志字符区分每次的发送内容,或者用固定长度区分
作者: ydfgic    时间: 2010-06-03 14:57
楼主,看样子是个新手
你没有搞清楚,通讯是需要协议的。
你每次read的时候,并不一定把所有的内容都接受完了,而这时候你又重新接收。
你能得到你期望的“\n\n\n”吗?
作者: hsum    时间: 2010-06-03 15:49
还是没能很好的解决,碰到几千字节的输出有问题,埃,求教
作者: benjiam    时间: 2010-06-03 17:36
要看贴。 不看贴 良药也无用
作者: 没本    时间: 2010-06-03 17:44
是啊,把我们的回帖全部无视了,这又不是水版,回帖又没什么废话,楼主你每帖看过但没经过大脑吧。
作者: 单眼皮大姐    时间: 2010-06-04 00:33
TCP 嘛~跟打电话似的~每说一句话你就得听一句啦 ,同意4楼的说法~服务端循环发,客户端循环接收
作者: yulihua49    时间: 2010-06-07 09:41
更新2:
问题原因查明了,服务端其实是正确的,每次write()后都传送了消息给客户端,问题在于服务端write() ...
hsum 发表于 2010-06-02 16:05



    1.你的write处理有问题,不能用strlen,要用绝对数字,因为你的串没有尾0。
   2.采用包头+包体方式,定长的包头,内含一个包体长度。先收包头,根据长度收包体。




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2