socket网络编程快速上手(一)
工作以来,写了很多socket相关的代码。磕磕碰碰,走了很多弯路,也积累了一些东西,今天正好整理一下。为了证明不是从书上抄来的,逻辑会有点乱(借口,呵呵)!知识点的介绍也不会像书上说的那么详细和精准,毕竟个人水平也就这样了。当然,主要还是以上手为主,不过分剖析原理性内容。一些陌生的函数要用到的头文件,使用man查看一下就能解决了。既然该文的名称为“快速上手”,那个人认为下述内容都不存在水分,都是必须要掌握的,一点都不能急躁!
一、socket连接流程:
对于程序员来说,开始的时候只会把socket编程当成一个工具,尽快上手,尽快解决战斗。于是乎最关心的就是socket那些函数的调用顺序,那就先给出UDP/TCP的流程图(从《UNIX网络编程》)吧:
有了流程图,再找一些资料,就很容易写出下面这样的代码(以TCP为例):
服务器程序:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h> #define PORT 1234
#define BACKLOG 5
#define MAXDATASIZE 1000 int main()
{
int listenfd, connectfd;
struct sockaddr_in server;
struct sockaddr_in client;
socklen_t addrlen;
char szbuf[MAXDATASIZE] = {}; if ((listenfd = socket(AF_INET, SOCK_STREAM, )) == -)
{
perror("Creating socket failed.");
exit();
} bzero(&server, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(PORT);
server.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(listenfd, (struct sockaddr *)&server, \
sizeof (server)) == -)
{
perror("Bind()error.");
exit();
}
if (listen(listenfd, BACKLOG) == -)
{
perror("listen()error\n");
exit();
} addrlen = sizeof(client);
if ((connectfd = accept(listenfd, \
(struct sockaddr*)&client, &addrlen)) == -)
{
perror("accept()error\n");
exit();
}
printf("You got a connection from cient's ip is %s, \
prot is %d\n", inet_ntoa(client.sin_addr), \
htons(client.sin_port)); memset(szbuf, 'a', sizeof(szbuf));
while ()
{
send(connectfd, szbuf, sizeof(szbuf), );
} close(connectfd);
close(listenfd); return ;
}
TCP服务器代码
客户端程序:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h> #define PORT 1234
#define MAXDATASIZE 1000 int main(int argc, char *argv[])
{
int sockfd, num;
char buf[MAXDATASIZE + ] = {};
struct sockaddr_in server; if (argc != )
{
printf("Usage:%s <IP Address>\n", argv[]);
exit();
} if ((sockfd=socket(AF_INET, SOCK_STREAM, )) == -)
{
printf("socket()error\n");
exit();
}
bzero(&server, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(PORT);
server.sin_addr.s_addr = inet_addr(argv[]);
if (connect(sockfd, (struct sockaddr *)&server, \
sizeof(server)) == -)
{
printf("connect()error\n");
exit();
} while ()
{
memset(buf, , sizeof(buf));
if ((num = recv(sockfd, buf, \ MAXDATASIZE,)) == -)
{
printf("recv() error\n");
exit();
}
buf[num - ]='\0';
printf("Server Message: %s\n",buf);
} close(sockfd); return ;
}
TCP客户端代码
二、基础知识不能忽略:
有了这些代码,顺利地话一次性就能编过,也都正常跑起来。有些人就认为已经学会了socket,哈哈,当时我就是这样!更多的情况是,我们参考资料所用的运行环境往往和我们自己电脑不一致,就需要自己来修改代码。然后就会发现代码里出现AF_INET、htons、inet_addr、struct sockaddr……这些完全不知道干什么的东西。更有甚者,那人还不知道使用man,不喜欢看书,那就只能剩下猜谜了。运气好一点,最后能瞎猫碰见死老鼠,要是运气差一点,估计头就大了。
Socket编程的关键函数自然不能不懂,但基础知识同样重要,不理解基础知识到最后只会一团乱。书上写了好几页,其实内容也并不是太多。耐住性子弄明白,后面就会没那么添堵了。下面整理了三点:
1.网络选择:
一般编程而言,socket(特指socket这个系统调用)函数的第一个参数都使用AF_INET,表示是在IPv4因特网域。也可以改变为其它的值,用于IPv6网络等。
第二个参数为套接字类型,TCP链接使用SOCK_STREAM,UDP链接使用SOCK_DGRAM,基本就这两个。如果想写一个类似于ping这样直接访问网络层的一个程序,那就要使用其他类型了。
2.网络字节序:
和1不同,1设置不对你就别想正确建立链接,但是字节序问题跳过去,可能开始并不会造成太大的影响。所以字节序转换往往就被人忽略了。
字节序问题是由“大端系统”和“小端系统”这两者的差异造成的。网络通信时,我们并不知道对端系统采用什么样的存储方式,这个时候,就引入了一个网络字节序的概念。网络协议统一使用大端字节序,不管什么类型的主机,在通过协议栈发送和接收时就知道数据需不需要转换了。
另外,并不是所有数据都需要转换的。如果采用字节流(没有类似int,short这种多字节格式化数据类型)方式进行通信,无需字节序转换,因为数据都是有序的,不管在什么系统中顺序都是一致的。
对于字节序的转换关系,建议还是自己画图理解一下,光动脑子想不是太容易理解。
字节序转换函数不多,只有四个,并且很容易记忆,它们分别是:
htonl、htons、ntohl、ntohs
其中h表示“主机(host)字节序”,n表示“网络(network)字节序”,l表示“长(long)整数(4个字节)”,s表示“短(short)整数(2个字节)”,to表示“从谁向谁转换”……比Stevens的原著都详细,这样再无法记住,还真是没什么办法了。
有时也会涉及到更多字节的字节序转换,比如long long类型(8个字节),其实,用上述函数也是有办法能够完成的。
3.网络地址转换:
实际使用时,IPv4和IPv6使用的地址结构是不同的。IPv4使用struct sockaddr_in,而后者使用struct sockaddr_in。但不同网络(比如IPv4、IPv6)编程使用的socket接口函数都是一样的,这是怎么做到的呢?答案是接口函数都使用了统一的地址结构struct sockaddr。这些地址结构的具体定义可以使用man查看,可以发现,这些结构的框架是相同的,在正确地使用方法下地址结构间可以相互强制转换。它们都符合
struct sockaddr {
unsigned short sa_family; /* address family, AF_xxx */
char sa_data[14]; /* 14 bytes of protocol address */
};
这种形式。因此,不管是在哪个网域下调用socket函数,只要将有差异的地址结构使用struct sockaddr进行强制类型转换就能实现接口的统一了。
接着,针对具体的IPv4编程中地址的赋值详细说明一下。上面提到,IPv4使用的地址结构及其定义为
struct sockaddr_in {
short sin_family; /* Address family */
unsigned short sin_port; /* Port number */
struct in_addr sin_addr; /* Internet address */
unsigned char sin_zero[8]; /* Same size as struct sockaddr */
};
在linux下:
in_addr结构
typedef struct in_addr{
unsigned long s_addr;
};
上面说到协议栈都需要使用网络字节序,sin_family自然是等于AF_INET(协议栈提供的值,不需要考虑字节序了)。sin_port(端口)是双字节变量,需要使用htons转字节序。sin_addr.s_addr(网络地址),它不是使用192.168.1.2这种点分十进制形式,而是需要将点分十进制地址转换成网络字节序的一个整数。这时就需要对192.168.1.2这种地址进行转换,系统提供了inet_addr函数(只能用于IPv4地址转换)。sin_addr.s_addr赋值为inet_addr(192.168.1.2)。能转过去就一定能变回来,系统提供了inet_ntoa函数进行逆变换。inet_addr和inet_ntoa只能适用于IPv4地址转换,系统还提供了inet_ntop和inet_pton函数,后两个函数兼容IPv4和IPv6,使用起来更加方便。
掌握这些基础知识之后写起代码来就不一定再需要拿着参考书来看了,这也是为一个健壮、稳定的网络程序做了热身运动,这可是必不可少的。
本来想一篇就写完的,写到这发现内容还真没想得那么少,在下一章会讲述网络程序的一些细节问题,也都是必不可少的。
socket网络编程快速上手(一)的更多相关文章
- socket网络编程快速上手(二)——细节问题(5)(完结篇)
6.Connect的使用方式 前面提到,connect发生EINTR错误时,是不能重新启动的.那怎么办呢,是关闭套接字还是直接退出进程呢?如果EINTR前,三次握手已经发起,我们当然希望链路就此已经建 ...
- socket网络编程快速上手(二)——细节问题(4)
5.慢系统调用及EINTR 还记得前面readn和writen函数么?里面有个EINTR,现在就来谈谈这个,这个很重要. Linux世界有个叫信号的东西,感觉他就像一位隐士,很少遇到他,而他又无处不在 ...
- socket网络编程快速上手(二)——细节问题(1)
三.细节问题一个也不能少 Socket编程说简单也简单,程序很容易就能跑起来,说麻烦还真是麻烦,程序动不动就出问题.记得刚开始写网络代码的时候,那真是令人抓狂的经历,问题一个套一个,一会服务器起不来了 ...
- socket网络编程快速上手(二)——细节问题(3)
3.SIGPIPE问题 人怕牺牲,我们写的程序也一样,人有死不瞑目,程序又何尝不是?程序跑着跑着,突然就崩掉了.好一点的牺牲前告诉你些打印,差点的也能用core文件等一些手段查出死在哪了,最惨不忍睹的 ...
- socket网络编程快速上手(二)——细节问题(2)
2.TCP数据包接收问题 对初学者来说,很多都会认为:客户端与服务器最终的打印数据接收或者发送条数都该是一致的,1000条发送打印,1000条接收打印,长度都为1000.但是,事实上并不是这样,发送打 ...
- Java网络编程快速上手(SE基础)
参考资料:百度百科TCP协议 本文涉及Java IO流.异常的知识,可参考我的另外的博客 一文简述Java IO 一文简述JAVA内部类和异常 1.概述 计算机网络相关知识: OSI七层模型 一个报文 ...
- SOCKET网络编程5
SOCKET网络编程快速上手(二)——细节问题(5)(完结篇) 6.Connect的使用方式 前面提到,connect发生EINTR错误时,是不能重新启动的.那怎么办呢,是关闭套接字还是直接退出进程呢 ...
- SOCKET网络编程细节问题(4)
SOCKET网络编程快速上手(二)——细节问题(4) 5.慢系统调用及EINTR 还记得前面readn和writen函数么?里面有个EINTR,现在就来谈谈这个,这个很重要. Linux世界有个叫信号 ...
- SOCKET网络编程细节问题3
SOCKET网络编程快速上手(二)——细节问题(3) 3.SIGPIPE问题 人怕牺牲,我们写的程序也一样,人有死不瞑目,程序又何尝不是?程序跑着跑着,突然就崩掉了.好一点的牺牲前告诉你些打印,差点的 ...
随机推荐
- crawler_爬虫开发的准备工作【工具】
俗话说工欲善其事必先利其器,做java网络爬虫开发分析网页的分析工具,抓包工具比不可少,一下是个人常用的几个工具. 1.firefox低版本是为了支持httpwather , ie各个版本都支持htt ...
- 转载Worktile 技术架构概要
Worktile 技术架构概要 其实早就该写这篇博客了,一直说忙于工作没有时间,其实时间挤挤总会有的,可能就是因为懒吧!从2013年11月一直拖到现在,今天就简单谈谈 Worktile 的技术架构吧 ...
- TextView随键盘弹出上移高度
很多时候我们都在为键盘遮挡了原本就不大的屏幕时而烦恼,特别是当用户处于编辑状态时,键盘下面的内容就看不见了,用户只能处于盲打状态了.现在有一种简单的解决办法,基本思路就是,添加通知.一直监听键盘事件, ...
- 编译预处理 -- 带参数的宏定义--【sky原创】
原文:编译预处理 -- 带参数的宏定义--[sky原创] 如有转载请注明出处 编译预处理 -- 带参数的宏定义 前面为输出文件,后面为输入文件 gcc -E -o test.i test.c ...
- TML5安全:CORS(跨域资源共享)简介
来源:http://blog.csdn.net/hfahe/article/details/7730944 前言:像CORS对于现代前端这么重要的技术在国内基本上居然很少有人使用和提及,在百度或者Go ...
- 从一道数学题弹程序员的思维:数学题,求证:(a+b%c)%c=(a+b)%c
在学校论坛看到这道题目,全忘了的感觉. 如果你是高中的,那我觉得你完全没问题.但是,在这个博客园的圈子,觉得全部人都是程(ban)序(zhuan)员(gong)相关的人员,解决这个问题有点难度,毕竟, ...
- web学习总结之布局
浅谈web布局中的“float”属性 对于刚学习web前端的同学,布局和排版都是一个难点.虽然我们能够通过各种调试实现我们希望得到的页面效果,但是如果不把其中的道理弄清楚的话,在下次布局的时候,也无法 ...
- 快速构建Windows 8风格应用12-SearchContract概述及原理
原文:快速构建Windows 8风格应用12-SearchContract概述及原理 本篇博文主要介绍Search Contract概述.Search Contract面板结构剖析.Search Co ...
- SQL点滴4—筛选数据列的类型,字段大小,是否可为空,是否是主键,约束等等信息
原文:SQL点滴4-筛选数据列的类型,字段大小,是否可为空,是否是主键,约束等等信息 项目需要将Access数据库中的数据导入到SQL Server中,需要检验导入后的数据完整性,数据值是否正确.我们 ...
- 4GB内存原32位系统(x86)取舍问题,显卡共享内存Win8.1完全不用担心
情景:集成显卡 配置: 4G显示3.25GB 此时系统自动将用不到的系统完全共享给显卡(768MB而不是256): 看显卡适配器信息,完全共享给了显卡 解说:上图总可用图形内存 = 图2中备用 + 硬 ...