您的位置:首页 > 编程语言

socket编程接口调用函数以及示例(C实现)

2010-06-11 23:00 148 查看

原文链接:http://gstarwd.javaeye.com/blog/539245

关键字: linux-socket c实现

socket()

我们使用系统调用 socket() 来获得文件描述符:
#include<sys/types.h>
#include<sys/socket.h>
int socket(int domain,int type,int protocol);
第一个参数 domain 设置为 “AF_INET” 。
第二个参数是套接口的类型: SOCK_STREAM 或
SOCK_DGRAM 。第三个参数设置为 0 。
系统调用 socket() 只返回一个套接口描述符,如果出错,则返回 -1 。
bind()
一旦你有了一个套接口以后,下一步就是把套接口绑定到本地计算机的某一个端口上。但如果你只想使用 connect() 则无此必要。
下面是系统调用 bind() 的使用方法:
#include<sys/types.h>
#include<sys/socket.h>
intbind(int sockfd,struct sockaddr*my_addr,int addrlen);
第一个参数 sockfd 是由 socket() 调用返回的套接口文件描述符。
第二个参数 my_addr 是指向数据结构 sockaddr 的指针。数据结构 sockaddr 中包括了关于你的地址、端口和 IP 地址的信息。
第三个参数 addrlen 可以设置成 sizeof(structsockaddr) 。
下面是一个例子:

C代码

#include<string.h>

#include<sys/types.h>

#include<sys/socket.h>

#defineMYPORT3490

main()

