Chinaunix首页 | 论坛 | 博客
  • 博客访问: 6270613
  • 博文数量: 2759
  • 博客积分: 1021
  • 博客等级: 中士
  • 技术积分: 4091
  • 用 户 组: 普通用户
  • 注册时间: 2012-03-11 14:14
文章分类

全部博文(2759)

文章存档

2019年(1)

2017年(84)

2016年(196)

2015年(204)

2014年(636)

2013年(1176)

2012年(463)

分类:

2012-05-21 10:32:57

原文地址:套接字编程系统调用 作者:yulianliu1218

Normal 0 7.8 磅 0 2 false false false EN-US ZH-CN X-NONE MicrosoftInternetExplorer4 /* Style Definitions */ table.MsoNormalTable {mso-style-name:普通表格; mso-tstyle-rowband-size:0; mso-tstyle-colband-size:0; mso-style-noshow:yes; mso-style-priority:99; mso-style-qformat:yes; mso-style-parent:""; mso-padding-alt:0cm 5.4pt 0cm 5.4pt; mso-para-margin:0cm; mso-para-margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:10.0pt; font-family:"Times New Roman","serif";} table.MsoTableGrid {mso-style-name:网格型; mso-tstyle-rowband-size:0; mso-tstyle-colband-size:0; mso-style-unhide:no; border:solid windowtext 1.0pt; mso-border-alt:solid windowtext .5pt; mso-padding-alt:0cm 5.4pt 0cm 5.4pt; mso-border-insideh:.5pt solid windowtext; mso-border-insidev:.5pt solid windowtext; mso-para-margin:0cm; mso-para-margin-bottom:.0001pt; text-align:justify; text-justify:inter-ideograph; mso-pagination:none; font-size:10.0pt; font-family:"Times New Roman","serif";} 服务器模式 

TCP/IP网络应用中,通信的两个进程间相互作用的主要模式是客户/服务器模式(Client/Server model),即客户向服务器发出服务请求,服务器接收到请求后,提供相应的服务。

 

服务器端:

首先服务器方要先启动,并根据请求提供相应服务: 

 

1. 打开一通信通道并告知本地主机,它愿意在某一公认地址上(周知口,如FTP21)接收客户请求; 

2. 等待客户请求到达该端口; 

3. 接收到重复服务请求,处理该请求并发送应答信号。接收到并发服务请求,要激活一新进程来处理这个客户请求(如UNIX系统中用forkexec)。新进程处理此客户请求,并不需要对其它请求作出应答。服务完成后,关闭此新进程与客户的通信链路,并终止。 

4. 返回第二步,等待另一客户请求。 

5. 关闭服务器 

 

客户端: 

1. 打开一通信通道,并连接到服务器所在主机的特定端口; 

2. 向服务器发服务请求报文,等待并接收应答;继续提出请求...... 

3. 请求结束后关闭通信通道并终止。

 

、创建套接字──socket() 

应用程序在使用套接字前,首先必须拥有一个套接字,系统调用socket()向应用程序提供创建套接字的手段,其调用格式如下:

#include

#include

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

第一个参数domain设置为“AF_INET”

第二个参数是套接口的类型:SOCK_STREAMSOCK_DGRAM。第三个参数设置为0

系统调用socket()只返回一个套接字描述符,如果出错,则返回-1 

应用示例:

TCP 方式:sockfd = socket(AF_INET,SOCK_STREAM,0);

UDP 方式:sockfd =socket(AF_INET, SOCK_DGRAM,0);

举例:

#include

#include

main()

{

        int sock;

        sock=socket(AF_INET,SOCK_STREAM,0);

        if(sock<0)

        {

                printf("socket is error");

                return -1;

        }

        ……

}

 

、关闭套接字──close() shutdown()

使用close()调用关闭连接的套接字文件描述符:

close(sockfd);

这样就不能再对此套接字做任何的读写操作了。

 

使用系统调用shutdown()部分关闭socket连接,可有更多的控制权。它允许在某一个方向切断通信,或者切断双方的通信:

int shutdown(int sockfd,int how);

第一个参数是你希望切断通信的套接字文件描述符。第二个参数how值如下:

0—不允许接收

1—不允许发送

2—不允许接收和发送(类似于close())

shutdown()如果成功则返回0,如果失败则返回-1

 

TCP方式:

一旦你有了一个套接口以后,下一步就是把套接口绑定到本地计算机的某一个端口上。如果想用listen()来监听某一端口的数据,这是必要一步,但如果只想使用connect()则无需调用bind函数。

