一段关于回车和换行的有意思的历史

常识zkbhj 发表了文章 • 0 个评论 • 1215 次浏览 • 2017-11-02 12:02 • 来自相关话题

关于“回车”(carriage return)和“换行”(line feed)这两个概念的来历和区别。

在计算机还没有出现之前,有一种叫做电传打字机(Teletype Model 33)的玩意,每秒钟可以打10个字符。但是它有一个问题,就是打完一行换行的时候,要用去0.2秒,正好可以打两个字符。要是在这0.2秒里面,又有新的字符传过来,那么这个字符将丢失。

于是,研制人员想了个办法解决这个问题,就是在每行后面加两个表示结束的字符。一个叫做“回车”,告诉打字机把打印头定位在左边界;另一个叫做“换行”,告诉打字机把纸向下移一行。

这就是“换行”和“回车”的来历,从它们的英语名字上也可以看出一二。

后来,计算机发明了,这两个概念也就被般到了计算机上。那时,存储器很贵,一些科学家认为在每行结尾加两个字符太浪费了,加一个就可以。于是,就出现了分歧。

Unix系统里,每行结尾只有“<换行>”,即“\n”;Windows系统里面,每行结尾是“<换行><回 车>”,即“\n\r”;Mac系统里,每行结尾是“<回车>”。一个直接后果是,Unix/Mac系统下的文件在Windows里打 开的话,所有文字会变成一行;而Windows里的文件在Unix/Mac下打开的话,在每行的结尾可能会多出一个^M符号。 
Dos和windows采用回车+换行CR/LF表示下一行,而UNIX/Linux采用换行符LF表示下一行,苹果机(MAC OS系统)则采用回车符CR表示下一行.
 
CR用符号'\r'表示, 十进制ASCII代码是13, 十六进制代码为0x0D;LF使用'\n'符号表示, ASCII代码是10, 十六制为0x0A. 所以Windows平台上换行在文本文件中是使用 0d 0a 两个字节表示, 而UNIX和苹果平台上换行则是使用0a或0d一个字节表示.


    一般操作系统上的运行库会自动决定文本文件的换行格式. 如一个程序在windows上运行就生成CR/LF换行格式的文本文件,而在Linux上运行就生成LF格式换行的文本文件. 在一个平台上使用另一种换行符的文件文件可能会带来意想不到的问题, 特别是在编辑程序代码时. 有时候代码在编辑器中显示正常, 但在编辑时却会因为换行符问题而出错. 很多文本/代码编辑器带有换行符转换功能, 使用这个功能可以将文本文件中的换行符在不同格式单互换.

    在不同平台间使用FTP软件传送文件时, 在ascii文本模式传输模式下, 一些FTP客户端程序会自动对换行格式进行转换. 经过这种传输的文件字节数可能会发生变化. 如果你不想ftp修改原文件, 可以使用bin模式(二进制模式)传输文本. 查看全部
关于“回车”(carriage return)和“换行”(line feed)这两个概念的来历和区别。

在计算机还没有出现之前,有一种叫做电传打字机(Teletype Model 33)的玩意,每秒钟可以打10个字符。但是它有一个问题,就是打完一行换行的时候,要用去0.2秒,正好可以打两个字符。要是在这0.2秒里面,又有新的字符传过来,那么这个字符将丢失。

于是,研制人员想了个办法解决这个问题,就是在每行后面加两个表示结束的字符。一个叫做“回车”,告诉打字机把打印头定位在左边界;另一个叫做“换行”,告诉打字机把纸向下移一行。

这就是“换行”和“回车”的来历,从它们的英语名字上也可以看出一二。

后来,计算机发明了,这两个概念也就被般到了计算机上。那时,存储器很贵,一些科学家认为在每行结尾加两个字符太浪费了,加一个就可以。于是,就出现了分歧。

Unix系统里,每行结尾只有“<换行>”,即“\n”;Windows系统里面,每行结尾是“<换行><回 车>”,即“\n\r”;Mac系统里,每行结尾是“<回车>”。一个直接后果是,Unix/Mac系统下的文件在Windows里打 开的话,所有文字会变成一行;而Windows里的文件在Unix/Mac下打开的话,在每行的结尾可能会多出一个^M符号。 
  • Dos和windows采用回车+换行CR/LF表示下一行,
  • 而UNIX/Linux采用换行符LF表示下一行,
  • 苹果机(MAC OS系统)则采用回车符CR表示下一行.

 
  • CR用符号'\r'表示, 十进制ASCII代码是13, 十六进制代码为0x0D;
  • LF使用'\n'符号表示, ASCII代码是10, 十六制为0x0A. 所以Windows平台上换行在文本文件中是使用 0d 0a 两个字节表示, 而UNIX和苹果平台上换行则是使用0a或0d一个字节表示.



    一般操作系统上的运行库会自动决定文本文件的换行格式. 如一个程序在windows上运行就生成CR/LF换行格式的文本文件,而在Linux上运行就生成LF格式换行的文本文件. 在一个平台上使用另一种换行符的文件文件可能会带来意想不到的问题, 特别是在编辑程序代码时. 有时候代码在编辑器中显示正常, 但在编辑时却会因为换行符问题而出错. 很多文本/代码编辑器带有换行符转换功能, 使用这个功能可以将文本文件中的换行符在不同格式单互换.

    在不同平台间使用FTP软件传送文件时, 在ascii文本模式传输模式下, 一些FTP客户端程序会自动对换行格式进行转换. 经过这种传输的文件字节数可能会发生变化. 如果你不想ftp修改原文件, 可以使用bin模式(二进制模式)传输文本.

