Chinaunix首页 | 论坛 | 博客
  • 博客访问: 46721
  • 博文数量: 7
  • 博客积分: 25
  • 博客等级: 民兵
  • 技术积分: 84
  • 用 户 组: 普通用户
  • 注册时间: 2013-01-04 18:31
文章分类
文章存档

2013年(7)

我的朋友

分类: 嵌入式

2013-04-17 09:59:58

<-----------------------------------如果有什么不对的地方还请大家指出来----------------------------------->
socket类型:
    1.    流式套接字:提供了一种面向连接,可靠的数据传输服务,数据无差错、无重复的发送且按发送顺序接收。内设置了流量控制,避免数据流淹没的接收方。数据被看作字节留,无长度限制。
    2.    数据包套接字:数据包以独立数据包的形式被发送,不提供无差错保证,数据可能丢失或重复,顺序发送,可能乱序接收。
    3.    原始套接字:可以对较低层协议如IP、ICMP直接访问。

socket的位置:
    流式套接字位于应用层跟TCP层之间,数据包套接字位于应用层跟UDP层之间,原始套接字位于应用层跟IP层之间。

ip地址:
    internet中主机要与别的机器通信必须要有一个ip地址。
    ip地址分为32位,128位。
    每个数据包必须携带目标IP地址和源IP地址,路由器依靠此信息为数据包选择路由。
表示形式:点分形式如(192.168.0.223),最后都会转换为一个32位的无符号整数。

端口号:
    为了区分一台主机接收到的数据包应该转交给那个进程来处理,使用端口号来区别。
    TCP端口号跟UDP端口号独立
    端口号一般由IANA(internet assigned numbers authority)
    众所周知的端口号:1~1023(1~255之间为众所周知的端口号,256~1023端口通常由unix系统占用)
    已登记的端口号:    1024~49151
    动态或私有端口:    49152~65535
    
字节序:    
    不同类型CPU的主机中,内存存储多字节整数的序列有两种方式。及大端、小端。
    1.    大端:高字节存储在低地址
    2.    小端:低字节存储在低地址
    》网络中传输的数据必须按网络字节序,及大端模式。所以当应用进程将整数送入socket之前需要转化成网络字节序;当应用进程从socket取出整数后,要转化成相应的字节序。(因为网络字节序是大端,但是终端的字节序不同,所以要将网络字节序转化为我们自己的字节序)。

IP地址与网络字节序的转换:
    1.    inet_aton()
            int inet_aton(const char *strptr, struct in_addr *addrptr);    将strptr所指的字符串装换成32位的网络字节序。
    2.    inet_addr()
            in_addr_t inet_addr(const char *strptr);        功能同上
    3.    inet_ntoa()
            char *inet_ntoa(struct in_addr inaddr);        将32位的网络字节序转化成点分十进制的形式

端口号与网络字节序的转换:
    1.    主机字节序到网络字节序:    u_long htonl (u_long hostlong)
                                                          u_short htons (u_short short)
    2.    网络字节序到主机字节序:    u_long ntohl (u_long hostlong)
                                                          u_short ntohs (u_shrt short)



tcp服务器端

1.创建套节字

int socket(int domain, int type, int protocol);
成功返回其描述符,失败返回-1
第一个参数:使用的协议族 IPv4 ,通常写AF_INET
第二个参数:套节字类型
第三个参数:使用的协议,如果是流式套节子或者是数据报套节字,通常写为0,让协议栈自动识别
   如果是原始套节字,必须指定使用的协议编号

eg:
int listen_fd;

if((listen_fd = socket(AF_INET,SOCK_STREAM,0)) < 0)
{
....
}

2.绑定服务器端地址

int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

通用的网络地址结构类型:struct sockaddr 
我们填充的地址结构类型:struct sockaddr_in 

struct sockaddr_in
{
协议族
sin_family;
端口
sin_port;
IP地址
struct in_addr sin_addr;
填充
char sin_zero[8];
};

eg:./server 192.168.0.10 8887
填充地址ip,端口号

struct sockaddr_in server_addr;