下面是系统调用bind()的使用方法:

#include

#include

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

第一个参数sockfd是由socket()调用返回的套接字文件描述符。

第二个参数my_addr是指向数据结构sockaddr的指针。数据结构sockaddr中包括了端口和IP地址的信息。

第三个参数addrlen可以设置成sizeof(struct sockaddr)

举例:

#include

#include

#include

#define MYPORT 6666

 

main()

{

        int sock;

        struct sockaddr_in my_addr;

        sock=socket(AF_INET,SOCK_STREAM,0);

        if(sock<0)

        {

                printf("socket is error");

                return -1;

        }

        my_addr.sin_family=AF_INET;/*hostbyteorder*/

        my_addr.sin_port=htons(MYPORT);/*short,network byte order*/

        my_addr.sin_addr.s_addr=inet_addr("132.241.5.10");

        bzero(&(my_addr.sin_zero),8);/*zero the rest of the struct*/

        if(bind(sock,(struct sockaddr*)&my_addr,sizeof(struct sockaddr))<0)

        {

                printf("bind error");

                return -1;

        }

...

}

如果出错,bind()也返回-1

在调用 bind() 的时候,要注意的另一件事情是:不要采用小于 1024的端口号。所有小于1024的端口号都被系统保留!你可以选择从1024 65535的端口(如果它们没有被别的程序使用的话)

如果你使用connect()系统调用,那么你不必知道你使用的端口号。当你调用connect()时,它检查套接口是否已经绑定,如果没有,它将会分配一个空闲的端口。 

如果希望不连接到远程的主机,也就是说你希望等待一个进入的连接请求,然后再处理它们。这样,你通过首先调用listen(),然后再调用accept()来实现。

系统调用listen()的形式如下:

int listen(int sockfd,int backlog);

第一个参数是系统调用socket()返回的套接口文件描述符。

第二个参数是进入队列中允许的连接的个数。进入的连接请求在使用系统调用accept()应答之前要在进入队列中等待。这个值是队列中最多可以拥有的请求的个数。大多数系统的缺省设置为20。你可以设置为5或者10。当出错时,listen()将会返回-1值。

当然,在使用系统调用listen()之前,我们需要调用bind()绑定到需要的端口,否则系统内核将会让我们监听一个随机的端口。所以,如果你希望监听一个端口,下面是应该使用的系统调用的顺序:

socket();

bind();

listen();

/* accept() 应该在这 */   

举例:

#include

#include

#include

#include

#include

#define MYPORT 6666

 

main()

{

        int sock,new_fd;

        int sin_size;

        struct sockaddr_in my_addr;

        struct sockaddr_in their_addr;

        sock=socket(AF_INET,SOCK_STREAM,0);

        if(sock<0)

        {

                printf("socket is error");

                return -1;

        }

        my_addr.sin_family=AF_INET;/*hostbyteorder*/

        my_addr.sin_port=htons(MYPORT);/*short,network byte order*/

        my_addr.sin_addr.s_addr=INADDR_ANY;

        bzero(&(my_addr.sin_zero),8);/*zero the rest of the struct*/

        if(bind(sock,(struct sockaddr*)&my_addr,sizeof(struct sockaddr))<0)

        {

                printf("bind error");

                return -1;

        }

        if(listen(sock,5)<0)

        {

                printf("listen error");

                return -1;

        }

……

}

 

系统调用accept()比较复杂。在远程的主机可能试图使用connect()连接你使用listen()正在监听的端口。但此连接将会在队列中等待,直到使用accept()处理它。调用accept()之后,将会返回一个全新的套接口文件描述符来处理这个单个的连接。这样,对于同一个连接来说,就有了两个文件描述符。原先的一个文件描述符正在监听你指定的端口,新的文件描述符可以用来调用send()recv()

调用的例子如下:

#include

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

第一个参数是正在监听端口的套接字文件描述符。第二个参数addr是指向本地的数据结构sockaddr_in的指针。调用connect()中的信息将存储在这里。通过它你可以了解哪个主机在哪个端口呼叫你。第三个参数同样可以使用sizeof(struct sockaddr_in)来获得。

如果出错,accept()也将返回-1

举例:

#include

#include

#include

#include

#include

#define MYPORT 6666

 

main()