Linux下Shell脚本执行PHP报错:Could not open input file,怎么解决?

回复

PHPzkbhj 回复了问题 • 1 人关注 • 1 个回复 • 2635 次浏览 • 2017-11-02 11:59 • 来自相关话题

经常打开Excel会提示内存或存储空间不足,怎么解决?

回复

工具软件zkbhj 回复了问题 • 1 人关注 • 1 个回复 • 4548 次浏览 • 2017-10-31 10:30 • 来自相关话题

Web网站的几个并发量级

服务器zkbhj 发表了文章 • 0 个评论 • 1360 次浏览 • 2017-10-25 10:45 • 来自相关话题

评价一个网站的“大小”,处于视角的不同,有很多种衡量的方法,类似文章数,页面数之类的数据非常明显,也没有什么可以争议的。但对于并发来说,争议非常之多,这里就从一个技术的角度开始,谈谈几个Web网站的数量级。

相信很多人谈论一个网站的热度,总免不了会询问日均PV,同时在线人数、注册用户数等运营数据,说实话从技术角度来说,这几个数值没有一个可以放在一起比较的——一个静态网站的PV跟一个SNS类/Web Game网站的PV根本就不是一回事。由于互联网有一个传说中的“3秒定律”,可能当下更多的网站技术指标要求1.5秒以内加载整页,或者至少可以达到阅读的标准。如果要较真什么“同时在线”,毫不客气的说,对于HTTP这类短链接的网络协议来说,在WebSocket还不普及的时代,能统计在线纯属扯淡,唯一能做的只是取个时间段,计算下访问用户而已。这些依然可以换算成QPS(Quest Per Second每秒请求数)。就并发而言,我唯一推崇的只有理论最大QPS和悲观QPS。

这里就大致根据理论最大QPS,给网站做几个分类

50QPS以下——小网站

没什么好说的,简单的小网站而已,就如同本站这样,你可以用最简单的方法快速搭建,短期没有太多的技术瓶颈,只要服务器不要太烂就好。

50~100QPS——DB极限型

大部分的关系型数据库的每次请求大多都能控制在0.01秒左右,即便你的网站每页面只有一次DB请求,那么页面请求无法保证在1秒钟内完成100个请求,这个阶段要考虑做Cache或者多DB负载。无论那种方案,网站重构是不可避免的。

300~800QPS——带宽极限型

目前服务器大多用了IDC提供的“百兆带宽”,这意味着网站出口的实际带宽是8M Byte左右。假定每个页面只有10K Byte,在这个并发条件下,百兆带宽已经吃完。首要考虑是CDN加速/异地缓存,多机负载等技术。

500~1000QPS——内网带宽极限+Memcache极限型

由于Key/value的特性,每个页面对memcache的请求远大于直接对DB的请求,Memcache的悲观并发数在2w左右,看似很高,但事实上大多数情况下,首先是有可能在次之前内网的带宽就已经吃光,接着是在8K QPS左右的情况下,Memcache已经表现出了不稳定,如果代码上没有足够的优化,可能直接将压力转嫁到了DB层上,这就最终导致整个系统在达到某个阀值之上,性能迅速下滑。

1000~2000QPS——FORK/SELECT,锁模式极限型

好吧,一句话:线程模型决定吞吐量。不管你系统中最常见的锁是什么锁,这个级别下,文件系统访问锁都成为了灾难。这就要求系统中不能存在中央节点,所有的数据都必须分布存储,数据需要分布处理。总之,关键词:分布

2000QPS以上——C10K极限 查看全部
评价一个网站的“大小”,处于视角的不同,有很多种衡量的方法,类似文章数,页面数之类的数据非常明显,也没有什么可以争议的。但对于并发来说,争议非常之多,这里就从一个技术的角度开始,谈谈几个Web网站的数量级。

相信很多人谈论一个网站的热度,总免不了会询问日均PV,同时在线人数、注册用户数等运营数据,说实话从技术角度来说,这几个数值没有一个可以放在一起比较的——一个静态网站的PV跟一个SNS类/Web Game网站的PV根本就不是一回事。由于互联网有一个传说中的“3秒定律”,可能当下更多的网站技术指标要求1.5秒以内加载整页,或者至少可以达到阅读的标准。如果要较真什么“同时在线”,毫不客气的说,对于HTTP这类短链接的网络协议来说,在WebSocket还不普及的时代,能统计在线纯属扯淡,唯一能做的只是取个时间段,计算下访问用户而已。这些依然可以换算成QPS(Quest Per Second每秒请求数)。就并发而言,我唯一推崇的只有理论最大QPS和悲观QPS。

这里就大致根据理论最大QPS,给网站做几个分类

50QPS以下——小网站

没什么好说的,简单的小网站而已,就如同本站这样,你可以用最简单的方法快速搭建,短期没有太多的技术瓶颈,只要服务器不要太烂就好。

