Linux 套接字编程

一、网络层结构 网络层应用程序通过BSD套接字进行数据传输,它下面是INET套接字层,管理TCP和UDP协议

BSD套接字接口是BSD的进程间通信方式,不仅支持各种形式的网络应用,而且是进程间通信的机制

1)stream (数据流)提供两个方向的数据传输,保证传输过程数据不丢失、破坏或重复。对应TCP协议支持

2)datagram(数据报)支持两个方向上的数据传输,不提供消息到达保证,由UDP支持,一般是面向事务处理的

3)Raw原始套接字,允许进程直接访问底层协议

4)Reliable Delivered Message 可靠数据报传输

5)Sequenced Packets 顺序数据报,像数据流但是数据包大小确定

6)Packet 允许进程在设备层直接访问Packet

 

 

二.套接字地址

1.通用套接字地址结构

#include<sys/socket.h>

struct socketaddr

{

 unsigned short int sa_family;// 地址类型,AF_XXX如TCP/IP地址类型为AF_INET

 unsigned char sa_data[14];//14字节的协议地址

};

2.TCP/IP套接字地址结构sockaddr_in

#include<netinet/in.h>

struct in_addr

{

 int_addr_t s_addr;

};

struct sockaddr_in

{

 _SOCKADDR_COMMON(sin__;

in_port_t sin_port;/**端口号**/ 网络字节存储

struct in_addr sin_addr;/*Internet 地址*/网络字节存储

/**填充部分 未用**/通常是设为0

unsigned char sin_zero[sizeof(struct sockaddr)-_SOCKADDR_COMMON_SIZE-sizeof(int_port_t)-sizeof(struct in_addr)];

};

地址引用:servaddr.sin_addr 引用的结构类型 struct in_addr数据

              servaddr.sin_addr.s_addr 引用的是整形数据

2.设置IP

#include<sys/socket.h>

#include<netinet/in.h>

#include<arpa/inet.h>

int inet_aton(char *cp,struct in_arrd *inp);将字符形式的IP转换成二进制IP,存储在inp中,成功返回1,否则返0

unsigned long int inet_addr(char *cp);不能处理广播,错误返回长岭INADDR_NONE

char * inet_ntoa(strunct in_addr in); 将32位二进制形式的IP地址转换成数字点形式的IP,结果在函数中返回

 

3.字节顺序

1)大端模式(Big_endian):字数据的高字节存储在低地址中,而字数据的低字节则存放在高地址中。

2)小端模式(Little_endian):字数据的高字节存储在高地址中,而字数据的低字节则存放在低地址中。

利用union检测系统的字节存储模式

union 型数据所占的空间等于其最大的成员所占的空间。对union 型的成员的存取都是相对于该联合体基地址的偏移量为0 处开始,也就是联合体的访问不论对哪个变量的存取都是从union 的首地址位置开始。

int checkSystem( )
{
   union check
   {
      int i;
      char ch;
   } c;
   c.i = 1;
   return (c.ch ==1);
}

 

而网络协议中数据采用统一的网络字节顺序,全部采用big-endian方式

字节转换函数

#include<netinet/in.h>

unsigned long int htonl(unsigned long int hostlong);//主机字节顺序转换成网络字节顺序

unsigned  short int htons(unsigned sjort int hostshort);

unsigned long int ntohl(unsigned long int netlong);//网络字节顺序转换成主机字节顺序

unsigned  short int ntohs(unsigned sjort int netshort)

 

4.字节处理函数

#include<strings.h>

void bzero(void *s,int n);将s制定内存的前n个字节设成0。套接字地址清零bzero(&servaddr,sizeof(servaddr))

void bcopy(void *src,void *dest,int n) ;从src复制n个字节内容到dest指定内存

int bcmp(void *s1,void *s2,int n)比较s1和s2的前n个字符,相同返回0,否则返回非0

 

#include<string.h>

void *memset(void *s,int c,size_t n);s指定内存区域前n个字节设成参数c

void *memcpy(void *dest,void *src,size_t n);

int memcmp(void *s1,void *s2,size_t n);比较s1和s2指定区域的前n个字节内容,相同返回0,否则返回非0

 

三、套接字函数

1.socket函数 创建一个套接字描述符,成功返回套接字描述符,否则返回-1

#include<sys/types.h>

#include<sys/socket.h>

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

domain:要创建的套接字协议族 

    AF_UNIX  UNIX域协议族,本机进程通信使用

    AF_INET Internet 协议族

    AF_ISO ISO 协议族

type:套接字类型

    SOCK_STREAM 流套接字TCP

   SOCK_DGRAM  数据报套接字

  SOCK_RAM  原始套接字

protocol:通常设为0 默认

2.connet连接

 #include<sys/types.h>

#include<socket.h>

int connect(sockfd,struct sockaddr * servaddr,int addrlen);servaddr 远程服务器的套接字地址,addrlen套接字地址长度,成功返回0,失败返回-1

3.bind函数

 #include<sys/types.h>

#include<socket.h>

int bind(int sockfd,struct sockaddr *myaddr,int addrlen);myaddr 本地地址,addrlen套接字地址结构的长度,bind成功返回0否则返回-1

4.listen将一个套接字装换成倾听套接字

 #include<sys/types.h>

#include<socket.h>

