socket编程实验

在《计算机通信的奥秘,带你彻底了解socket网络编程(上)》一文中,我们提到了客户端发送完数据后,服务器和客户端都关闭了,也就是说数据只能发送一次,这显然不符合实际使用。那么如何改变程序呢?

socket编程实验

1、持续发送

如果你想实现持续传输,你可能想到过用循环,想法完全正确,但是你循环几次呢?服务器的实际使用会一直运行,除非系统崩溃,客户端和服务器的长连接会一直连接,除非客户端自己关闭连接。所以我们的想法是两端都是无限循环!所以你可以用while(1)来循环,所以从哪里开始循环取决于你的具体需求。如果只需要三次握手来发送数据一次,将会连续发送。那么服务器和客户端的代码如下:

1.1、服务端代码

# include & ltstdio.h & gt# include & ltsys/socket . h & gt;# include & ltnetinet/in . h & gt;# include & ltstdlib.h & gt# include & ltarpa/inet . h & gt;# include & ltunistd.h & gt# include & ltstring.h & gt# define BUF _ SIZE 512 # define ERR _ EXIT(m)\ do \ { \ perror(m);\ EXIT(EXIT _ FAILURE);\ } while(0)int main(){//Create socket int m _ sockfd = socket(af _ inet,sock _ stream,0);if(m _ sockfd & lt;0){ ERR _ EXIT(& # 34;创建套接字失败& # 34;);}//初始化套接字元素struct sockaddr _ in server _ addrint server _ len = sizeof(server _ addr);memset(&server_addr,0,server _ len);server _ addr . sin _ family = AF _ INET;//server _ addr . sin _ addr . s _ addr = inet _ addr(& # 34;0.0.0.0");//也可以这样写server _ addr . sin _ addr . s _ addr = in addr _ any;server _ addr . sin _ port = htons(39002);//用服务器的ip和端口号int m _ bindfd = bind (m _ sockfd,(structsockaddr *) & server _ addr,server _ len)绑定文件描述符;if(m _ bindfd & lt;0){ ERR _ EXIT(& # 34;绑定ip和端口失败& # 34;);}//进入监听状态,等待用户发起请求int m _ listen = listen (m _ sockfd,20);if(m _ listen FD & lt;0){ ERR _ EXIT(& # 34;监听客户端失败& # 34;);}//定义客户端的套接字。这里,返回一个新的套接字。以后通信的时候,会用到这个m_connfd进行通信。struct sockaddr _ in client _ addrsocklen _ t client _ len = sizeof(client _ addr);int m_connfd = accept(m_sockfd,(struct sockaddr *)&client_addr,& client _ len);printf(& # 34;客户端接受成功\ n & # 34);//接收客户端数据和对应的char缓冲区[BUF _ SIZE];while (1) { memset(buffer,0,sizeof(buffer));//复位缓冲区recv (m _ connfd,buffer,sizeof (buffer)-1,0);printf(& # 34;服务器接收:% s \ n & # 34,缓冲);strcat(缓冲区,& # 34;+ACK & # 34;);send(m_connfd,buffer,sizeof(buffer)-1,0);}//关闭套接字Close(m _ conn FD);close(m _ sockfd);printf(& # 34;服务器套接字关闭!!!\ n & # 34);返回0;}1.2,客户端代码# include

2.服务器一直接收空包,那么上面的代码有问题吗?如果你亲自在linux主机上运行过,你可能会发现,如果你用Ctrl+C掐掉客户端或者杀死客户端进程,服务器会一直打印接收到的数据,但是数据是一个空字符串。GDB调试发现实际收到的0长空包(有时连续收到多个空包后服务器会断开连接)。换句话说

当客户端断开连接时,服务器一直接收0字节。

这很奇怪。客户端已断开连接。为什么服务器仍然接收到0字节的数据?这个问题我找了很久也没有找到一个合理的答案。网上解释不是很清楚。知道的同学可以在评论区帮忙解释一下。以下解释来源于网络。

首先,用户正常退出。这时候循环调用Receive方法就会出现,陷入死循环。
第二,用户异常退出时服务器会捕捉到异常,包括拔掉网线、死机等。

问题原因暂时找不到,但有解决方法。当我们从客户端接收数据时,我们可以判断数据包的大小。如果为0,可能意味着客户端关闭。那么这个时候服务器需要做的就是判断接收到的数据包的大小。如果为0,将连接到close()客户端,这样就可以正常收发数据。当客户端关闭时,服务器不会有任何问题。修改后的while部分的代码如下

while(1){ if(m _ conn FD & lt;0) { m_connfd = accept(m_sockfd,(struct sockaddr *)&client_addr,& client _ len);printf(& # 34;客户再次接受成功!!!\ n & # 34);} memset(缓冲区,0,sizeof(缓冲区));//Reset buffer int recv len = recv(m _ conn FD,buffer,size of (buffer)-1,0);if(recv len & lt;= 0){ close(m _ conn FD);m _ conn FD =-1;printf(& # 34;客户端失去连接!!!\ n & # 34);继续;} printf(& # 34;服务器接收:% s \ n & # 34,缓冲);strcat(缓冲区,& # 34;+ACK & # 34;);send(m_connfd,buffer,sizeof(buffer) – 1,0);}3、思考问题上面的代码确实可以实现客户端继续发送数据,客户端断开再连接,服务器保持正常的过程。但是如果你认为socket网络编程就这么简单,那你就真的太天真了。上面的客户端和服务器只是一一对应,也就是说服务器是进程,客户端是进程。但是在实际应用的过程中怎么会这么简单呢?可能只有一个服务器,但有多个客户端。

这时候请做一个实验,启动一个客户端进程。你会发现服务器没有反应。如果客户端发送数据,服务器也不响应!那么我们该怎么办呢?聪明的你能想出解决的办法吗?欲知如何,且听下回分解!

更多精彩内容请关注同名公众号:alittle note。

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。

发表回复

登录后才能评论