bzero(&server_addr,sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(atoi(argv[2]));
server_addr.sin_addr.s_addr = inet_addr(argv[1]);

if(bind(listen_fd,(struct sockaddr *)&server_addr,sizeof(struct sockaddr)) < 0)
{
....
}

3.设置套节子为监听模式
    它是为了接收客户端的请求,然后将请求放到请求队列中,供accept去取走请求。
int listen(int sockfd, int backlog);
sockfd:套接字的文件描述符
backlog:监听的一个最大值

eg:listen(listen_fd,5);


4.提取请求
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
第一个参数:监听套节字的描述符
第二个参数:存放发起请求的客户端地址
第三个参数:地址大小,它是一个值结果参数(使用前必须进行初始化,调用结束记录对方实际地址大小)

注意:如果请求队列为空,则阻塞
 如果请求队列不为空,提取请求,并且创建一个已连接的套接字,返回其描述符,此描述符,可以进行读写

5.connect();    这个用于向listen发出请求
    int connect(int sockfd, struct sockaddr *serv_addr, int addrlen)
    sockfd:    socket返回的文件描述符
    serv_addr:    服务器端的地址信息
    addrlen:    serv_addr的长度
这个函数是客户端使用的系统调用

6.建立好连接以后就是相互之间发送信息。例如:
    6.1:ssize_t send (int socket, const void *buf, size_t length, int flags);        向socket套接字中发送信息,相当于write
            返回值:成功返回实际发送的字节数,失败返回-1
            buf:发送缓冲区首地址
            length:    发送的字节数
            flags:    发送的方式,通常为0。在这种情况下跟write一样。
    6.2    ssize_t recv (int socket, const void *buf, size_t length, int flags);        从socket套接字中提取信息,相当于read
            返回值:成功返回接收的字节数,失败返回-1
            buf:    接收缓冲区的首地址
            length:    字节数
            flags:    接收方式,通常为0
其中还有两组函数可以实现上面的功能:sendto recvfrom 这一组一般用在UDP协议中
                                                                recvmsg    sendmsg    这一组可以实现前面所有的功能,但用起来比较复杂,平时很少使用。
7.    关闭套接字:
        int close( int socket );  关闭双向通信
        int shutdown(int sockfd, int howto);    TCP连接是双向的,当我们用close关闭时是将读写端都关闭,如果想关闭一端用这个就行。
        howto:为0时:关闭读端,但还可以往里面写数据
                    为1时: 关闭写端,只能从里面读数据
                    为2时:    关闭读写端。同close一样


下面是源码:
服务器端:

点击(此处)折叠或打开

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <errno.h>
  5. #include <sys/socket.h>
  6. #include <netinet/in.h>
  7. #include <arpa/inet.h>

  8. #define hander_err(meg) \
  9.     do {perror(meg); exit(EXIT_FAILURE);} while(0)
  10. //./server ip port
  11. int main(int argc,char *argv[])
  12. {
  13.     int n;
  14.     char buf[1024];
  15.     int listen_fd,connfd;
  16.     int addr_len = sizeof(struct sockaddr);
  17.     struct sockaddr_in server_addr,peer_addr;

  18.     if(argc < 3)
  19.     {
  20.         fprintf(stderr,"Usage : %s ip port.\n",argv[0]);
  21.         exit(EXIT_FAILURE);
  22.     }

  23.     if((listen_fd = socket(AF_INET,SOCK_STREAM,0)) < 0)
  24.             hander_err("socket");
  25.     
  26.     bzero(&server_addr,sizeof(server_addr));
  27.     server_addr.sin_family = AF_INET;
  28.     server_addr.sin_port = htons(atoi(argv[2])); //这里用htons是因为sin_port的类型
  29.     server_addr.sin_addr.s_addr = inet_addr(argv[1]);
  30.     
  31.     if(bind(listen_fd,(struct sockaddr *)&server_addr,sizeof(server_addr)) < 0)
  32.               hander_err("listen");

  33.     listen(listen_fd,5);

  34.     if((connfd = accept(listen_fd,(struct sockaddr *)&peer_addr,&addr_len)) < 0) //其实我们通信是通过connfd这个套接字通信的?
  35.               hander_err("accept");

  36.     printf("connfd = %d.\n",connfd);
  37.     
  38.     printf("**********************************\n");
  39.     printf("Ip : %s.\n",inet_ntoa(peer_addr.sin_addr));
  40.     printf("Port : %d.\n",ntohs(peer_addr.sin_port));
  41.     printf("**********************************\n");

  42.     while(1)
  43.     {
  44.         n = read(connfd,buf,sizeof(buf));
  45.         buf[n] = '\0';
  46.         printf("Read %d bytes : %s.\n",n,buf);
  47.     }

  48.     exit(EXIT_SUCCESS);
  49. }

客户端:

点击(此处)折叠或打开

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <errno.h>
  5. #include <sys/socket.h>
  6. #include <netinet/in.h>
  7. #include <arpa/inet.h>

  8. #define hander_err(meg) \
  9.     do {perror(meg); exit(EXIT_FAILURE);} while(0)

  10. //./server ip port
  11. int main(int argc,char *argv[])
  12. {
  13.     char buf[1024];
  14.     int sockfd;
  15.     int addr_len = sizeof(struct sockaddr);
  16.     struct sockaddr_in server_addr;

  17.     if(argc < 3)
  18.     {
  19.         fprintf(stderr,"Usage : %s ip port.\n",argv[0]);
  20.         exit(EXIT_FAILURE);
  21.     }

  22.     if((sockfd = socket(AF_INET,SOCK_STREAM,0)) < 0)
  23.             hander_err("socket");
  24.     
  25.     bzero(&server_addr,sizeof(server_addr));
  26.     server_addr.sin_family = AF_INET;
  27.     server_addr.sin_port = htons(atoi(argv[2]));
  28.     server_addr.sin_addr.s_addr = inet_addr(argv[1]);
  29.     
  30.     //发出请求
  31.     if(connect(sockfd,(struct sockaddr *)&server_addr,sizeof(server_addr)) < 0)
  32.             hander_err("connect");

  33.     while(1)
  34.     {
  35.         printf(">");
  36.         fgets(buf,sizeof(buf),stdin);
  37.         buf[strlen(buf)-1] = '\0';

  38.         write(sockfd,buf,strlen(buf));
  39.     }

  40.     exit(EXIT_SUCCESS);
  41. }



UDP:
    函数的介绍:
        1.   ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                        struct sockaddr *src_addr, socklen_t *addrlen);
            socket:    套接字文件描述符
            buf:      接收数据的首地址
            flags:    通常为0
            len:        接收数据的长度
            src_addr:    为NULL时,后面的addrlen就不用了,直接写NULL就行
                                当不为NULL时,存的是数据发送端的一些ip、port信息,此时adrlen为sockaddr的大小。    
            返回值:    成功返回接收到的字节数,失败返回-1,如果信息发送端不存在将返回0。

        2.     ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
                      const struct sockaddr *dest_addr, socklen_t addrlen);
                socket:    套接字文件描述符
                buf:        要发送的信息的首地址
                len:        发送信息的大小
                flags:    通常为0
                dest_addr:    如果想将自己的ip、port发出去的话这里要写上。反之为NULL。此时addrlen为0
                返回值:    成功返回发送的字节数,失败返回-1,并且设置错误码
  
下面是源代码:
 服务器端:
   

点击(此处)折叠或打开

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <sys/types.h>
  4. #include <sys/socket.h>
  5. #include <string.h>
  6. #include <arpa/inet.h>
  7. #include <netinet/in.h>

  8. #define handler_err(msg) \
  9.     do {perror(msg); exit(EXIT_FAILURE);} while(0)

  10. // .service ip port
  11. int main(int argc, char *argv[])
  12. {
  13.     int sockfd;
  14.     struct sockaddr_in service_addr,peer_addr;
  15.     int ret;
  16.     char buf[1024];
  17.     socklen_t addrlen = sizeof(peer_addr);

  18.     if(argc != 3)
  19.     {
  20.         fprintf(stderr,"Usage : %s ip port\n",argv[0]);
  21.         exit(EXIT_FAILURE);
  22.     }
  23.     
  24.     sockfd = socket(AF_INET,SOCK_DGRAM,0);
  25.     if(sockfd < 0)
  26.         handler_err("Fail to socket");
  27.     
  28.     bzero(&service_addr, sizeof(service_addr));
  29.     service_addr.sin_family = AF_INET;
  30.     service_addr.sin_port = htons(atoi(argv[2]));
  31.     service_addr.sin_addr.s_addr = inet_addr(argv[1]);

  32.     ret = bind(sockfd,(struct sockaddr*)&service_addr,sizeof(service_addr));
  33.     if(ret < 0)
  34.         handler_err("Fail to bind");

  35.     while(1)
  36.     {
  37.         ret = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr*)&peer_addr, &addrlen);
  38.         buf[ret] = '\0';
  39.         if(ret <= 0)
  40.             handler_err("Fail to recvfrom");

  41.         printf("buf : %s\n",buf);
  42.     }
  43.     exit(EXIT_SUCCESS);
  44. }


客户端程序:

点击(此处)折叠或打开

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <sys/types.h>
  4. #include <sys/socket.h>
  5. #include <string.h>
  6. #include <arpa/inet.h>
  7. #include <netinet/in.h>

  8. #define handler_err(msg) \
  9. do {perror(msg); exit(EXIT_FAILURE);} while(0)

  10. // .service ip port
  11. int main(int argc, char *argv[])
  12. {
  13. int sockfd;
  14. struct sockaddr_in service_addr;
  15. int ret;
  16. char buf[1024];

  17. if(argc != 3)
  18. {
  19. fprintf(stderr,"Usage : %s ip port\n",argv[0]);
  20. exit(EXIT_FAILURE);
  21. }
  22. sockfd = socket(AF_INET,SOCK_DGRAM,0);
  23. if(sockfd < 0)
  24. handler_err("Fail to socket");
  25. bzero(&service_addr, sizeof(service_addr));
  26. service_addr.sin_family = AF_INET;
  27. service_addr.sin_port = htons(atoi(argv[2]));
  28. service_addr.sin_addr.s_addr = inet_addr(argv[1]);
  29. /*
  30. ret = bind(sockfd,(struct sockaddr*)&service_addr,sizeof(service_addr));
  31. if(ret < 0)
  32. handler_err("Fail to bind");
  33. */
  34. while(1)
  35. {
  36. printf(">");
  37. fgets(buf,sizeof(buf),stdin);
  38. buf[strlen(buf) - 1] = '\0';

  39. ret = sendto(sockfd, &buf, strlen(buf), 0, (struct sockaddr*)&service_addr, sizeof(struct sockaddr_in));
  40. if(ret <= 0)
  41. handler_err("Fail to sendto");
  42. }
  43. exit(EXIT_SUCCESS);
  44. }





阅读(1937) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~