linux网络编程

网络互联促成TCP/IP协议的产生,它包括 用来检测网络传输中差错的传输控制协议TCP,专门负责对不同网络进行互联的互联网协议IP
TCP/IP协议成为Internet中的世界语。
两类重要的体系结构: OSI与 TCP/IP

OSI开放系统互联模型

应用层、表示层、会话层、传输层、网络层、数据链路层、物理层,它是个理想化的模型。

TCP/IP一共有四层

应用层 (Telnet、FTP、HTTP、DNS、SMTP等)
传输层 (TCP和UDP)
网络层 (IP、ICMP、IGMP)
网络接口和物理层 (以太网、令牌环网、FDDI等)


TCP与UDP
同为传输层协议,TCP有连接,可靠,UDP无连接,不可靠

TCP(传输控制协议):是一种面向连接的传输层协议,它能提供高可靠性通信(数据无误、数据无丢失、数据无失序、数据无重复到达的通信)
适用情况:
1.对于传输质量要求较高,以及传输大量数据的通信。
2.在需要可靠数据传输的场合,通常使用TCP协议
3.MSN/qq等几十通信软件的用户登录账户管理相关的功能通常采用TCP协议。
UDP协议:用户数据报协议,是不可靠的无连接的协议,在数据发送前,因为不需要进行连接,所以可以进行高效率的数据传输。

适用情况:
1.发送小尺寸数据
2.在接收到数据,给出应答交困难的网络中使用(无线网络)
3.适用于广播/组播通信
4.点对点文本通信以及音视频通信
5.流媒体、VOD、IPTV等
Socket简介:

是一个编程接口,是一种特殊的文件描述符,并不仅限于TCP/IP协议
在OSI模型中,主要位于会话层和传输层之间
BSD socket(伯克利套接字)

套接字的分类:
1.流式套接字(SOCK_STREAM)提供了一个面向连接、可靠的数据传输服务,数据无差错、无重复的发送且按发送顺序接收。
内设置了流量控制,避免数据流淹没慢的接收方。数据被看做是字节流,无长度限制。
2.数据报套接字(SOCK_DGRAM)提供无连接服务。数据包以独立数据包的形式被发送。不提供无差错保证。
3.原始套接字(SOCK_RAW)可以对较低层次协议如 IP、ICMP直接访问

IP地址是Internet中主机的标识
IP地址为32位(ipv4)或128位(ipv6)
/***************地址IP的转换***********************************/

三个函数:
1.inet_aton()
所需头文件:
#include <arpa/inet.h>
#include <sys/socket.h>   
#include <netinet/in.h>
功能描述 :将strptr所指的字符串转换成32位的网络字节序二进制值
int inet_aton(const char * strptr,struct in_addr * addrptr);

参数描述: 1 输入参数string包含ASCII表示的IP地址。
 2 输出参数addrptr是将要用新的IP地址更新的结构。

  这个结构体会在下面介绍,它是internet协议地质结构。

返回值:  如果这个函数成功,函数的返回值非零,如果输入地址不正确则会返回零。使用这个函数并没有错误码存放在errno中,所以他的值会被忽略。

2.
in_addr_t inet_addr(const char * strptr)

函数功能和上面的函数一样,但是这个函数有个缺点,它区分不开 255.255.255.255和-1
3.
char * inet_ntoa(struct in_addr inaddr);
功能: 将32为网络字节序二进制地址转换成点分十进制的字符串。


/******************************字节序**************************************************/
因为不同类型的CPU的主机中,内存存储多字节正数序列有两种方法,成为主机字节序(HBO)
小端序: 低序字节存储在低地址 Intel,AMD采用
大端序: 高序字节存储在低地址 ARM,Motorola等采用
网络中传输的数据必须按照网络字节序 :大端序
所以在大部分PC机上,当应用进程将整数送入socket前,需要转换成网络字节序;当应用进程从socket取出整数后,
要转换成本地字节序(主机字节序)。
为了避免不同类别主机之间在数据交换时由于字节序的不同而导致差错,引入了网络字节序。

主机字节序到网络字节序转换函数:

u_long htonl(u_long hostlong);
u_short htons(u_short short);

网络字节序转换成主机字节序函数:
u_long ntohl(u_long hostlong);
u_short ntohs(u_short short);


/**********************************网络地址相关的数据结构******************************************************/
通用地址结构
struct sockaddr
{
u_short sa_family; //地址族,AF_xxx
char sa_data[14];//14字节协议地址
};

Internet协议地址结构
struct sockaddr_in
{
u_shrot sin_family; //地址族,AF_INET,2bytes
u_short sin_port; //端口 ,2 bytes
struct in_addr sin_addr; //IPV4地址,4bytes
char sin_zero[8]; // 8bytes unused,作为填充
};

以上的结构体封装了另外一个IPV4的地址结构体,如下:
IPV4地质结构
struct in_addr
{
in_addr_t s_addr; //32位网络地址
};

 


/**********************************以上是网络传输所需要的前提准备************************************************/

 

网络编程相关的API
1.创建套接字
函数原型: socket(int domain,int type,int protocol)
函数参数: domain是地址族
PF_INET//internet协议
PF_UNIX//unix internal协议
PF_NS //Xerox NS 协议
PF_IMPLINK //Interface Message协议
type 套接字类型
SOCK_STREAM 流式套接字
SOCK_DGRAM 数据报套接字
SOCK_RAW 原始套接字
protocol指定应用程序所使用的通信协议。此参数可以指定单个协议系列中的不同传输协议。
在Internet通讯域中,此参数一般取值为0,系统会根据套接字的类型决定应使用的传输层协议。