50~100QPS——DB极限型

大部分的关系型数据库的每次请求大多都能控制在0.01秒左右,即便你的网站每页面只有一次DB请求,那么页面请求无法保证在1秒钟内完成100个请求,这个阶段要考虑做Cache或者多DB负载。无论那种方案,网站重构是不可避免的。

300~800QPS——带宽极限型

目前服务器大多用了IDC提供的“百兆带宽”,这意味着网站出口的实际带宽是8M Byte左右。假定每个页面只有10K Byte,在这个并发条件下,百兆带宽已经吃完。首要考虑是CDN加速/异地缓存,多机负载等技术。

500~1000QPS——内网带宽极限+Memcache极限型

由于Key/value的特性,每个页面对memcache的请求远大于直接对DB的请求,Memcache的悲观并发数在2w左右,看似很高,但事实上大多数情况下,首先是有可能在次之前内网的带宽就已经吃光,接着是在8K QPS左右的情况下,Memcache已经表现出了不稳定,如果代码上没有足够的优化,可能直接将压力转嫁到了DB层上,这就最终导致整个系统在达到某个阀值之上,性能迅速下滑。

1000~2000QPS——FORK/SELECT,锁模式极限型

好吧,一句话:线程模型决定吞吐量。不管你系统中最常见的锁是什么锁,这个级别下,文件系统访问锁都成为了灾难。这就要求系统中不能存在中央节点,所有的数据都必须分布存储,数据需要分布处理。总之,关键词:分布

2000QPS以上——C10K极限

【技能每日GET】记录平时零碎的知识和技能点

随手记zkbhj 发表了文章 • 0 个评论 • 1536 次浏览 • 2017-10-22 17:28 • 来自相关话题

【2017年10月22日16:57:45】
 
[ 行首/行尾 ]

在vim中,移动到行首的命令非常简单,就是”0“,这个是数字0,而不是大写字母O。移动到行尾的命令是”$“。

另外还有一个命令”^“,用它可以移动到行首的第一个非空白字符。

在正则表达式中我们会看到,”^“字符代表行首,而”$“字符代表行尾。可见,vi/vim的按键的安排,的确是别具匠心的。
 
[ 在文件中移动 ]

vim提供了一些命令,可以方便的在文件中移动。

命令”gg“移动到文件的第一行,而命令”G“则移动到文件的最后一行。

命令”G“前可以加上数字,在这里,数字的含义并不是倍数,而是你打算跳转的行号。例如,你想跳转到文件的第1234行,只需输入”1234G“。

你还可以按百分比来跳转,例如,你想跳到文件的正中间,输入”50%“;如果想跳到75%处,输入”75%“。注意,你必须先输入一个数字,然后输入”%“。如果直接输入”%“,那含义就完全不同了。”:help N%“阅读更多细节。

在文件中移动,你可能会迷失自己的位置,这时使用”CTRL-G“命令,查看一下自己位置。这个命令会显示出光标的位置及其它信息。为了避免迷失,你可以打开行号显示;使用”:set number“命令后,会在每一行前显示出行号,可以更方便的定位的跳转(:help ‘number’)。 查看全部
【2017年10月22日16:57:45】
 
[ 行首/行尾 ]

在vim中,移动到行首的命令非常简单,就是”0“,这个是数字0,而不是大写字母O。移动到行尾的命令是”$“

另外还有一个命令”^“,用它可以移动到行首的第一个非空白字符。

在正则表达式中我们会看到,”^“字符代表行首,而”$“字符代表行尾。可见,vi/vim的按键的安排,的确是别具匠心的。
 
[ 在文件中移动 ]

vim提供了一些命令,可以方便的在文件中移动。

命令”gg“移动到文件的第一行,而命令”G“则移动到文件的最后一行。

命令”G“前可以加上数字,在这里,数字的含义并不是倍数,而是你打算跳转的行号。例如,你想跳转到文件的第1234行,只需输入”1234G“

你还可以按百分比来跳转,例如,你想跳到文件的正中间,输入”50%“;如果想跳到75%处,输入”75%“。注意,你必须先输入一个数字,然后输入”%“。如果直接输入”%“,那含义就完全不同了。”:help N%“阅读更多细节。

在文件中移动,你可能会迷失自己的位置,这时使用”CTRL-G“命令,查看一下自己位置。这个命令会显示出光标的位置及其它信息。为了避免迷失,你可以打开行号显示;使用”:set number“命令后,会在每一行前显示出行号,可以更方便的定位的跳转(:help ‘number’)。

如何拼SQL到数据到MySQL表里更新部分字段?

回复

数据库zkbhj 回复了问题 • 1 人关注 • 1 个回复 • 4035 次浏览 • 2017-10-20 12:21 • 来自相关话题

初识Socket套接字

服务器zkbhj 发表了文章 • 0 个评论 • 1301 次浏览 • 2017-10-18 22:11 • 来自相关话题

对TCP/IP、UDP、Socket编程这些词你不会很陌生吧?随着网络技术的发展,这些词充斥着我们的耳朵。那么我想问:

1. 什么是TCP/IP、UDP?

2. Socket在哪里呢?

3. Socket是什么呢?

4. 你会使用它们吗?

什么是TCP/IP、UDP?

TCP/IP(Transmission Control Protocol/Internet Protocol)即传输控制协议/网间协议,是一个工业标准的协议集,它是为广域网(WANs)设计的。

UDP(User Data Protocol,用户数据报协议)是与TCP相对应的协议。它是属于TCP/IP协议族中的一种。

这里有一张图,表明了这些协议的关系。    





 
TCP/IP协议族包括运输层、网络层、链路层。现在你知道TCP/IP与UDP的关系了吧。

Socket在哪里呢?

在图1中,我们没有看到Socket的影子,那么它到底在哪里呢?还是用图来说话,一目了然。





 
原来Socket在这里。

Socket是什么呢?

Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面(Facade)模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。

你会使用它们吗?

前人已经给我们做了好多的事了,网络间的通信也就简单了许多,但毕竟还是有挺多工作要做的。以前听到Socket编程,觉得它是比较高深的编程知识,但是只要弄清Socket编程的工作原理,神秘的面纱也就揭开了。

一个生活中的场景。你要打电话给一个朋友,先拨号,朋友听到电话铃声后提起电话,这时你和你的朋友就建立起了连接,就可以讲话了。等交流结束,挂断电话结束此次交谈。    生活中的场景就解释了这工作原理,也许TCP/IP协议族就是诞生于生活中,这也不一定。





 
先从服务器端说起。服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束。

在这里我就举个简单的例子,我们走的是TCP协议这条路(见图2)。例子用MFC编写,运行的界面如下:





 





 
  在客户端输入服务器端的IP地址和发送的数据,然后按发送按钮,服务器端接收到数据,然后回应客户端。客户端读取回应的数据,显示在界面上。
下面是接收数据和发送数据的函数:
int Receive(SOCKET fd, char *szText, int len)
{
int cnt;
int rc;
cnt = len;

while(cnt>0)
{
rc= recv(fd, szText, cnt, 0);

if(rc == SOCKET_ERROR)
{
return -1;
}

if(rc == 0)
{
return len-cnt;
}

szText += rc;
cnt -= rc;
}

return len;

}

int Send(SOCKET fd, char *szText, int len)
{

int cnt;
int rc;
cnt = len;

while(cnt > 0)
{
rc = send(fd, szText, cnt, 0);

if(rc == SOCKET_ERROR)
{
return -1;
}

if(rc == 0)
{
return len-cnt;
}

szText += rc;
cnt -= rc;
}

return len;
}服务器端:

在服务器端,主要是启动Socket和监听线程。#define DEFAULT_PORT 2000

void CServerDlg::OnStart()
{
sockaddr_in local;
DWORDdwThreadID = 0;
local.sin_family = AF_INET;

//设置的端口为DEFAULT_PORT。
local.sin_port = htons(DEFAULT_PORT);

//IP地址设置成INADDR_ANY,让系统自动获取本机的IP地址。
local.sin_addr.S_un.S_addr = INADDR_ANY;

//初始化Socket
m_Listening = socket(AF_INET,SOCK_STREAM,0);

if(m_Listening == INVALID_SOCKET)
{
return ;
}

//将本地地址绑定到所创建的套接字上
if(bind(m_Listening, (LPSOCKADDR) & local, sizeof(local)) == SOCKET_ERROR )
{
closesocket(m_Listening);
return ;
}

//创建监听线程,这样也能响应界面上操作。
m_hListenThread = ::CreateThread(NULL, 0, ListenThread, this, 0, &dwThreadID);

m_StartBtn.EnableWindow(FALSE);

m_StopBtn.EnableWindow(TRUE);

}

//监听线程函数:
DWORD WINAPI CServerDlg::ListenThread(LPVOID lpparam)
{
CServerDlg* pDlg = (CServerDlg*)lpparam;

if(pDlg == NULL) return 0;

SOCKET Listening = pDlg->m_Listening;

//开始监听是否有客户端连接。
if(listen(Listening,40) == SOCKET_ERROR)
{
return 0;
}

char szBuf[MAX_PATH];

//初始化
memset(szBuf,0,MAX_PATH);

while(1)
{

SOCKET ConnectSocket;
sockaddr_in ClientAddr;

int nLen = sizeof(sockaddr);

//阻塞直到有客户端连接,不然多浪费CPU资源。
ConnectSocket = accept(Listening,(sockaddr*)&ClientAddr,&nLen);

//得到客户端的IP地址。
char *pAddrname = inet_ntoa(ClientAddr.sin_addr);

pDlg->Receive(ConnectSocket,szBuf,100);

//界面上显示请求数据。
pDlg->SetRequestText(szBuf);

strcat(szBuf, " :我是老猫,收到(");

strcat(szBuf, pAddrname);

strcat(szBuf, ")");

//向客户端发送回应数据
pDlg->Send(ConnectSocket,szBuf,100);
}
return 0;
}    服务器端一直在监听是否有客户端连接,如有连接,处理客户端的请求,给出回应,然后继续监听。

客户端:

客户端的发送函数:
#define DEFAULT_PORT 2000

void CClientDlg::OnSend()
{

DWORDdwIP = 0;

TCHAR szText[MAX_PATH];

memset(szText, 0, MAX_PATH);

m_IP.GetWindowText(szText, MAX_PATH);

//把字符串形式的IP地址转成IN_ADDR结构需要的形式。
dwIP = inet_addr(szText);

m_RequestEdit.GetWindowText(szText, MAX_PATH);

sockaddr_in local;

SOCKET socketTmp;

//必须是AF_INET,表示该socket在Internet域中进行通信
local.sin_family = AF_INET;

//端口号
local.sin_port = htons(DEFAULT_PORT);

//服务器的IP地址。
local.sin_addr.S_un.S_addr = dwIP;

////初始化Socket
socketTmp = socket(AF_INET, SOCK_STREAM, 0);

//连接服务器

if(connect(socketTmp, (LPSOCKADDR) & local, sizeof(local)) < 0)
{
closesocket(socketTmp);
MessageBox("连接服务器失败。");
return ;
}

//发送请求,为简单只发100字节,在服务器端也规定100字节。
Send(socketTmp, szText, 100);

//读取服务器端返回的数据。
memset(szText,0,MAX_PATH);

//接收服务器端的回应。
Receive(socketTmp, szText, 100);

TCHAR szMessage[MAX_PATH];

memset(szMessage, 0, MAX_PATH);

strcat(szMessage, szText);

//界面上显示回应数据。
m_ReplyBtn.SetWindowText(szMessage);

closesocket(socketTmp);
}       客户端就一个函数完成了一次通信。在这里IP地址为何用127.0.0.1呢?使用这个IP地址,服务器端和客户端就能运行在同一台机器上,这样调试方便多了。当然你可以在你朋友的机器上运行Server程序(本人在局域网中测试过),在自己的机器上运行Client程序,当然输入的IP地址就该是你朋友机器的IP地址了。

       简单的理论和实践都说了,现在Socket编程不神秘了吧?希望对你有些帮助。 查看全部
对TCP/IP、UDP、Socket编程这些词你不会很陌生吧?随着网络技术的发展,这些词充斥着我们的耳朵。那么我想问:

1. 什么是TCP/IP、UDP?

2. Socket在哪里呢?

3. Socket是什么呢?

4. 你会使用它们吗?

什么是TCP/IP、UDP?

TCP/IP(Transmission Control Protocol/Internet Protocol)即传输控制协议/网间协议,是一个工业标准的协议集,它是为广域网(WANs)设计的。

UDP(User Data Protocol,用户数据报协议)是与TCP相对应的协议。它是属于TCP/IP协议族中的一种。

这里有一张图,表明了这些协议的关系。    

20125448-c898586acc614d02ad4c97d71da3634b.jpg

 
TCP/IP协议族包括运输层、网络层、链路层。现在你知道TCP/IP与UDP的关系了吧。

Socket在哪里呢?

在图1中,我们没有看到Socket的影子,那么它到底在哪里呢?还是用图来说话,一目了然。

20125526-ee33df3ffda64a92a9f8ba170ab0ddb9.jpg

 
原来Socket在这里。

Socket是什么呢?

Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面(Facade)模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。

你会使用它们吗?

前人已经给我们做了好多的事了,网络间的通信也就简单了许多,但毕竟还是有挺多工作要做的。以前听到Socket编程,觉得它是比较高深的编程知识,但是只要弄清Socket编程的工作原理,神秘的面纱也就揭开了。

一个生活中的场景。你要打电话给一个朋友,先拨号,朋友听到电话铃声后提起电话,这时你和你的朋友就建立起了连接,就可以讲话了。等交流结束,挂断电话结束此次交谈。    生活中的场景就解释了这工作原理,也许TCP/IP协议族就是诞生于生活中,这也不一定。

20125856-3ed8e334d72c4f979299551b96df1d6f.jpg

 
先从服务器端说起。服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束。

在这里我就举个简单的例子,我们走的是TCP协议这条路(见图2)。例子用MFC编写,运行的界面如下:

20130032-7388909634d04c589661b5b247539dc4.jpg

 

20130059-d3a7147eac4041a1bab09a81c82f918d.jpg

 
  在客户端输入服务器端的IP地址和发送的数据,然后按发送按钮,服务器端接收到数据,然后回应客户端。客户端读取回应的数据,显示在界面上。
下面是接收数据和发送数据的函数:
int Receive(SOCKET fd, char *szText, int len)
{
int cnt;
int rc;
cnt = len;

while(cnt>0)
{
rc= recv(fd, szText, cnt, 0);

if(rc == SOCKET_ERROR)
{
return -1;
}

if(rc == 0)
{
return len-cnt;
}

szText += rc;
cnt -= rc;
}

return len;

}

int Send(SOCKET fd, char *szText, int len)
{

int cnt;
int rc;
cnt = len;

while(cnt > 0)
{
rc = send(fd, szText, cnt, 0);

if(rc == SOCKET_ERROR)
{
return -1;
}

if(rc == 0)
{
return len-cnt;
}

szText += rc;
cnt -= rc;
}

return len;
}
服务器端:

在服务器端,主要是启动Socket和监听线程。
#define DEFAULT_PORT 2000