{

        int sock,new_fd;

        int sin_size;

        struct sockaddr_in my_addr;

        struct sockaddr_in their_addr;

        sock=socket(AF_INET,SOCK_STREAM,0);

        if(sock<0)

        {

                printf("socket is error");

                return -1;

        }

        my_addr.sin_family=AF_INET;/*hostbyteorder*/

        my_addr.sin_port=htons(MYPORT);/*short,network byte order*/

        my_addr.sin_addr.s_addr=INADDR_ANY;

        bzero(&(my_addr.sin_zero),8);/*zero the rest of the struct*/

        if(bind(sock,(struct sockaddr*)&my_addr,sizeof(struct sockaddr))<0)

        {

                printf("bind error");

                return -1;

        }

        if(listen(sock,5)<0)

        {

                printf("listen error");

                return -1;

        }

        sin_size=sizeof(struct sockaddr_in);

        new_fd=accept(sock,&their_addr,&sin_size);

        if(new_fd<0)

        {

                printf("accept error");

                return -1;

        }

……

}

 

系统调用connect()的用法如下:

#include

#include

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

第一个参数还是套接口文件描述符,它是由系统调用socket()返回的。

第二个参数是serv_addr指向数据结构sockaddr的指针,其中包括目的端口和IP地址。

第三个参数可以使用sizeof(struct sockaddr)获得。

举例:

#include

#include

#include

#include

#include

 

#define DEST_PORT 6666

#define DEST_IP "127.0.0.1"

 

int main(int argc,char argv[])

{

        int sockfd;

        struct sockaddr_in dest_addr;

        sockfd=socket(AF_INET,SOCK_STREAM,0);

        if(sockfd<0)

        {

                printf("socket error");

                return -1;

        }

        dest_addr.sin_family=AF_INET;

        dest_addr.sin_port=htons(DEST_PORT);

        dest_addr.sin_addr.s_addr=inet_addr(DEST_IP);

        bzero(&(dest_addr.sin_zero),8);

        if(connect(sockfd,(struct sockaddr*)&dest_addr,sizeof(struct sockaddr))<0)

        {

                printf("connect error");

                return -1;

        }

        ……

}

 

同样,如果出错,connect()将会返回-1可能是连接超时或无法访问

──send()recv()

下面,我们将可以使用新创建的套接字文件描述符new_fd来调用send()recv()

系统调用send()的用法如下:

int send(int sockfd,const void* msg,int len,int flags);

第一个参数是你希望给发送数据的套接字文件描述符。它可以是你通过socket()系统调用返回的,也可以是通过accept()系统调用得到的。

第二个参数是指向你希望发送的数据的指针。

第三个参数是数据的字节长度。第四个参数标志设置为0

举例:

#include

#include

#include

#include

#include

 

#define DEST_PORT 6666

#define DEST_IP "127.0.0.1"

 

int main(int argc,char argv[])

{

        int sockfd;

        struct sockaddr_in dest_addr;

        char *msg="Hello World!";

        int len,bytes_sent;

        sockfd=socket(AF_INET,SOCK_STREAM,0);

        if(sockfd<0)

        {

                printf("socket error");

                return -1;

        }

        dest_addr.sin_family=AF_INET;

        dest_addr.sin_port=htons(DEST_PORT);

        dest_addr.sin_addr.s_addr=inet_addr(DEST_IP);

        bzero(&(dest_addr.sin_zero),8);

        if(connect(sockfd,(struct sockaddr*)&dest_addr,sizeof(struct sockaddr))<0)

        {

                printf("connect error");

                return -1;

        }

        len=strlen(msg);

        bytes_sent=send(sockfd,msg,len,0);

        ……

}

 

系统调用send()返回实际发送的字节数,这可能比你实际想要发送的字节数少。如果返回的字节数比要发送的字节数少,你在以后必须发送剩下的数据。当send()出错时,将返回-1

 

系统调用recv()的使用方法和send()类似:

int recv(int sockfd,void* buf,int len,unsigned int flags);

第一个参数是要读取的套接口文件描述符。

第二个参数是保存读入信息的地址。

第三个参数是缓冲区的最大长度。第四个参数设置为0

系统调用recv()返回实际读取到缓冲区的字节数,如果出错则返回-1

举例:

#include

#include

#include

#include

#include

#define MYPORT 6666

       

main() 

