09Socket编程
tcp是基于字节流的,udp是基于报文即数据包的,所以tcp会产生一个叫做粘包的问题,而udp不会产生。
我们这节主要讨论粘包问题:
先看一下粘包问题的原因:
总结如下:
1、应用进程的缓冲区和Socket缓冲区的大小不一定相吻合。
2、tcp传输段有mss限制。
3、链路层有个mtu限制。
粘包的解决方案:
1、设置定长包,定长接受。
2、在包尾加上\r\n,例如ftp就是这样实现的。
3、包头加上包体长度。
4、更复杂的应用层协议。
下面就封装一下readn和writen来实现解决粘包问题,改进一下先前的那个回射程序:
服务器端:
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h> #include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h> #define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0) struct packet //新定义包的结构,包体长度和包体
{
int len;
char buf[1024];
}; ssize_t readn(int fd, void *buf, size_t count) //readn的封装
{
size_t nleft = count;
ssize_t nread;
char *bufp = (char*)buf; while (nleft > 0)
{
if ((nread = read(fd, bufp, nleft)) < 0)//这里小于零两种情况一种是信号打断一种就是失败
{
if (errno == EINTR) //可能是信号打断
continue;
return -1; //失败
}
else if (nread == 0)
return count - nleft; bufp += nread;
nleft -= nread;
} return count;
} ssize_t writen(int fd, const void *buf, size_t count)//writen的封装
{
size_t nleft = count;
ssize_t nwritten;
char *bufp = (char*)buf; while (nleft > 0)
{
if ((nwritten = write(fd, bufp, nleft)) < 0)
{
if (errno == EINTR)
continue;
return -1;
}
else if (nwritten == 0)
continue; bufp += nwritten;
nleft -= nwritten;
} return count;
} void do_service(int conn)
{
struct packet recvbuf;
int n;
while (1)
{
memset(&recvbuf, 0, sizeof(recvbuf));
int ret = readn(conn, &recvbuf.len, 4); //先接受包头长度
if (ret == -1)
ERR_EXIT("read");
else if (ret < 4)
{
printf("client close\n");
break;
} n = ntohl(recvbuf.len); //这里有个字节序的转换,因为是要在网络上传输
ret = readn(conn, recvbuf.buf, n); //再接受包体
if (ret == -1)
ERR_EXIT("read");
else if (ret < n)
{
printf("client close\n");
break;
} fputs(recvbuf.buf, stdout);
writen(conn, &recvbuf, 4+n); //回写要包体长度加包体
}
} int main(void)
{
int listenfd;
if ((listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
/* if ((listenfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)*/
ERR_EXIT("socket"); struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(5188);
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
/*servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");*/
/*inet_aton("127.0.0.1", &servaddr.sin_addr);*/ int on = 1;
if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
ERR_EXIT("setsockopt"); if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
ERR_EXIT("bind");
if (listen(listenfd, SOMAXCONN) < 0)
ERR_EXIT("listen"); struct sockaddr_in peeraddr;
socklen_t peerlen = sizeof(peeraddr);
int conn; pid_t pid;
while (1)
{
if ((conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen)) < 0)
ERR_EXIT("accept"); printf("ip=%s port=%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port)); pid = fork();
if (pid == -1)
ERR_EXIT("fork");
if (pid == 0)
{
close(listenfd);
do_service(conn);
exit(EXIT_SUCCESS);
}
else
close(conn);
} return 0;
}
客户端:
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h> #include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h> #define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while(0) struct packet
{
int len;
char buf[1024];
}; ssize_t readn(int fd, void *buf, size_t count)
{
size_t nleft = count;
ssize_t nread;
char *bufp = (char*)buf; while (nleft > 0)
{
if ((nread = read(fd, bufp, nleft)) < 0)
{
if (errno == EINTR)
continue;
return -1;
}
else if (nread == 0)
return count - nleft; bufp += nread;
nleft -= nread;
} return count;
} ssize_t writen(int fd, const void *buf, size_t count)
{
size_t nleft = count;
ssize_t nwritten;
char *bufp = (char*)buf; while (nleft > 0)
{
if ((nwritten = write(fd, bufp, nleft)) < 0)
{
if (errno == EINTR)
continue;
return -1;
}
else if (nwritten == 0)
continue; bufp += nwritten;
nleft -= nwritten;
} return count;
}
int main(void)
{
int sock;
if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
ERR_EXIT("socket"); struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(5188);
servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); if (connect(sock, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
ERR_EXIT("connect"); struct packet sendbuf;
struct packet recvbuf;
memset(&sendbuf, 0, sizeof(sendbuf));
memset(&recvbuf, 0, sizeof(recvbuf));
int n;
while (fgets(sendbuf.buf, sizeof(sendbuf.buf), stdin) != NULL)
{
n = strlen(sendbuf.buf);
sendbuf.len = htonl(n);
writen(sock, &sendbuf, 4+n); int ret = readn(sock, &recvbuf.len, 4);
if (ret == -1)
ERR_EXIT("read");
else if (ret < 4)
{
printf("client close\n");
break;
} n = ntohl(recvbuf.len);
ret = readn(sock, recvbuf.buf, n);
if (ret == -1)
ERR_EXIT("read");
else if (ret < n)
{
printf("client close\n");
break;
} fputs(recvbuf.buf, stdout);
memset(&sendbuf, 0, sizeof(sendbuf));
memset(&recvbuf, 0, sizeof(recvbuf));
} close(sock); return 0;
}
这种解决粘包的方法特别实在网络中传输的时候十分必要。
09Socket编程的更多相关文章
- Python语言系列-09-socket编程
简介 软件开发的架构 1.C/S架构(client-server) 2.B/S架构 (browser-server) 网络基础概念 网络三要素: 1.ip 2.port 3.通信协议:TCP.UDP ...
- 从直播编程到直播教育:LiveEdu.tv开启多元化的在线学习直播时代
2015年9月,一个叫Livecoding.tv的网站在互联网上引起了编程界的注意.缘于Pingwest品玩的一位编辑在上网时无意中发现了这个网站,并写了一篇文章<一个比直播睡觉更奇怪的网站:直 ...
- JavaScript之父Brendan Eich,Clojure 创建者Rich Hickey,Python创建者Van Rossum等编程大牛对程序员的职业建议
软件开发是现时很火的职业.据美国劳动局发布的一项统计数据显示,从2014年至2024年,美国就业市场对开发人员的需求量将增长17%,而这个增长率比起所有职业的平均需求量高出了7%.很多人年轻人会选择编 ...
- 读书笔记:JavaScript DOM 编程艺术(第二版)
读完还是能学到很多的基础知识,这里记录下,方便回顾与及时查阅. 内容也有自己的一些补充. JavaScript DOM 编程艺术(第二版) 1.JavaScript简史 JavaScript由Nets ...
- [ 高并发]Java高并发编程系列第二篇--线程同步
高并发,听起来高大上的一个词汇,在身处于互联网潮的社会大趋势下,高并发赋予了更多的传奇色彩.首先,我们可以看到很多招聘中,会提到有高并发项目者优先.高并发,意味着,你的前雇主,有很大的业务层面的需求, ...
- C#异步编程(一)
异步编程简介 前言 本人学习.Net两年有余,是第一次写博客,虽然写的很认真,当毕竟是第一次,肯定会有很多不足之处, 希望大家照顾照顾新人,有错误之处可以指出来,我会虚心接受的. 何谓异步 与同步相对 ...
- UE4新手之编程指南
虚幻引擎4为程序员提供了两套工具集,可共同使用来加速开发的工作流程. 新的游戏类.Slate和Canvas用户接口元素以及编辑器功能可以使用C++语言来编写,并且在使用Visual Studio 或 ...
- C#与C++的发展历程第三 - C#5.0异步编程巅峰
系列文章目录 1. C#与C++的发展历程第一 - 由C#3.0起 2. C#与C++的发展历程第二 - C#4.0再接再厉 3. C#与C++的发展历程第三 - C#5.0异步编程的巅峰 C#5.0 ...
- 猫哥网络编程系列:HTTP PEM 万能调试法
注:本文内容较长且细节较多,建议先收藏再阅读,原文将在 Github 上维护与更新. 在 HTTP 接口开发与调试过程中,我们经常遇到以下类似的问题: 为什么本地环境接口可以调用成功,但放到手机上就跑 ...
随机推荐
- C++学习笔记3:一些错误
错误1:执行程序后,控制台闪烁后退出 处理办法: (1)添加头文件:#include <iostream> (2)在程序返回前添加代码: std::cin.clear();//清除错误标志 ...
- Cache
1.Cache中的块与主存储器中的块时按照什么样的规则建立对应关系的? 2.在这种对应关系下,主存地址又是如何变换成Cache地址的? Cache信息: 1.数据Cache和指令Cache是分开还是统 ...
- sdust 2410 Mine Number
今天看了3个这种题了 枚举第一行即可 #include<cstdio> #include<cstring> #include<iostream> #include ...
- 正则表达式入门教程&&经典Javascript正则表达式(share)
前言 例子: ^.+@.+\\..+$ 这样的代码曾经多次把我自己给吓退过.可能很多人也是被这样的代码给吓跑的吧.继续阅读本文将让你也可以自由应用这样的代码. 正文 教程:正则表达式30分钟入门教程 ...
- Android开发优化
一:Android性能优化之渲染篇 1.双层LinearLayout重叠用一个RelatayLout替代布局 二:Android性能优化之运算篇 1.float比较时间是int的4倍,尽量使用int类 ...
- 发布常见问题(C#)
1.Sys.WebForms.PageRequestManagerServerErrorException: 在服务器上处理请求时出现未知错误.服务器返回的状态码为: 500 可能的原因: asp.n ...
- HDU-3586 Information Disturbing(树形DP+删边)
题目大意:一棵有n个节点的有根树,1为根节点,边带权,表示删掉这条边的代价.现在要删掉一些边,使叶子节点不能到达根节点.但是,每次删除的边的代价不能超过limit,删掉的边的总代价不能超过m,求最小的 ...
- HDU-4276 The Ghost Blows Light (树形DP+背包)
题目大意:在一个n个节点的树形迷宫中,1为起点,n为出口.每个节点上有一定价值的珠宝,在节点之间移动的时间已知,问在能走出迷宫的前提下并且不超过m的时间内能收集的最多珠宝是多少? 题目分析:在树上,从 ...
- click 绑定(三)防止事件冒泡
防止事件冒泡 默认情况下,Knockout允许click事件继续在更高一层的事件句柄上冒泡执行.例如,如果你的元素和父元素都绑定了click事件,那当你点击该元素的时候两个事件都会触发的.如果需要, ...
- java多线程:并发包中ReentrantReadWriteLock读写锁的原理
一:读写锁解决的场景问题--->数据的读取频率远远大于写的频率的场景,就可以使用读写锁.二:读写锁的结构--->用state一个变量.将其转化成二进制,前16位为高位,标记读线程获取锁的次 ...