码迷,mamicode.com
首页 > 其他好文 > 详细

计算机网络Socket编程之TCP协议

时间:2016-08-08 01:09:35      阅读:165      评论:0      收藏:0      [点我收藏+]

标签:internet   address   计算机网络   ip地址   socket   

>TCP协议位于传输层,是一种面向连接的可靠的传输协议

>socket(套接字):是IP地址与端口号的统称

>套接字的基本结构

          struct sockaddr   这个结构用来存储套接字地址

结构体的定义

  struct sockaddr {

  unsigned short sa_family; /* address族, AF_xxx */

  har sa_data[14]; /* 14 bytes的协议地址 */

  };

       sa_family    一般来说,都是“AFINET”。

       sa_data      包含了一些远程电脑的地址、端口和套接字的数目,它里面的数据是杂溶在一切的。

为了处理struct sockaddr, 程序员建立了另外一个相似的结构 struct sockaddr_in:

   struct sockaddr_in (“in” 代表 “Internet”)

   struct sockaddr_in {

   short int sin_family; /* Internet地址族 */

   unsigned short int sin_port; /* 端口号 */

   struct in_addr sin_addr; /* Internet地址 */

   unsigned char sin_zero[8]; /* 添0(和struct sockaddr一样大小)*/

   };

这个结构提供了方便的手段来访问socket address(struct sockaddr)结构中的每一个元素


 >IP 地址转换

Linux 系统提供和很多用于转换IP 地址的函数.首先,假设你有一个struct sockaddr_in ina,并且你的IP 166.111.69.52 ,你想把你的IP 存储到ina 中。你可以使用的函数: inet_addr() ,它能够把一个用数字和点表示IP 地址的字符串转换成一个无符号长整型。你可以像下面这样使用它:

      ina.sin_addr.s_addr = inet_addr(“166.111.69.52”);

注意:

inet_addr() 返回的地址已经是网络字节顺序了,你没有必要再去调用htonl() 函数反过来,如果你有一个struct in_addr 并且你想把它代表的IP 地址打印出来(按照数字.数字.数字.数字的格式),那么你可以使用函数inet_ntoa()(“ntoa”代表“Network to ASCII”),它会把struct in_addr 里面存储的网络地址以数字.数字.数字.数字的格式。

l inet_ntoa() 使用struct in_addr 作为一个参数,不是一个长整型值。


>套接字字节转换程序的列表:

l htons()——“Host to Network Short”主机字节顺序转换为网络字节顺序(对无符号短型进行操作4 bytes)

l htonl()——“Host to Network Long” 主机字节顺序转换为网络字节顺序(对无符号长型进行操作8 bytes)

l ntohs()——“Network to Host Short “ 网络字节顺序转换为主机字节顺序(对无符号短型进行操作4 bytes)

l ntohl()——“Network to Host Long “ 网络字节顺序转换为主机字节顺序(对无符号长型进行操作8 bytes)


>基本套接字调用

socket() 函数

取得套接字描述符

socket 函数的定义是下面这样子的:

#include <sys/types.h>

#include <sys/socket.h>

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


bind() 函数

bind()函数可以帮助你指定一个套接字使用的端口。

当你使用socket() 函数得到一个套接字描述符,你也许需要将socket 绑定上一个你的机器上的端口。当你需要进行口监听 listen()操作,等待接受一个连入请求的时候,一般都需要经过这一步。比如网络泥巴(MUD),Telnet a.b.c.d 4000.如果你只是想进行连接一台服务器,也就是进行 connect() 操作的时候,这一步并不是必须的。

bind()的系统调用声明如下:

#include <sys/types.h>

#include <sys/socket.h>

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

参数说明:

l sockfd 是由socket()函数返回的套接字描述符。

l my_addr 是一个指向struct sockaddr 的指针,包含有关你的地址的信息:名称、端口和IP 地址。

addrlen 可以设置为sizeof(struct sockaddr)。