{      

        int sock,new_fd;

        int sin_size;

        struct sockaddr_in my_addr;

        struct sockaddr_in their_addr;

        int len,recv_size;

        char buf[255];

        sock=socket(AF_INET,SOCK_STREAM,0);

        if(sock<0)

        {

                printf("socket is error");

                return -1;

        }

        my_addr.sin_family=AF_INET;/*hostbyteorder*/

        my_addr.sin_port=htons(MYPORT);/*short,network byte order*/

        my_addr.sin_addr.s_addr=INADDR_ANY;

        bzero(&(my_addr.sin_zero),8);/*zero the rest of the struct*/

        if(bind(sock,(struct sockaddr*)&my_addr,sizeof(struct sockaddr))<0)

        {

                printf("bind error");

                return -1;

        }

        if(listen(sock,5)<0)

        {

                printf("listen error");

                return -1;

        }

        printf("listen right\n");

        sin_size=sizeof(struct sockaddr_in);

        new_fd=accept(sock,&their_addr,&sin_size);

        if(new_fd<0)

        {

                printf("accept error");

                return -1;

        }

        recv_size=recv(new_fd,buf,255,0);

……

}

 

这样使用上面的系统调用,就可以通过数据流套接口来发送和接受信息。 

 

UDP方式 sendto() recvfrom()

1.         sendto()

既然数据报套接字不是连接到远程主机的,在发送一个包之前需要目标地址,系统调用sendto()的用法如下:

int sendto(int sockfd, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen);

除了两个信息外,其余的和函数 send() 是一样 的。 to 是个指向数据结构 struct sockaddr 的指针,它包含了目的地的 IP 地址和端口信息。tolen 可以简单地设置为sizeof(struct sockaddr)。和函数 send() 类似,sendto()返回实际发送的字节数(它也可能小于你想要发送的字节数),或者在错误的时候返回 -1

举例:

#include

#include

#include

#include

#include

#include

 

#include

#include

#define DEST_PORT 6666

#define DEST_IP "127.0.0.1"

 

int main(int argc,char argv[])

{

        int sockfd;

        int write_buf,len;

        char *buf="Hello World!";

        struct sockaddr_in dest_addr;

        dest_addr.sin_family=AF_INET;

        dest_addr.sin_port=htons(DEST_PORT);

        dest_addr.sin_addr.s_addr=inet_addr(DEST_IP);

        sockfd=socket(AF_INET,SOCK_DGRAM,0);

        if(sockfd<0)

        {

                printf("socket error\n");

                return -1;

        }

        len=strlen(buf);

        write_buf=sendto(sockfd,buf,len,0,(struct sockaddr *)&dest_addr,sizeof(struct sockaddr));

        if(write_buf<0)

        {

                printf("send error\n");

                close(sockfd);

                return -1;

        }

……

}

 

2.         recvfrom()

recvfrom() 的定义是这样的:

int recvfrom(int sockfd, void *buf, int len, unsigned int flags, struct sockaddr *from, int *fromlen);

同理,除了两个增加的参数外,这个函数和 recv() 也是一样的。from 是一个指向局部数据结构 struct sockaddr 的指针,它的内容是源机器的 IP 地址和端口信息。fromlen 是个 int 型的局部指针,它的初始值为 sizeof(struct sockaddr)。函数调用返回后,fromlen 保存着实际储存在 from 中的地址的长度。

recvfrom() 返回收到的字节长度,或者在发生错误后返回 -1

举例:

#include

#include

#include

#include

#include

#include

 

#include

#include

#define MYPORT 6666

 

int main(int argc,char argv[])

{

        int sockfd;

        int recv_buf,len;

        char buf[256];

        struct sockaddr_in my_addr;

        struct sockaddr_in their_addr;

        my_addr.sin_family=AF_INET;

        my_addr.sin_port=htons(MYPORT);

        my_addr.sin_addr.s_addr=INADDR_ANY;

        bzero(&(my_addr.sin_zero),8);

        sockfd=socket(AF_INET,SOCK_DGRAM,0);

        if(sockfd<0)

        {

                printf("socket error\n");

                return -1;

        }

        if(bind(sockfd,(struct sockaddr *)&my_addr,sizeof(struct sockaddr))<0)

        {

                printf("bind error\n");

                close(sockfd);

                return -1;

        }

 

        int fromlen=sizeof(struct sockaddr);

        recv_buf=recvfrom(sockfd,buf,256,0,(struct sockaddr *)&their_addr,&fromlen);

        if(recv_buf<0)

        {

                printf("send error\n");

                close(sockfd);

                return -1;

        }

……

}

 

TCP 方式的区别:

需要指定发送/接收数据的对方(第五个参数to/from)函数返回实际发送/接收的字节数,返回-1 表示出错。

函数缺省是阻塞函数,直到发送/接收完毕或出错

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