Chinaunix首页 | 论坛 | 博客
  • 博客访问: 295682
  • 博文数量: 52
  • 博客积分: 814
  • 博客等级: 军士长
  • 技术积分: 689
  • 用 户 组: 普通用户
  • 注册时间: 2010-11-21 19:41
文章分类
文章存档

2017年(1)

2016年(2)

2014年(1)

2012年(42)

2011年(6)

分类: LINUX

2012-05-09 10:32:02

       Socket()编程是Linux进程间通信的方式之一。她不同于其他常见的通信方式,如管道,FIFO、消息队列、信号量、和共享内存等,他们只是局限于同一台PC之间的通信。 Socket()编程则可以实现不同计算机之间进程间的通信。
   首先,要知道进程与端口之间的关系。如两台PC之间进程之间相互通信,那么怎么解决两者之间的识别呢?每台PC都有很多个进程,如何准确的确定通信的目的进程(我自己想的)就需要一个识别号码。计算机的识别很简单,通过网络上的IP地址就可以了。确定了目的计算机后,下一步就需要确定进程了。此时,提出了端口PORTnumber的概念。每一个进程对应一个端口号,而且是一一对应的关系。那么这样就解决了进程之间相互识别的问题。
    由于某些特别的进程分配了固定的端口号,如HTTP(80)/、  ftp(21)等等。其他的可以自己在编程时可以随意configure。
   socket()编程就是把IP地址和port number绑定,理解了这个就知道通信的要求了。
还需要了解下 socket()编程的基础。
1.套接口的数据结构。在头文件中,其数据结构定义如下

 struct  sockaddr
{
uint8_t sa_len;                uint8_t 是POSIX.1要求的数据类型。
sa_family_t   sa_family;    套接口的协议族
cahr   sa_data[14];             协议地址的大小
}

以上是通用的套接口的数据结构,在平常中我们用到的是ipv4套接口的地址数据结构。

struct socketaddr_in

{
uint8_t  sin_len;              
sa_family_t   sin_family;
in_port_t     sin_port;
struct  in_addr   sin_addr;
unsigned char     sin_zero[8];
}

在编写程序的时候,就需要根据实际要求填充以上数据结构。
此时填充的各个成员的时候就需要一些特定的函数。如怎么将ip地址和端口号在网络上的排序(字节排序函数),如何将ip地址由十进制(192.2.2.2)转换成32的二进制形式(IP地址转换函数)。填充数据结构需要先将数据结构清除下(常用的bzero())函数。还有登陆某些网址(域名)转换成ip地址的函数。

在tcp套接口编程中。server端和client端相互通信,其中server端的需要做的工作可以归结为:创建套接字--------》填充套接字---------》绑定端口(bind())---------->监听端口是否有请求?----------》接受请求(accept()函数,它会创建一个新的套接字标示符)-----------》接收函数接收client的数据----------》调用write()发送函数发送到client端。-----------》关闭客户端的套接口---------》关闭服务器的套接口。

client端的工作流程:
创建套接字--------》填充套接字---------》调用connet()函数发次请求连接(connect服务器的端口)---------》调用read(*)获取服务器的发送来的数据--------------》关闭套接口。

其实在tcp中,可以理解为是全双工的通信,彼此之间可以发送和接收信息。上面这个是最基本的建立通信的模型。
server_tcp.c 代码如下:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MAXSIZE 1024     /*定义数据缓冲区大小*/