int listen(int sockfd,int backlog)sockfd 指定要转换的套接字描述符,backlog设置请求队列的最大长度,成功返回0,失败返回-1

要建立一个倾听套接字,就必须调用socket创建主动套接字,调用bind绑定服务器套接字地址,调用listen进行转换

 

5.accept

 #include<sys/types.h>

#include<socket.h>

int accept(int sockfd,struct sockaddr *addr,int*addrlen) 从倾听套接字的完成队列中接受一个连接

函数返回一个新的套接字描述符标志接受的连接;addr指向结构变量中客户机的地址;参数addrlen指向整形变量中存储客户机地址的长度

服务器程序:

/***ex8-2server.c***/
#include<time.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<netdb.h>
#include<string.h>

#define LISTENQ 5
#define MAXLINE 512

int main()
{
int listenfd,connfd;
socklen_t len;
struct sockaddr_in servaddr,cliaddr;
char buff[MAXLINE];
time_t ticks;
listenfd=socket(AF_INET,SOCK_STREAM,0);
if(listenfd<0)
{
printf("Socket created failed.\n");
return -1;
}
servaddr.sin_family=AF_INET;
servaddr.sin_port=htons(6666);
servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
if(bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr))<0)
{
printf("bind failed.\n");
return -1;
}
printf("listening....\n");
listen(listenfd,LISTENQ);
while(1)
{
len=sizeof(cliaddr);
connfd=accept(listenfd,(struct sockaddr *)&cliaddr,&len);
printf("connet from %s,port %d\n",inet_ntoa(cliaddr.sin_addr.s_addr),ntohs(cliaddr.sin_port));

ticks=times(NULL);
sprintf(buff,"%.24s\r\n",ctime(&ticks));
write(connfd,buff,strlen(buff));
close(connfd);
}
}

 

客户端程序

/***ex8-2client.c**/
#include<stdio.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<netdb.h>
#define MAXBUFFERSIZE 256
#define PORT 6666
#define HOST_ADDR "127.0.0.1"

int main(int argc,char *argv[])
{
int sockfd,n;
char revbuff[MAXBUFFERSIZE];
struct sockaddr_in servaddr;
sockfd=socket(AF_INET,SOCK_STREAM,0);
if(sockfd<0)
{
printf("Socket created failed.\n");
return -1;
}
servaddr.sin_family=AF_INET;
servaddr.sin_port=htons(6666);
servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
if(connect(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr))<0)
{
printf("Connect failed.\n");
return -1;
}
while((n=read(sockfd,revbuff,MAXBUFFERSIZE))>0)
{
revbuff[n]=0;
fputs(revbuff,stdout);
}
if(n<0)
{
printf("read failed.\n");
return -2;
}
return 0;
}

 

6.getsockname

int getsockname(int socket,(socket sockaddr*)address,socklen_t *address_len) 获取由socket给出的套接字的本地绑定名,成功返回0,并 将地址存在address,超出部分被截断,长度存储在address_len

失败返回-1

/**ex8-3.c***/
#include<sys/socket.h>
#include<sys/time.h>
#include<netinet/in.h>
#include<netdb.h>
#include<stdio.h>

int main()
{
int sockfd;
socklen_t len;
struct sockaddr_in addr,raddr;
sockfd=socket(AF_INET,SOCK_STREAM,0);
if(sockfd<0)
{
printf("createsocketfailed!\n");
return -1;
}
addr.sin_family=AF_INET;
addr.sin_port=htons(0);
addr.sin_addr.s_addr=htonl(INADDR_ANY);
if(bind(sockfd,(struct sockaddr *) &addr,sizeof(addr))<0)
{
printf("bind socket failed\n");
return -2;
}
len=sizeof(raddr);
if(getsockname(sockfd,(struct sockaddr *)&raddr,&len)<0)
{
printf("getsockname failed.\n");
return -3;
}
printf("bound name=%s,port= %d,\n",ntohl(raddr.sin_addr.s_addr),ntohs(raddr.sin_port));
return 0;
}

7.getpeername

int getpeername(int socket,(socket sockaddr*)address,socklen_t *address_len)返回与socket连接的那个套接字的地址,成功返回0,错误返回-1.

8.send 和recv函数:可以用来读取发送带外数据

#include<sys/socket.h>

ssize_t send(int socket,const void *buffer,size_t length,int flags);

ssize_t recv(int socket,void *buffer,size_t length,int flags);

flags

  send

     MSG_OOB:带外数据,流套接字特有的,正常的数据流之外发送的

     MSG_DONTROUTE :不在消息中包含路由信息

recv 

    MSG_OOB 带外数据

    MSG_PEEK 窥视数据但不读取它们

   MSG_WAITALL 请求函数阻塞直至所请求的数据全部收到

成功返回字节数,失败返回-1

 

9数据报套接字操作:将数据集中为一个包,为每个包单独指定目的地址,并且每个包独立进行通信,不允许有listen和accept函数

#include<sys/socket.h>

int recvfrom(int socket,void *buffer,size_t size,int flags,struct sockaddr *from,size_t *addrlen);

int sendto(int socket,void *buffer,size_t size,int flags,struct sockaddr *from,size_t *addrlen);

 

posted @ 2016-07-04 21:24  ranran1203  阅读(277)  评论(0)    收藏  举报