您的位置:首页 > 编程语言

socket编程——一个简单的例子

2015-12-11 17:26 591 查看
从一个简单的使用TCP例子开始socket编程,其基本步骤如下:

server client



+++++++ ++++++++

创建socket 创建socket

+++++++ ++++++++

| |

| |

| |

+++++++ ++++++++

地址赋值( 地址赋值(

自己的地址) 服务器地址)

+++++++ ++++++++

| |

| |

| |

++++++++ |

用bind绑定 |

socket和地址 |

++++++++ |

| |

| |

| |

+++++++ |

listen |

+++++++ |

| ++++++++++

| <------------------------------ connect 服务器

| ++++++++++

+++++++ |

accept |

+++++++ |

| |

| +++++++++

| recv 和send

| 进行数据处理

| +++++++++

+++++++++ |

用accept得到 |

的socket进行 |

recv 和 send |

+++++++++ |

| |

| |

| |

+++++++++ +++++++++

close socket close socket

+++++++++ +++++++++



根据以上步骤,服务器端的代码为

[cpp] view
plaincopy

#include <stdio.h>

#include <string.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <stdlib.h>

#include <syslog.h>

#include <errno.h>

#define MAX_LISTEN_NUM 5

#define SEND_BUF_SIZE 100

#define RECV_BUF_SIZE 100

#define LISTEN_PORT 1010

int main()

{

int listen_sock = 0;

int app_sock = 0;

struct sockaddr_in hostaddr;

struct sockaddr_in clientaddr;

int socklen = sizeof(clientaddr);

char sendbuf[SEND_BUF_SIZE] = {0};

char recvbuf[RECV_BUF_SIZE] = {0};

int sendlen = 0;

int recvlen = 0;

int retlen = 0;

int leftlen = 0;

char *ptr = NULL;

memset((void *)&hostaddr, 0, sizeof(hostaddr));

memset((void *)&clientaddr, 0, sizeof(clientaddr));

hostaddr.sin_family = AF_INET;

hostaddr.sin_port = htons(LISTEN_PORT);

hostaddr.sin_addr.s_addr = htonl(INADDR_ANY);

listen_sock = socket(AF_INET, SOCK_STREAM, 0);

if(listen_sock < 0)

{

syslog(LOG_ERR, "%s:%d, create socket failed", __FILE__, __LINE__);

exit(1);

}

if(bind(listen_sock, (struct sockaddr *)&hostaddr, sizeof(hostaddr)) < 0)

{

syslog(LOG_ERR, "%s:%d, bind socket failed", __FILE__, __LINE__);

exit(1);

}

if(listen(listen_sock, MAX_LISTEN_NUM) < 0)

{

syslog(LOG_ERR, "%s:%d, listen failed", __FILE__, __LINE__);

exit(1);

}

while(1)

{

app_sock = accept(listen_sock, (struct sockaddr *)&clientaddr, &socklen);

if(app_sock < 0)

{

syslog(LOG_ERR, "%s:%d, accept failed", __FILE__, __LINE__);

exit(1);

}

sprintf(sendbuf, "welcome %s:%d here!/n", inet_ntoa(clientaddr.sin_addr.s_addr), clientaddr.sin_port);

//send data

sendlen = strlen(sendbuf) +1;

retlen = 0;

leftlen = sendlen;

ptr = sendbuf;

//while(leftlen)

{

retlen = send(app_sock, ptr, sendlen, 0);

if(retlen < 0)

{

if(errno == EINTR)

retlen = 0;

else

exit(1);

}

leftlen -= retlen;

ptr += retlen;

}

//receive data

recvlen = 0;

retlen = 0;

ptr = recvbuf;

leftlen = RECV_BUF_SIZE -1;

//do

{

retlen = recv(app_sock, ptr, leftlen, 0) ;

if(retlen < 0)

{

if(errno == EINTR)

retlen = 0;

else

exit(1);

}

recvlen += retlen;

leftlen -= retlen;

ptr += retlen;

}

//while(recvlen && leftlen);

printf("receive data is : %s", recvbuf);

close(app_sock);

}

close(listen_sock);



return 0;





}





客户端代码为:

[cpp] view
plaincopy

#include <stdio.h>

#include <string.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <syslog.h>

#include <errno.h>

#include <stdlib.h>

#define MAX_LISTEN_NUM 5

#define SEND_BUF_SIZE 100

#define RECV_BUF_SIZE 100

#define SERVER_PORT 1010

int main()

{

int sock_fd = 0;

char recvbuf[RECV_BUF_SIZE] = {0};

char sendbuf[SEND_BUF_SIZE] = {0};

int recvlen = 0;

int retlen = 0;

int sendlen = 0;

int leftlen = 0;

char *ptr = NULL;

struct sockaddr_in ser_addr;



memset(&ser_addr, 0, sizeof(ser_addr));

ser_addr.sin_family = AF_INET;

inet_aton("127.0.0.1", (struct in_addr *)&ser_addr.sin_addr);

ser_addr.sin_port = htons(SERVER_PORT);

sock_fd = socket(AF_INET, SOCK_STREAM, 0);

if(sock_fd < 0)

{

syslog(LOG_ERR, "%s:%d, create socket failed", __FILE__, __LINE__);

exit(1);

}

if(connect(sock_fd, (struct sockaddr *)&ser_addr, sizeof(ser_addr)) < 0)

{

syslog(LOG_ERR, "%s:%d, connect socket failed", __FILE__, __LINE__);

exit(1);

}

//receive data

recvlen = 0;

retlen = 0;

ptr = recvbuf;

leftlen = RECV_BUF_SIZE -1;

//do

{

retlen = recv(sock_fd, ptr, leftlen, 0) ;

if(retlen < 0)

{

if(errno == EINTR)

retlen = 0;

else

exit(1);

}

recvlen += retlen;

leftlen -= retlen;

ptr += retlen;

}

//while(recvlen && leftlen);

printf("receive data is : %s", recvbuf);

sprintf(sendbuf, "hello server/n");

//send data

sendlen = strlen(sendbuf) +1;

retlen = 0;

leftlen = sendlen;

ptr = sendbuf;

// while(leftlen)

{

retlen = send(sock_fd, ptr, sendlen, 0);

if(retlen < 0)

{

if(errno == EINTR)

retlen = 0;

else

exit(1);

}

leftlen -= retlen;

ptr += retlen;

}

close(sock_fd);



}