函数返回值: 成功就返回新创建的套接字的描述符,如果失败就返回INVALID_SOCKET error(-1)


2.绑定套接字

int bind (int sockfd,struct sockaddr *my_addr,int addrlen);

所需头文件: #include <sys/types.h> #include <sys/socket.h>
函数参数 :
sockfd: socket调用返回的文件描述符
my_addr:描述本机的端口和IP地址
addrlen :sockaddr地址结构的长度

返回值: 成功 0,失败 -1;
注意,如果定义的my_addr是 sockaddr_in的结构体类型的话,需要强制转换成通用地址结构。

既然要绑定的话,那么在进行绑定前肯定要告知我需要绑定什么地址,什么端口吧?所以需要填充addr的信息
填充addr的步骤如下:
1.定义一个struct_sockaddr_in类型的变量并清空
struct sockaddr_in myaddr;
memset(&myaddr,0,sizeof(myaddr));
2.填充地址信息
myaddr.sin_family = PE_INET;
myaddr.sin_port =htons(8080); //需要将主机字节序转换成网络字节序
myaddr.sin_addr.s_addr = inet_addr("192.168.1.111"); //直接就转换成网络字节序喽
3.将该结构体类型的变量强制转换成struct socketaddr类型(通用网络地址结构)
bind(listenfd,(struct sockaddr *)(&myaddr),sizeof(myaddr));

这样就绑定了地址 :192.168.1.111 端口:8080

3.监听套接字
int listen(int sockfd,int backlog)

函数参数:
sockfd:监听连接的套接字
backlog:指定了正在等待连接的最大队列长度,它的作用在于处理可能同时出现的几个连接请求。
DoS(拒绝服务)攻击利用了这个漏洞,非法的连接占用了全部的连接数,造成正常的连接请求被拒绝。
返回值: 0或-1;

4.int accept(int sockfd,struct sockaddr * addr,socklen_t *addrlen);

函数参数:
sockfd:监听套接字
addr:对方的地址
addrlen:地址长度 注意,在函数中是取地址,所以在调用该函数前要先定义addrlen,然后取地址。
函数返回值:
如果成功则返回接收到的套接字,即客户端发给服务器端的文件描述符,失败 -1;

5.int connect(int sockfd,struct sockaddr *serv_addr,int addrlen);

函数参数:
sockfd:socket返回的文件描述符
serv_addr:服务器端的地址信息
addrlen:serv_addr的长度
返回值 : 0 或 -1;


6.发送函数
ssize_t send (intsocket,const void *buffer ,size_t length, int flags);

函数参数:
buffer 发送缓冲区首地址
length :发送字节数
flags:发送方式,通常为0
返回值:成功,实际发送的字节数,失败 -1并设置errno值;

 

7.接收函数
ssize_t recv(int socket , const void * buffer ,size_t length,int falgs);


函数返回值: 成功,实际接收到的字节数,失败 -1并设置errno

有时候用read、write代替recv、send,看个人爱好。


8.UDP协议使用的发送和接收函数

ssize_t sendto(intsocket, void *message ,size_t length, int flags,struct sockaddr * dest_addr,socklen_t dest_len);

比较上面的send多加了2个参数:
dest_addr //目标地址
dest_len //目标地址长度
因为UDP是无连接的协议,所以在发送的时候指定IP地址。

 

9.套接字的关闭函数

int close(int sockfd);

关闭双向通信


int shutdown(int sockfd,int howto);
TCP连接是双向的,当我们使用close时,会把读写通道都关闭,有时候我们希望只关闭一个方向,这时候我们就可以使用shutdown。

针对不同的howto,系统会采取不同的关闭方式。

howto = 0 关闭读通道
howto = 1 关闭写通道
howto = 3 关闭读写通道,和close一样了。

 

例子:

server端
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>

// int socket(int domain, int type, int protocol);
//

int main()
{
int sockfd, clientfd;
struct sockaddr_in servaddr, clientaddr;
char buf[1024];
socklen_t len;
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("socket");
exit(1);
}

printf("socket = %d\n", sockfd);

memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(8888);
servaddr.sin_addr.s_addr = inet_addr("192.168.1.233");

if(bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)
{
perror("bind");
exit(1);
}

listen(sockfd, 5);

while(1)
{
memset(&clientaddr, 0, sizeof(clientaddr));
memset(buf, 0, sizeof(buf));
len = sizeof(clientaddr);
if((clientfd = accept(sockfd, (struct sockaddr *)&clientaddr, &len)) < 0)
{
perror("accept");
exit(1);
}

printf("client ip=%s, port=%d, clientfd = %d\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port), clientfd);
recv(clientfd, buf, sizeof(buf), 0);
printf("buf = %s\n", buf);
send(clientfd, buf, strlen(buf), 0);

close(clientfd);

}

return 0;
}

/**************client端******************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>

// int socket(int domain, int type, int protocol);
//

int main()
{
int sockfd;
struct sockaddr_in servaddr;
char buf[1024] = "hello world";
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("socket");
exit(1);
}

printf("socket = %d\n", sockfd);

memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(8888);
servaddr.sin_addr.s_addr = inet_addr("192.168.1.233");

connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));

send(sockfd, buf, strlen(buf), 0);
memset(buf, 0, sizeof(buf));
recv(sockfd, buf, sizeof(buf), 0);
printf("buf = %s\n", buf);

close(sockfd);

return 0;
}

 

posted on 2012-07-06 20:28  孟浩依然  阅读(657)  评论(0编辑  收藏  举报