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套接口编程。很简单吧!
阅读(3717) | 评论(0) | 转发(0) |