现在一个简单的使用tcp的socket通信的例子已经完成了,这里有几个需要说明的问题

1)头文件:

sys/socket.h 包含了socket相关的函数,如socket,send 和recv, 以及struct sockaddr等

netinet/in.h 包含了地址结构,如struct sockaddr_in

errno.h 包含了errno 和 EINTR

syslog.h 包含了syslog相关的信息,其打印结果在/var/log/messages里面



2)socket地址

对于IPv4来说,其地址用的是struct sockaddr_in,具体结构如下
[cpp] view plaincopystruct in_addr {  
  in_addr_t   s_addr;           /* 32-bit IPv4 address */  
                                /* network byte ordered */  
};  
  
struct sockaddr_in {  
  uint8_t         sin_len;      /* length of structure (16) */  
  sa_family_t     sin_family;   /* AF_INET */  
  in_port_t       sin_port;     /* 16-bit TCP or UDP port number */  
                                /* network byte ordered */  
  struct in_addr  sin_addr;     /* 32-bit IPv4 address */  
                                /* network byte ordered */  
  char            sin_zero[8];  /* unused */  
};  

 

其中sin_len我们一般不关注,也不填(只有在使用routing socket的时候才用到,被内核用来处理各种协议簇的地址结构)。

bind, connect, sendto, 和 sendmsg会把socket地址从程序传递给内核; 而accept, recvfrom, recvmsg, getpeername, 和 getsockname会把地址从内核传递给程序。因为不同协议簇的地址结构是不一样的,所以必须要有一个通用的指针来传递地址,对于ANSI C来说我们一般使用void *,但是socket产生早于ANSI C,所以也就没有使用这个机制,而是使用一个通用的地址结构struct sockaddr来处理的

[cpp] view plaincopystruct sockaddr {  
  uint8_t      sa_len;  
  sa_family_t  sa_family;    /* address family: AF_xxx value */  
  char         sa_data[14];  /* protocol-specific address */  
};  

 

IPv6的socket地址为struct sockaddr_in6

[cpp] view plaincopystruct in6_addr {  
  uint8_t  s6_addr[16];          /* 128-bit IPv6 address */  
                                 /* network byte ordered */  
};  
  
#define SIN6_LEN      /* required for compile-time tests */  
  
struct sockaddr_in6 {  
  uint8_t         sin6_len;      /* length of this struct (28) */  
  sa_family_t     sin6_family;   /* AF_INET6 */  
  in_port_t       sin6_port;     /* transport layer port# */  
                                 /* network byte ordered */  
  uint32_t        sin6_flowinfo; /* flow information, undefined */  
  struct in6_addr sin6_addr;     /* IPv6 address */  
                                 /* network byte ordered */  
  uint32_t        sin6_scope_id; /* set of interfaces for a scope */  
};  

 

对于sockaddr-in6来说,我们不能用通用的地址struct sockaddr来存储了,而是产用新的通用地址结构struct sockaddr_storage,这个结构足够大可以存储任何系统支持的地址。

[cpp] view plaincopystruct sockaddr_storage {  
  uint8_t      ss_len;       /* length of this struct (implementation dependent) */  
  sa_family_t  ss_family;    /* address family: AF_xxx value */  
  /* implementation-dependent elements to provide: 
   * a) alignment sufficient to fulfill the alignment requirements of 
   *    all socket address types that the system support 
   * b) enough storage to hold any type of socket address that the 
   *    system supports. 
   */  
};  

 

几种常见的地址结构



3) 相关函数的的length

对于从程序传地址给内核的函数(如connect),其长度是一个整型值,告诉内核要copy的地址长度。

对于从内核传递给程序的函数(如accpt),其长度是一个整型指针,是一个value-result参数。有两个目的:一告诉内核地址结构的长度,让内核在copy的时候不要超过这个长度;二返回内核真正copy的长度。

4)字节序

socket相关的函数都是使用网络字节序

5)地址转换函数

inet_aton, inet_ntoa, and inet_addr把IPv4字符串地址转为32位的网络字节序地址

inet_ptonand inet_ntop可以转换IPv4和IPv6的地址

6)listen中的backlog

要知道这个值的含义先用说一下,对于一个listen的socket,有两个队列:一个是incomplete connection队列(仅仅收到SYN);一个是complete connection队列(三次握手完成)。accept函数就是在complete connection队列中取一个socket。backlog就是指队列的个数,但不行的是各个地方都没有明确定义这个值,没有说明究竟代表了哪个队列,或是两个队列之和。一般来说可以

同时处理的连接数是backlog的1.5倍,很多地方都用5.

7) getsockname 和 getpeername

这两个函数可以与socket关联的地址,getsockname 和 getpeername分别得到自己和对端的地址

[cpp] view plaincopyint getsockname(int sockfd, struct sockaddr *localaddr, socklen_t *addrlen)  
  
int getpeername(int sockfd , struct sockaddr * peeraddr , socklen_t * addrlen );


FROM: http://blog.csdn.net/wind19/article/details/6156339
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: