最近学习linux网络编程,linux下网络编程是基于socket。socket在百度百科上的解释很形象——
一、socket有3种类型:
1>流式套接字(SOCK_STREAM)——基于tcp协议(稳定性),连接型,保证数据传输正确性和顺序性。
2>数据报套接字(SOCK_DGRAM)——基于udp协议(实时性),无连接,数据通过相互独立的报文进行传输,无序的,不可靠,可能出现差错;
3>原始套接字(SOCK_RAW)——直接基于IP协议,主要用于新网络的测试等等。
二、socket网络地址:
网络地址存在于sockaddr之中,sockaddr是在头文件 /usr/include/bits/socket.h 中定义的,如下:
structsockaddr
{
__SOCKADDR_COMMON (sa_); /* Common data: address family and length.
charsa_data[14]; /* Address data. */
};
*sa_family:
协议族,采用AF_xxx的格式,AF_INET——> ip协议族;
* sa_data:
14字节的特定协议地址,包括了ip和端口号;
但是,实际应用当中,我们一般用到的时sockaddr_in,这是在/usr/include/netinet/in.h 中定义的sockaddr_in:
01./* Structure describing an Internet socket address. */
02.struct sockaddr_in
03.{
04.__SOCKADDR_COMMON (sin_); /* 协议族 */
05.in_port_t sin_port; /* Port number. 端口号 */
06.struct in_addr sin_addr; /* Internet address. IP地址 */
07.
08./* Pad to size of `struct sockaddr'. 用于填充的0字节 */
09.unsigned char sin_zero[sizeof (struct sockaddr) -
10.__SOCKADDR_COMMON_SIZE -
11.sizeof (in_port_t) -
12.sizeof (struct in_addr)];
13.};
14.
15./* Internet address. */
16.typedef uint32_t in_addr_t;
17.struct in_addr
18.{
19.in_addr_t s_addr;
20.};
为什么会使用sockaddr_in?原因是sockaddr_in更加的简便。
这里借鉴了优秀文章来帮助理解sockaddr和sockaddr_in之间的区别。https://blog.youkuaiyun.com/joeblackzqq/article/details/8258693(优秀文章链接)
按我个人理解,两者无区别,首先sizeof(sockaddr)=sizeof(sockaddr_in),两个结构体需要分配的空间大小一样,进入具体的头文件中去观察两个结构体,通过对比两段代码,我发表一点点我自己的看法(如有错误望予斧正):sockaddr和sockaddr_in都将sock连接需要的填充的数据封装起来了,前者中的
__SOCKADDR_COMMON (sa_);定义了协议族
和后者的
__SOCKADDR_COMMON (sin_);
是一样的操作,都是装了协议族,用于指明地址属于哪种协议族;
而在地址部分,sockaddr 将地址信息全部装入
charsa_data[14];
sockaddr_in将地址分为了三部分:端口,地址和8位未使用部分,是操作更加清晰直观;
而在sockaddr_in之中又嵌套了一个struct in_addr我们来看一看这个struct in_addr 的具体
struct in_addr
{
in_addr_t s_addr; //in_addr_t 实质时long类型
};
注释中我解释到了,in_addr_t实质时long,也就是说这个结构体使用了32位整数来记录了IP地址;
然而习惯上使用的IP地址是用4个段来记录的(xxx.xxx.x.x);所以如果不经过转化,我可能完全不知道你的32bit整数讲的到底是什么。。。。所以此时我们需要将它进行转换,让32位整数能变成我们所熟知的形式,这里就要用到两个函数了:
int inet_aton(const char *cp,struct in_addr *inp);
char *inet_ntoa(struct in_addr in)
其中,a代表了ascii码,n代表network。
也就是说,这两个函数将IP地址从net能够识别的long转变为易读懂的string,反之亦然。
三、数据传输细节:
数据传输需要注意字节序,不同的CPU使用的字节序是不同的,这是需要字节序转换,网络上传输的数据被规定为大端数据(以高位结尾),不同的CPU将会根据自己的实际情况转换字序,使用到一下函数:
htons();(host to network short)——ntohs();(network to host short)
htonl();(host to network long)——ntohl();(network to host long)
四、具体编程实现:
socket分为两部分,一是主机/服务器(server),二是客户端(client)。
首先主要介绍一下主机部分。客户端和主机部分大同小异,只有一小点点的不同,先附上我自己写的简单的c源码(由于使用vim就用英语注释了,英语比较烂,勉强着看吧):
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#define portnum 34 //define netport number
int main (int argc,char *argv[])
{
int sockfd,new_fd; //define socketfile po