connect()函数

让我们花一点时间来假设你是一个Telnet 应用程序。你的使用者命令你建立一个套接字描述符。你遵从命令,调用了socket()。然后,使用者告诉你连接到“166.111.69.52”的23 端口(标准的Telnet 端口)你应该怎么做呢?

你很幸运:Telnet 应用程序,你现在正在阅读的就是套接字的进行网络连接部分:

connect()。

connect() 函数的定义是这样的:

#include <sys/types.h>

#include <sys/socket.h>

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

connect()的三个参数意义如下:

l sockfd :套接字文件描述符,由socket()函数返回的。

l serv_addr 是一个存储远程计算机的IP 地址和端口信息的结构。

l addrlen 应该是sizeof(struct sockaddr)。

listen() 函数

listen()函数是等待别人连接,进行系统侦听请求的函数。当有人连接你的时候,你有两步需要做:通过listen()函数等待连接请求,然后使用accept()函数来处理。(accept()函数在下面介绍)。

listen()函数调用是非常简单的。函数声明如下:

#include <sys/socket.h>

int listen(int sockfd, int backlog);

listen()函数的参数意义如下:

l sockfd 是一个套接字描述符,由socket()系统调用获得。

l backlog 是未经过处理的连接请求队列可以容纳的最大数目。

backlog 具体一些是什么意思呢?每一个连入请求都要进入一个连入请求队列,等待listen 的程序调accept((accept()函数下面有介绍)函数来接受这个连接。当系统还没有调用accept()函数的时候,如果有很多连接,那么本地能够等待的最大数目就是backlog 的数值。你可以将其设成5 到10 之间的数值

accept()函数

函数accept()有一些难懂。当调用它的时候,大致过程是下面这样的:

有人从很远很远的地方尝试调用 connect()来连接你的机器上的某个端口(当然是你已经在listen()的)。他的连接将被 listen 加入等待队列等待accept()函数的调用(加入等待队列的最多数目由调用listen()函数的第二个参数backlog 来决定)。你调用 accept()函数,告诉他你准备连接。accept()函数将回返回一个新的套接字描述符,这个描述符就代表了这个连接.好,这时候你有了两个套接字描述符,返回给你的那个就是和远程计算机的连接,而第一个套接字描述符仍然在你的机器上原来的那个端口上listen()。这时候你所得到的那个新的套接字描述符就可以进行send()操作和recv()操作了。

下面是accept()函数的声明:

#include <sys/socket.h>

int accept(int sockfd, void *addr, int *addrlen);

accept()函数的参数意义如下:

l sockfd 是正在listen() 的一个套接字描述符。

l addr 一般是一个指向struct sockaddr_in 结构的指针;里面存储着远程连接过来的计算机的信息(比如远程计算机的IP 地址和端口)


>   下面我们就来模拟一个socket编程的TCP协议

   server.c 的作用是接受client的请求,并与client进行简单的数据通信,整体为一个阻塞式的网络聊天工具

//server.c

  1#include<stdio.h>

  2 #include<stdlib.h>

  3 #include<sys/types.h>

  4 #include<sys/socket.h>

  5 #include<netinet/in.h>

  6 #include<string.h>

  7 #include<error.h>

  8 #include<arpa/inet.h>

  9 #include<pthread.h>

 10 static void usage(const char* proc)

 11 {

 12     printf("Usage: %s [ip] [port]\n",proc);

 13 }

 14 void *thread_run(void* arg)

 15 {

 16     printf("creat a new thread\n");

 17     int fd=(int)arg;

 18     char buf[1024];

 19     while(1)

 20     {

 21         memset(buf,‘\0‘,sizeof(buf));

 22         ssize_t _s=read(fd,buf,sizeof(buf)-1);

 23         if(_s>0)

 24         {

 25             printf("client#: %s\n",buf);

 26             write(fd,buf,strlen(buf));

 27         }

 28         else if(_s==0)

 29         {

 30             printf("client close...\n");

 31             break;

 32         }

 33         else

 34         {

 35             printf("read eror...\n");

 36            break;

 37         }

 38         return (void*)0;

 39     }

 40 }

 41 int main(int argc,char* argv[])

 42 {

 43     if(argc!=3)

 44     {

 45         usage(argv[0]);

 46         exit(1);

 47     }

 48     int listen_sock=socket(AF_INET,SOCK_STREAM,0);

 49     if(listen_sock<0)

 50     {

 51         perror("socket");

 52         exit(2);

 53     }

 54     struct sockaddr_in server_socket;

 55     server_socket.sin_family=AF_INET;

 56     server_socket.sin_addr.s_addr=inet_addr(argv[1]);

 57     server_socket.sin_port=htons(atoi(argv[2]));

 58     int opt=1;

 59     setsockopt(listen_sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));

 60 

 61     if(bind(listen_sock,(struct sockaddr*)&server_socket,sizeof(server_socke    t))<0)

 62     {

 63         perror("bind");

 64         exit(3);

 65     }

 66     listen(listen_sock,5);

 67     struct sockaddr_in peer;

 68     socklen_t len=sizeof(peer);

 69     int fd=accept(listen_sock,(struct sockaddr*)&peer,&len);

 70     printf("fd: %d\n",fd);

 71     if(fd<0)

 72     {

 73         perror("accept");

 74         exit(4);

 75     }

 76     printf("get a new link,socket->[%s] [%d]",inet_ntoa(peer.sin_addr),ntohs    (peer.sin_port));

 77     while(1)

 78     {

 79         pthread_t id;

 80         pthread_create(&id,NULL,thread_run,(void*)fd);

 81         pthread_join(id,NULL);

 82     }

 83     return 0;

 84 }