void CServerDlg::OnStart()
{
sockaddr_in local;
DWORDdwThreadID = 0;
local.sin_family = AF_INET;

//设置的端口为DEFAULT_PORT。
local.sin_port = htons(DEFAULT_PORT);

//IP地址设置成INADDR_ANY,让系统自动获取本机的IP地址。
local.sin_addr.S_un.S_addr = INADDR_ANY;

//初始化Socket
m_Listening = socket(AF_INET,SOCK_STREAM,0);

if(m_Listening == INVALID_SOCKET)
{
return ;
}

//将本地地址绑定到所创建的套接字上
if(bind(m_Listening, (LPSOCKADDR) & local, sizeof(local)) == SOCKET_ERROR )
{
closesocket(m_Listening);
return ;
}

//创建监听线程,这样也能响应界面上操作。
m_hListenThread = ::CreateThread(NULL, 0, ListenThread, this, 0, &dwThreadID);

m_StartBtn.EnableWindow(FALSE);

m_StopBtn.EnableWindow(TRUE);

}

//监听线程函数:
DWORD WINAPI CServerDlg::ListenThread(LPVOID lpparam)
{
CServerDlg* pDlg = (CServerDlg*)lpparam;

if(pDlg == NULL) return 0;

SOCKET Listening = pDlg->m_Listening;

//开始监听是否有客户端连接。
if(listen(Listening,40) == SOCKET_ERROR)
{
return 0;
}

char szBuf[MAX_PATH];

//初始化
memset(szBuf,0,MAX_PATH);

while(1)
{

SOCKET ConnectSocket;
sockaddr_in ClientAddr;

int nLen = sizeof(sockaddr);

//阻塞直到有客户端连接,不然多浪费CPU资源。
ConnectSocket = accept(Listening,(sockaddr*)&ClientAddr,&nLen);

//得到客户端的IP地址。
char *pAddrname = inet_ntoa(ClientAddr.sin_addr);

pDlg->Receive(ConnectSocket,szBuf,100);

//界面上显示请求数据。
pDlg->SetRequestText(szBuf);

strcat(szBuf, " :我是老猫,收到(");

strcat(szBuf, pAddrname);

strcat(szBuf, ")");

//向客户端发送回应数据
pDlg->Send(ConnectSocket,szBuf,100);
}
return 0;
}
    服务器端一直在监听是否有客户端连接,如有连接,处理客户端的请求,给出回应,然后继续监听。

客户端:

客户端的发送函数:
#define DEFAULT_PORT 2000

void CClientDlg::OnSend()
{

DWORDdwIP = 0;

TCHAR szText[MAX_PATH];

memset(szText, 0, MAX_PATH);

m_IP.GetWindowText(szText, MAX_PATH);

//把字符串形式的IP地址转成IN_ADDR结构需要的形式。
dwIP = inet_addr(szText);

m_RequestEdit.GetWindowText(szText, MAX_PATH);

sockaddr_in local;

SOCKET socketTmp;

//必须是AF_INET,表示该socket在Internet域中进行通信
local.sin_family = AF_INET;

//端口号
local.sin_port = htons(DEFAULT_PORT);

//服务器的IP地址。
local.sin_addr.S_un.S_addr = dwIP;

////初始化Socket
socketTmp = socket(AF_INET, SOCK_STREAM, 0);

//连接服务器

if(connect(socketTmp, (LPSOCKADDR) & local, sizeof(local)) < 0)
{
closesocket(socketTmp);
MessageBox("连接服务器失败。");
return ;
}

//发送请求,为简单只发100字节,在服务器端也规定100字节。
Send(socketTmp, szText, 100);

//读取服务器端返回的数据。
memset(szText,0,MAX_PATH);

//接收服务器端的回应。
Receive(socketTmp, szText, 100);

TCHAR szMessage[MAX_PATH];

memset(szMessage, 0, MAX_PATH);

strcat(szMessage, szText);

//界面上显示回应数据。
m_ReplyBtn.SetWindowText(szMessage);

closesocket(socketTmp);
}
       客户端就一个函数完成了一次通信。在这里IP地址为何用127.0.0.1呢?使用这个IP地址,服务器端和客户端就能运行在同一台机器上,这样调试方便多了。当然你可以在你朋友的机器上运行Server程序(本人在局域网中测试过),在自己的机器上运行Client程序,当然输入的IP地址就该是你朋友机器的IP地址了。

       简单的理论和实践都说了,现在Socket编程不神秘了吧?希望对你有些帮助。

PHP函数学习之循环

PHPzkbhj 发表了文章 • 0 个评论 • 1141 次浏览 • 2017-10-11 09:42 • 来自相关话题

foreach
foreach 语法结构提供了遍历数组的简单方式。foreach 仅能够应用于数组和对象,如果尝试应用于其他数据类型的变量,或者未初始化的变量将发出错误信息。有两种语法:
foreach (array_expression as $value)
statement

foreach (array_expression as $key => $value)
statement第一种格式遍历给定的 array_expression 数组。每次循环中,当前单元的值被赋给 $value 并且数组内部的指针向前移一步(因此下一次循环中将会得到下一个单元)。

第二种格式做同样的事,只除了当前单元的键名也会在每次循环中被赋给变量 $key。
 
可以很容易地通过在 $value 之前加上 & 来修改数组的元素。此方法将以引用赋值而不是拷贝一个值。
<?php
$arr = array(1, 2, 3, 4);
foreach ($arr as &$value) {
$value = $value * 2;
}
// $arr is now array(2, 4, 6, 8)
unset($value); // 最后取消掉引用
?>$value 的引用仅在被遍历的数组可以被引用时才可用(例如是个变量)。以下代码则无法运行:
<?php
foreach (array(1, 2, 3, 4) as &$value) {
$value = $value * 2;
}

?>用 list() 给嵌套的数组解包
(PHP 5 >= 5.5.0, PHP 7)

PHP 5.5 增添了遍历一个数组的数组的功能并且把嵌套的数组解包到循环变量中,只需将 list() 作为值提供。
<?php
$array = [
[1, 2],
[3, 4],
];

foreach ($array as list($a, $b)) {
// $a contains the first element of the nested array,
// and $b contains the second element.
echo "A: $a; B: $b\n";
}
?>list() 中的单元可以少于嵌套数组的,此时多出来的数组单元将被忽略:
<?php
$array = [
[1, 2],
[3, 4],
];

foreach ($array as list($a)) {
// Note that there is no $b here.
echo "$a\n";
}
?> 查看全部
foreach
foreach 语法结构提供了遍历数组的简单方式。foreach 仅能够应用于数组和对象,如果尝试应用于其他数据类型的变量,或者未初始化的变量将发出错误信息。有两种语法:
foreach (array_expression as $value)
statement

foreach (array_expression as $key => $value)
statement
第一种格式遍历给定的 array_expression 数组。每次循环中,当前单元的值被赋给 $value 并且数组内部的指针向前移一步(因此下一次循环中将会得到下一个单元)。

第二种格式做同样的事,只除了当前单元的键名也会在每次循环中被赋给变量 $key。
 
可以很容易地通过在 $value 之前加上 & 来修改数组的元素。此方法将以引用赋值而不是拷贝一个值。
<?php
$arr = array(1, 2, 3, 4);
foreach ($arr as &$value) {
$value = $value * 2;
}
// $arr is now array(2, 4, 6, 8)
unset($value); // 最后取消掉引用
?>
$value 的引用仅在被遍历的数组可以被引用时才可用(例如是个变量)。以下代码则无法运行:
<?php
foreach (array(1, 2, 3, 4) as &$value) {
$value = $value * 2;
}

?>
用 list() 给嵌套的数组解包
(PHP 5 >= 5.5.0, PHP 7)

PHP 5.5 增添了遍历一个数组的数组的功能并且把嵌套的数组解包到循环变量中,只需将 list() 作为值提供。
<?php
$array = [
[1, 2],
[3, 4],
];

foreach ($array as list($a, $b)) {
// $a contains the first element of the nested array,
// and $b contains the second element.
echo "A: $a; B: $b\n";
}
?>
list() 中的单元可以少于嵌套数组的,此时多出来的数组单元将被忽略:
<?php
$array = [
[1, 2],
[3, 4],
];

foreach ($array as list($a)) {
// Note that there is no $b here.
echo "$a\n";
}
?>

windows下给WAMP集成环境安装redis扩展方法

服务器zkbhj 发表了文章 • 0 个评论 • 1327 次浏览 • 2017-10-03 12:18 • 来自相关话题

如果你的程序中正好使用到了redis,而你的本地Windows开发环境中又未安装redis相关的扩展,那么这篇文章可以帮到你!
 
我本地的wamp版本是3.0.6,PHP版本是7.0.10。
 
安装redis扩展需要两个步骤:

1、下载window下的redis,访问 https://github.com/ServiceStack/redis-windows  即可下载
 
  1.1 下载redis,可以 直接下载最新版的redis redis-latest.zip

  1.2 解压到D或者其他盘下的目录即可,我解压到了F:/redis-latest/

  1.3 双击redis-server.exe,如下图所示,成功运行redis服务端了





 
 1.4 双击 redis-cli.exe 可以打开客户端 尝试一下设置个缓存看看。
 




 2 安装php_redis.dll,配置php.ini  2.1 下载对应版本的php_redis.dll 
 
   下载地址:http://windows.php.net/downloa ... redis

  注:php7目录下有php7.dll的选择nts版本;有php7ts.dll的选择ts版本。 

  我下载的是upupw的扩展 http://php.upupw.net/news/4/5305.html

  解压后将php_redis.dll放在 wamp64\bin\php\php7.0.10\ext\ 目录下

  2.2 配置php.ini 

   在php.ini中加入 extension=php_redis.dll

   重启wamp后,查看phpinfo。





  看到以上信息说明redis扩展已经加上了.
 
 3 如果隐藏redis运行窗口

每次打开redis-server都会出现上面的那个窗口,关闭后redis就停止运行了,对于强迫症的我就不喜欢那个窗口一直停留在我的任务栏,所以找了一下如何隐藏的教程,在此分享一下.

参考地址:http://www.cnblogs.com/tujia/p/6076661.html​ 
 
 
 
  查看全部
如果你的程序中正好使用到了redis,而你的本地Windows开发环境中又未安装redis相关的扩展,那么这篇文章可以帮到你!
 
我本地的wamp版本是3.0.6,PHP版本是7.0.10。
 
安装redis扩展需要两个步骤:

1、下载window下的redis,访问 https://github.com/ServiceStack/redis-windows  即可下载
 
  1.1 下载redis,可以 直接下载最新版的redis redis-latest.zip

  1.2 解压到D或者其他盘下的目录即可,我解压到了F:/redis-latest/

  1.3 双击redis-server.exe,如下图所示,成功运行redis服务端了

QQ截图20171003120846.png

 
 1.4 双击 redis-cli.exe 可以打开客户端 尝试一下设置个缓存看看。
 
QQ截图20171003121126.png

 2 安装php_redis.dll,配置php.ini  2.1 下载对应版本的php_redis.dll 
 
   下载地址:http://windows.php.net/downloa ... redis

  注:php7目录下有php7.dll的选择nts版本;有php7ts.dll的选择ts版本。 

  我下载的是upupw的扩展 http://php.upupw.net/news/4/5305.html

  解压后将php_redis.dll放在 wamp64\bin\php\php7.0.10\ext\ 目录下

  2.2 配置php.ini 

   在php.ini中加入 extension=php_redis.dll

   重启wamp后,查看phpinfo。

QQ截图20171003121516.png

  看到以上信息说明redis扩展已经加上了.
 
 3 如果隐藏redis运行窗口

每次打开redis-server都会出现上面的那个窗口,关闭后redis就停止运行了,对于强迫症的我就不喜欢那个窗口一直停留在我的任务栏,所以找了一下如何隐藏的教程,在此分享一下.

参考地址:http://www.cnblogs.com/tujia/p/6076661.html​ 
 
 
 
 

MySQL中不会使用到索引的情况总结

数据库zkbhj 发表了文章 • 0 个评论 • 1533 次浏览 • 2017-09-27 15:58 • 来自相关话题

   众所周知,增加索引是提高查询速度的有效途径,但是很多时候,即使增加了索引,查询仍然不使用索引,这种情况严重影响性能,这里就简单总结几条MySQL不使用索引的情况
如果MySQL估计使用索引比全表扫描更慢,则不使用索引。例如,如果列key均匀分布在1和100之间,下面的查询使用索引就不是很好:select * from table_name where key>1 and key<90;如果使用MEMORY/HEAP表,并且where条件中不使用“=”进行索引列,那么不会用到索引,head表只有在“=”的条件下才会使用索引用or分隔开的条件,如果or前的条件中的列有索引,而后面的列没有索引,那么涉及到的索引都不会被用到,例如:select * from table_name where key1='a' or key2='b';如果在key1上有索引而在key2上没有索引,则该查询也不会走索引复合索引,如果索引列不是复合索引的第一部分,则不使用索引(即不符合最左前缀),例如,复合索引为(key1,key2),则查询select * from table_name where key2='b';将不会使用索引如果like是以‘%’开始的,则该列上的索引不会被使用。例如select * from table_name where key1 like '%a';该查询即使key1上存在索引,也不会被使用如果列为字符串,则where条件中必须将字符常量值加引号,否则即使该列上存在索引,也不会被使用。例如,select * from table_name where key1=1;如果key1列保存的是字符串,即使key1上有索引,也不会被使用。

    从上面可以看出,即使我们建立了索引,也不一定会被使用,那么我们如何知道我们索引的使用情况呢??在MySQL中,有Handler_read_key和Handler_read_rnd_key两个变量,如果Handler_read_key值很高而Handler_read_rnd_key的值很低,则表明索引经常不被使用,应该重新考虑建立索引。可以通过:show status like 'Handler_read%'来查看着连个参数的值。 查看全部
   众所周知,增加索引是提高查询速度的有效途径,但是很多时候,即使增加了索引,查询仍然不使用索引,这种情况严重影响性能,这里就简单总结几条MySQL不使用索引的情况
  1. 如果MySQL估计使用索引比全表扫描更慢,则不使用索引。例如,如果列key均匀分布在1和100之间,下面的查询使用索引就不是很好:select * from table_name where key>1 and key<90;
  2. 如果使用MEMORY/HEAP表,并且where条件中不使用“=”进行索引列,那么不会用到索引,head表只有在“=”的条件下才会使用索引
  3. 用or分隔开的条件,如果or前的条件中的列有索引,而后面的列没有索引,那么涉及到的索引都不会被用到,例如:select * from table_name where key1='a' or key2='b';如果在key1上有索引而在key2上没有索引,则该查询也不会走索引
  4. 复合索引,如果索引列不是复合索引的第一部分,则不使用索引(即不符合最左前缀),例如,复合索引为(key1,key2),则查询select * from table_name where key2='b';将不会使用索引
  5. 如果like是以‘%’开始的,则该列上的索引不会被使用。例如select * from table_name where key1 like '%a';该查询即使key1上存在索引,也不会被使用
  6. 如果列为字符串,则where条件中必须将字符常量值加引号,否则即使该列上存在索引,也不会被使用。例如,select * from table_name where key1=1;如果key1列保存的是字符串,即使key1上有索引,也不会被使用。


    从上面可以看出,即使我们建立了索引,也不一定会被使用,那么我们如何知道我们索引的使用情况呢??在MySQL中,有Handler_read_key和Handler_read_rnd_key两个变量,如果Handler_read_key值很高而Handler_read_rnd_key的值很低,则表明索引经常不被使用,应该重新考虑建立索引。可以通过:show status like 'Handler_read%'来查看着连个参数的值。