{

int sockfd;

struct sockaddr_inmy_addr;

sockfd=socket(AF_INET,SOCK_STREAM,0);/*do someerror checking!*/

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*/

/*don't forget your error checking for bind():*/

bind(sockfd,(struct sockaddr*)&my_addr,sizeof(struct sockaddr));

...

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

connect()
系统调用 connect() 的用法如下:
#include<sys/types.h>
#include<sys/socket.h>
int connect(int sockfd,struct sockaddr* serv_addr,int addrlen);
第一个参数还是套接口文件描述符,它是由系统调用 socket() 返回的。
第二个参数是 serv_addr 是指向数据结构 sockaddr 的指针,其中包括目的端口和 IP 地址。
第三个参数可以使用 sizeof(structsockaddr) 而获得。
下面是一个例子:
#include<string.h>
#include<sys/types.h>
#include<sys/socket.h>
#defineDEST_IP"132.241.5.10"
#defineDEST_PORT23
main()
{
intsockfd;
structsockaddr_indest_addr;/*will hold the destination addr*/
sockfd=socket(AF_INET,SOCK_STREAM,0);/*do some error checking!*/
dest_addr.sin_family=AF_INET;/*hostbyteorder*/
dest_addr.sin_port=htons(DEST_PORT);/*short,network byte order*/
dest_addr.sin_addr.s_addr=inet_addr(DEST_IP);
bzero(&(dest_addr.sin_zero),8);/*zero the rest of the struct*/
/*don'tforgettoerrorchecktheconnect()!*/
connect(sockfd,(structsockaddr*)&dest_addr,sizeof(struct sockaddr));
...
同样,如果出错, connect() 将会返回 -1 。

listen()

如果你希望不连接到远程的主机,也就是说你希望等待一个进入的连接请求,然后再处理它们。这样,你通过首先调用 listen() ,然后再调用 accept() 来实现。
系统调用 listen() 的形式如下:
intl isten(int sockfd,int backlog);
第一个参数是系统调用 socket() 返回的套接口文件描述符。
第二个参数是进入队列中允许的连接的个数。进入的连接请求在使用系统调用 accept() 应答之前要在进入队列中等待。这个值是队列中最多可以拥有的请求的个数。大多数系统的缺省设置为 20 。你可以设置为 5 或者 10 。当出错时, listen() 将会返回 -1 值。
当然,在使用系统调用 listen() 之前,我们需要调用 bind() 绑定到需要的端口,否则系统内核将会让我们监听一个随机的端口。所以,如果你希望监听一个端口,下面是应该使用的系统调用的顺序:
socket();
bind();
listen();
/*accept()goeshere*/

accept()

系统调用 accept() 比较起来有点复杂。在远程的主机可能试图使用 connect() 连接你使用
listen() 正在监听的端口。但此连接将会在队列中等待,直到使用 accept() 处理它。调用 accept()
之后,将会返回一个全新的套接口文件描述符来处理这个单个的连接。这样,对于同一个连接
来说,你就有了两个文件描述符。原先的一个文件描述符正在监听你指定的端口,新的文件描
述符可以用来调用 send() 和 recv() 。
调用的例子如下:
#include<sys/socket.h>
intaccept(intsockfd,void*addr,int*addrlen);
第一个参数是正在监听端口的套接口文件描述符。第二个参数 addr 是指向本地的数据结构
sockaddr_in 的指针。调用 connect() 中的信息将存储在这里。通过它你可以了解哪个主机在哪个
端口呼叫你。第三个参数同样可以使用 sizeof(structsockaddr_in) 来获得。
如果出错, accept() 也将返回 -1 。下面是一个简单的例子:

C代码

#include<string.h>

#include<sys/types.h>

#include<sys/socket.h>

#defineMYPORT3490/*theportuserswillbeconnectingto*/

#defineBACKLOG10/*howmanypendingconnectionsqueuewillhold*/

main()

{

intsockfd,new_fd;/*listenonsock_fd,newconnectiononnew_fd*/

structsockaddr_inmy_addr;/*myaddressinformation*/

structsockaddr_intheir_addr;/*connector'saddressinformation*/

intsin_size;

sockfd=socket(AF_INET,SOCK_STREAM,0);/*dosomeerrorchecking!*/

my_addr.sin_family=AF_INET;/*hostbyteorder*/

my_addr.sin_port=htons(MYPORT);/*short,networkbyteorder*/

my_addr.sin_addr.s_addr=INADDR_ANY;/*auto-fillwithmyIP*/

bzero(&(my_addr.sin_zero),8);/*zerotherestofthestruct*/

/*don'tforgetyourerrorcheckingforthesecalls:*/

bind(sockfd,(structsockaddr*)&my_addr,sizeof(structsockaddr));

listen(sockfd,BACKLOG);

sin_size=sizeof(structsockaddr_in);

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

...

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

send() recv()

系统调用 send() 的用法如下:
int send(int sockfd,const void* msg,int len,int flags);
第一个参数是你希望给发送数据的套接口文件描述符。它可以是你通过 socket() 系统调用返回的,也可以是通过 accept() 系统调用得到的。
第二个参数是指向你希望发送的数据的指针。
第三个参数是数据的字节长度。第四个参数标志设置为 0 。
下面是一个简单的例子:
char*msg="Beejwashere!";
intlen,bytes_sent;
..
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 。
这样使用上面的系统调用,你可以通过数据流套接口来发送和接受信息。

sendto() recvfrom()

因为数据报套接口并不连接到远程的主机上,所以在发送数据包之前,我们必须首先给出目的地址,请看:
int sendto(int sockfd,const void* msg,int len,unsigned int flags,
conststruct sockaddr*to,inttolen);
除了两个参数以外,其他的参数和系统调用 send() 时相同。
参数 to 是指向包含目的 IP 地址和端口号的数据结构 sockaddr 的指针。
参数 tolen 可以设置为 sizeof(structsockaddr) 。
系统调用 sendto() 返回实际发送的字节数,如果出错则返回 -1 。
系统调用 recvfrom() 的使用方法也和 recv() 的十分近似:
int recvfrom(int sockfd,void* buf,int len,unsigned int flags
struct sockaddr* from,int* fromlen);
参数 from 是指向本地计算机中包含源 IP 地址和端口号的数据结构 sockaddr 的指针。
参数 fromlen 设置为 sizeof(struct sockaddr) 。
系统调用 recvfrom() 返回接收到的字节数,如果出错则返回 -1 。

close() shutdown()

你可以使用 close() 调用关闭连接的套接口文件描述符:
close(sockfd);
这样就不能再对此套接口做任何的读写操作了。
使用系统调用 shutdown() ,可有更多的控制权。它允许你在某一个方向切断通信,或者切断双方的通信:
int shutdown(int sockfd,int how);
第一个参数是你希望切断通信的套接口文件描述符。第二个参数 how 值如下:
0—Furtherreceivesaredisallowed
1—Furthersendsaredisallowed
2—Furthersendsandreceivesaredisallowed(likeclose())
shutdown() 如果成功则返回 0 ,如果失败则返回 -1 。

getpeername()

这个系统的调用十分简单。它将告诉你是谁在连接的另一端:
#include<sys/socket.h>
int getpeername(int sockfd,struct sockaddr* addr,int* addrlen);
第一个参数是连接的数据流套接口文件描述符。
第二个参数是指向包含另一端的信息的数据结构 sockaddr 的指针。
第三个参数可以设置为 sizeof(structsockaddr) 。
如果出错,系统调用将返回 -1 。
一旦你获得了它们的地址,你可以使用 inet_ntoa() 或者 gethostbyaddr() 来得到更多的信息。

gethostname()

系统调用 gethostname() 比系统调用 getpeername() 还简单。它返回程序正在运行的计算机的名字。系统调用 gethostbyname() 可以使用这个名字来决定你的机器的 IP 地址。
下面是一个例子:
#include<unistd.h>
int gethostname(char*hostname,size_tsize);
如果成功, gethostname 将返回 0 。如果失败,它将返回 -1 。
SOCKET C 程序代码
Makefile 文件

s: app_service.c

gcc -o s app_service.c

c: app_client.c

gcc -o c app_client.c

app_client.c 文件

C代码

//客户端程序代码如下:

#include<stdio.h>

#include <stdlib.h>

#include <errno.h>

#include <string.h>

#include <netdb.h>

#include <sys/types.h>

#include <netinet/in.h>

#include <sys/socket.h>

#define SERVPORT 3333

#define MAXDATASIZE 100 // 每次最大数据传输量

main(int argc, char *argv[])

{

int sockfd, recvbytes;

char buf[MAXDATASIZE];

struct hostent *host;

struct sockaddr_in serv_addr;

if (argc < 2) {

fprintf(stderr,"Please enter the server's hostname!/n");

exit(1);

}

if ((host = gethostbyname(argv[1])) == NULL) {

herror("gethostbyname出错!");

exit(1);

}

if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){

perror("socket创建出错!");

exit(1);

}

serv_addr.sin_family = AF_INET;

serv_addr.sin_port = htons(SERVPORT);

serv_addr.sin_addr = *((struct in_addr *)host->h_addr);

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

if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(struct sockaddr)) == -1) {

perror("connect出错!");

exit(1);

}

if ((recvbytes = recv(sockfd, buf, MAXDATASIZE, 0)) ==-1) {

perror("recv出错!");

exit(1);

}

buf[recvbytes] = '/0';

printf("Received: %s",buf);

close(sockfd);

}

/*

客户端程序首先通过服务器域名获得服务器的IP地址,然后创建一个socket,调用connect函数与服务器建立连接,连接成功之后接收从服务器发送过来的数据,最后关闭socket。

  函数gethostbyname()是完成域名转换的。由于IP地址难以记忆和读写,所以为了方便,人们常常用域名来表示主机,这就需要进行域名和IP地址的转换。函数原型为:

  struct hostent *gethostbyname(const char *name);

  函数返回为hosten的结构类型,它的定义如下:

  struct hostent {

char *h_name; // 主机的官方域名

char **h_aliases; // 一个以NULL结尾的主机别名数组

int h_addrtype; // 返回的地址类型,在Internet环境下为AF-INET

int h_length; // 地址的字节长度

char **h_addr_list; // 一个以0结尾的数组,包含该主机的所有地址

};

  #define h_addr h_addr_list[0] //在h-addr-list中的第一个地址

  当 gethostname()调用成功时,返回指向struct hosten的指针,当调用失败时返回-1。当调用gethostbyname时,你不能使用perror()函数来输出错误信息,

应该使用herror()函数来输出。

  无连接的客户/服务器程序的在原理上和连接的客户/服务器是一样的,两者的区别在于无连接的客户/服务器中的客户一般不需要建立连接,而且在发送接收

数据时,需要指定远端机的地址。

*/

app_service.c 文件

C代码

/*

面向连接的Socket实例

  代码实例中的服务器通过socket连接向客户端发送字符串"Hello, you are connected!"。只要在服务器上运行该服务器软件,在客户端运行客户软件,客户端就会收到该字符串。

  该服务器软件代码如下:

*/

#include <stdio.h>

#include <stdlib.h>

#include <errno.h>

#include <string.h>

#include <sys/types.h>

#include <netinet/in.h>

#include <sys/socket.h>

#include <sys/wait.h>

#define SERVPORT 3333 // 服务器监听端口号

#define BACKLOG 10 // 最大同时连接请求数

main()

{

int sockfd, client_fd; // sock_fd:监听socket;client_fd:数据传输socket

struct sockaddr_in my_addr; // 本机地址信息

struct sockaddr_in remote_addr; // 客户端地址信息

int sin_size;

if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {

perror("socket创建出错!"); exit(1);

}

my_addr.sin_family = AF_INET;

my_addr.sin_port = htons(SERVPORT);

my_addr.sin_addr.s_addr = INADDR_ANY;

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

if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == -1) {

perror("bind出错!");

exit(1);

}

if (listen(sockfd, BACKLOG) == -1) {

perror("listen出错!");

exit(1);

}

while(1) {

sin_size = sizeof(struct sockaddr_in);

if ((client_fd = accept(sockfd, (void *)&remote_addr, &sin_size)) == -1) {

perror("accept出错");

continue;

}

printf("received a connection from %s/n", inet_ntoa(remote_addr.sin_addr));

if (!fork()) { /* 子进程代码段 */

if (send(client_fd, "Hello, you are connected!/n", 26, 0) == -1) {

perror("send出错!");

close(client_fd);

exit(0);

}

}

close(client_fd);

}

}

/*

 服务器的工作流程是这样的:首先调用socket函数创建一个Socket,然后调用bind函数将其与本机地址以及一个本地端口号绑定,然后调用 listen在相应的socket上监听,当accpet接收到一个连接服务请求时,将生成一个新的socket。服务器显示该客户机的IP地址,并通过新的socket向客户端发送字符串"Hello,you are connected!"。最后关闭该socket。

  代码实例中的fork()函数生成一个子进程来处理数据传输部分,fork()语句对于子进程返回的值为0。所以包含fork函数的if语句是子进程代码部分,它与if语句后面的父进程代码部分是并发执行的。

*/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