int main(int argc, char *argv[])
{
int sockfd,new_fd;
struct sockaddr_in server_addr;   /*定义服务器端套接口数据结构server_addr */
struct sockaddr_in client_addr;   /*定义客户端套接口数据结构client_addr */
int sin_size,portnumber; 
char buf[MAXSIZE];     /*发送数据缓冲区*/
if(argc!=2)
{
fprintf(stderr,"Usage:%s portnumber\a\n",argv[0]);
exit(1);
}
if((portnumber=atoi(argv[1]))<0) 
{  /*获得命令行的第二个参数--端口号,atoi()把字符串转换成整型数*/
fprintf(stderr,"Usage:%s portnumber\a\n",argv[0]);
exit(1);
}
if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)
/*服务器端开始建立socket描述符*/
{
fprintf(stderr,"Socket error:%s\n\a",strerror(errno));
exit(1);
/*服务器端填充 sockaddr结构*/
bzero(&server_addr,sizeof(struct sockaddr_in)); /*先将套接口地址数据结构清零*/
server_addr.sin_family=AF_INET;
server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
server_addr.sin_port=htons(portnumber);
if(bind(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1)
                                   /*调用bind函数绑定端口*/
fprintf(stderr,"Bind error:%s\n\a",strerror(errno)); 
exit(1); 
if(listen(sockfd,5)==-1)  
/*端口绑定成功,监听sockfd描述符,同时处理的最大连接请求数为5 */
{
fprintf(stderr,"Listen error:%s\n\a",strerror(errno));
exit(1);
}
while(1)      /*服务器阻塞,等待接收连接请求,直到客户程序发送连接请求*/
{
sin_size=sizeof(struct sockaddr_in);
if((new_fd=accept(sockfd,(struct sockaddr *)(&client_addr),&sin_size))==-1)
/*调用accept接受一个连接请求*/
{
fprintf(stderr,"Accept error:%s\n\a",strerror(errno)); 
exit(1); 
}
fprintf(stderr,"Server get connection from %s\n",inet_ntoa(client_addr.sin_addr));
/*TCP连接已建立,打印申请连接的客户机的IP地址*/
printf("Connected successful, please input the masage[<1024 bytes]:\n");
        /*提示用户输入将要发送的数据,长度小于缓冲区的长度,即1024字节*/
if(fgets(buf, sizeof(buf), stdin) != buf)
{  /*从终端输入的数据存放在buf缓冲区*/
         printf("fgets error!\n");
         exit(1);
}
if(write(new_fd,buf,strlen(buf))==-1)   /*调用write发送数据*/
{
fprintf(stderr,"Write Error:%s\n",strerror(errno)); 
exit(1); 
close(new_fd);  /*本次通信已结束,关闭客户端的套接口,并循环下一次等待*/
}
close(sockfd);  /*服务器进程结束,关闭服务器端套接口*/
exit(0);
}

client_tcp.c 如下:

include
#include
#include
#include
#include
#include
#include
#include

int main(int argc, char *argv[])
{
int sockfd;
char buffer[1024];
struct sockaddr_in server_addr; /*定义服务器端套接口数据结构server_addr */
struct hostent *host;
int portnumber,nbytes;
if(argc!=3)
{
fprintf(stderr,"Usage:%s hostname portnumber\a\n",argv[0]);
exit(1);
}
if((host=gethostbyname(argv[1]))==NULL)
{  /*获得命令行的第二个参数-主机名*/
fprintf(stderr,"Gethostname error\n");
exit(1);
}
if((portnumber=atoi(argv[2]))<0)
{  /*获得命令行的第三个参数--端口号,atoi()把字符串转换成整型数*/
fprintf(stderr,"Usage:%s hostname portnumber\a\n",argv[0]);
exit(1);
}
/* 客户程序开始建立 sockfd描述符 */
if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)
{
fprintf(stderr,"Socket Error:%s\a\n",strerror(errno));
exit(1); 
/*客户程序填充服务端的资料*/
bzero(&server_addr,sizeof(server_addr));
server_addr.sin_family=AF_INET;
server_addr.sin_port=htons(portnumber);
server_addr.sin_addr=*((struct in_addr *)host->h_addr);
/*客户程序发起连接请求*/
if(connect(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1)
{
fprintf(stderr,"Connect Error:%s\a\n",strerror(errno));
exit(1);
}
/*连接成功,调用read读取服务器发送来的书籍*/
if((nbytes=read(sockfd,buffer,1024))==-1)
{
fprintf(stderr,"Read Error:%s\n",strerror(errno));
exit(1);
buffer[nbytes]='\0';
printf("I have received:%s\n",buffer);   /*输出接收到的数据*/
close(sockfd);   /*结束通信*/ 
exit(0);
}

通过上机练习,充分了解了tcp套接口编程。很简单吧!
阅读(3687) | 评论(0) | 转发(0) |
0

上一篇:UDP

下一篇:streer(), perror(),errno

给主人留下些什么吧!~~