//client.c

1 #include<stdio.h>

  2 #include<sys/types.h>

  3 #include<sys/socket.h>

  4 #include<netinet/in.h>

  5 #include<arpa/inet.h>

  6 #include<string.h>

  7 #include<pthread.h>

  8 #include<error.h>

  9 #include<stdlib.h>

 10 static void usage(const char *proc)

 11 {   

 12     printf("usage: %s [ip] [port]\n",proc);

 13 }

 14 int main(int argc,char* argv[])

 15 {

 16     if(argc!=3)

 17     {

 18         usage(argv[0]);

 19         exit(1);

 20     }

 21     int sock=socket(AF_INET,SOCK_STREAM,0);

 22     if(sock==0)

 23     {

 24         perror("socket");

 25         exit(2);

 26     }

 27     struct sockaddr_in client_sock;

 28     client_sock.sin_family=AF_INET;

 29     client_sock.sin_addr.s_addr=inet_addr(argv[1]);

 30     client_sock.sin_port=htons(atoi(argv[2]));

 31 

 32     if(connect(sock,(struct sockaddr*)&client_sock,sizeof(client_sock))<0)

 33     {

 34         perror("connect");

 35         exit(3);

 36     }

 37     char buf[1024];

 38     while(1)

 39     {

 40         memset(buf,‘\0‘,sizeof(buf));

 41         printf("Please Enter:");

 42         fflush(stdout);

 43         ssize_t _s=read(0,buf,sizeof(buf)-1);

 44         if(_s>0)

 45         {

 46             buf[_s-1]=‘\0‘;

 47             write(sock,buf,strlen(buf));

 48             _s=read(sock,buf,sizeof(buf));

 49             if(_s>0)

 50             {

 51                 printf("%s\n",buf);

 52             }

 53         }

 54     }

 55     return 0;

 56 }

运行结果:

技术分享



本文出自 “11275984” 博客,请务必保留此出处http://11285984.blog.51cto.com/11275984/1835420

计算机网络Socket编程之TCP协议

标签:internet   address   计算机网络   ip地址   socket   

原文地址:http://11285984.blog.51cto.com/11275984/1835420

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!