用C语言编写Socket程序.doc_第1页
用C语言编写Socket程序.doc_第2页
用C语言编写Socket程序.doc_第3页
用C语言编写Socket程序.doc_第4页
用C语言编写Socket程序.doc_第5页
已阅读5页,还剩23页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

用C语言编写Socket程序本文的目的在于为初学者提供一个快速的入门指导,用来迅速熟悉用C语言来编写Internet网络应用程序。本文假设读者已经具备了C语言的基本知识和语法,并且读者有使用Uinx/Linux的经验。尽管Uinx/Linux的Socket编程与在Windows下的有一些不同的地方,但是在此我并不想展开。另外,本文所有的程序都在Red Hat 5.2下编译通过,并且在glibc 2.0.7和libc 5.3.12两种环境下都没有问题。现在就开始我们的教程吧:)。对一个程序员而言,sockets和底层的文件描述符非常类似(可以在sockets里使用read()和write()函数),尽管建立一个socket比打开,读取和写入一个文件更为麻烦,但这是由于网络连接比单纯的本地硬盘的读写复杂的多所造成的。通常,sockets用来实现客户机/服务器对。服务器的任务是监听某个特定的端口,当接收到客户端的服务请求时完成相应的服务;客户机的任务是请求服务器完成事先设定好的服务。作为入门级的文章,我们在这里不会使用所有的socket类型和功能,但是我们会向读者提供足够的信息。现在,就让我们开始吧。=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=建立一个socket:socket()你所要学的socket编程的第一件事就是用socket()建立一个socket:- -#include #include int socket(int af, int type, int protocol)- -int af代表地址族或者称为socket所代表的域,通常有两个选项: AF_UNIX - 只在单机上使用。 AF_INET - 可以在单机或其他使用DARPA协议(UDP/TCP/IP)的异种机通信。int type代表你所使用的连接类型,通常也有两种情况: SOCK_STREAM - 用来建立面向连接的sockets,可以进行可靠无误的的数据传输 SOCK_DGRAM - 用来建立没有连接的sockets,不能保证数据传输的可靠性。在本文中,我们着重使用AF_INET地址族和SOCK_STREAM连接类型。int protocol通常设定为0。这样的目的是使系统选择默认的由协议族和连接类型所确定的协议。这个函数的返回值是一个文件描述句柄,如果在此期间发生错误则返回-1并且设定了相应的errno。- -#include #include int sockfd /* soon to be socket file descriptor */sockfd = socket(AF_INET, SOCK_STREAM, 0)/* error checking here */- -如果执行成功,我们就拥有了一个socket的文件句柄,通过这个句柄就可以访问Internet了。=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=名字绑定socket: bind()下一步要完成的就是名字绑定工作了:- -#include #include int bind(int sockfd, struct sockaddr *name, int namelen)- -在这个函数里,sockfd是从socket()调用得到的文件描述句柄。name是一个指向sockaddr类型结构的一个指针。如果地址族被设定为AF_UNIX,这个类型的定义是如下所示:- -struct sockaddr u_short sa_family;char sa_data14;- -在这个结构种,name.sa_family应当被设定为AF_UNIX。name.sa_data应当包含最长为14个字节的文件名,这个文件名用来分配给len给出了文件名的具体长度。- -#include #include struct sockaddr name;int sockfd;name.sa_family = AF_UNIX;strcpy(name.sa_data, /tmp/whatever);sockfd = socket(AF_UNIX, SOCK_STREAM, 0)/* error checking code here */bind(sockfd, &name, strlen(name.sa_data) + sizeof(name.sa_family)/* error checking code here */- -如果调用成功,则返回值为0,如果调用失败返回值为-1,并设定相应的错误代码errno。现在,让我们在使用另一种结构,它是在使用AF_INET地址族的时候使用的。- -struct sockaddr_in short int sin_family; /* Address family */unsigned short int sin_port; /* Port number */struct in_addr sin_addr; /* Internet address */unsigned char sin_zero8; /* Same size as struct sockaddr */;- -看起来它比前一个大多了,但要掌握它并不十分困难。- -#include #include #include #include int sockfd, port = 23;struct sockaddr_in my_addr;if(sockfd=socket(AF_INET, SOCK_STREAM, 0) = -1)printf(Socket Error, %dn, errno);exit(1);my_addr.sin_family = AF_INET; /* host byte order */my_addr.sin_port = htons(port); /* see man htons for more information */my_addr.sin_addr.s_addr = htonl(INADDR_ANY); /* get our address */bzero(&(my_addr.sin_zero), 8); /* zero out the rest of the space */if(bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)= -1)printf(Bind Error, %dn, errno);close(sockfd);exit(1);- -现在,如果没有问题的话,我们建立的socket就有一个名字了!相反,如果不成功,它会设定相应的错误代码,并使程序退出。这里需要说明的是,如果你的计算机不想和别人的计算机连接,那么完全没有必要使用bind()。对于端口的绑定,在服务器而言是不合适的,它只应该在客户机上实现。=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=远程连接: connect()这是和别的计算机连接所必须的一步。- -#include #include int connect(int sockfd, struct sockaddr *serv_addr, int addrlen); - -sockfd是我们建立的文件描述句柄,serv_addr是一个sockaddr结构,包含目的的地址和端口号,addrlen 被设定为sockaddr结构的大小。- -#include #include #include #define DEST_IP 0#define DEST_PORT 23main()int sockfd;struct sockaddr_in dest_addr; /* will hold the destination addr */sockfd = socket(AF_INET, SOCK_STREAM, 0); /* do some error checking! */dest_addr.sin_family = AF_INET; /* host byte order */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 */connect(sockfd, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr);/* error checking code here */* more code .*/- -同样,connect()在调用返回后,如果返回值为0则表明成功,如果是1则说明有错误,并且同时设定了相应的错误代码。由于我们现在不关心具体和那个端口连接,所以在上面的例程里我们没有调用了bind()函数。=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=监听: listen()当我们需要建立一个服务器的时候,我们需要有一种手段来监听输入的请求,而listen()函数正是提供这个功能。- -#include #include int listen(int sockfd, int backlog);- -参数backlog是指一次可以监听多少个连接它的调用返回结果和上述的几个函数是一样的,这里就不多说了。值得一提的是,在这里,我们需要建立一个绑定,用来接收特定端口的服务请求。- -socket(); /* to create out socket file descriptor */bind(); /* to give our socket a name */listen(); /* listen for connection */- -=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=接受连接: accept()好了,现在开始实质性的工作了。当有人试图从我们打开的端口登陆进来时我们应该响应他,这个时候就要用到accept()函数了。- -#include #include int accept(int sockfd, void *addr, int *addrlen);- -函数调用所需的参数都是我们所熟悉的:)- -#include #include #include #define MYPORT 1500 /* the port users will be connecting to */#define BACKLOG 5 /* how many pending connections queue will hold */main()int sockfd, new_fd; /* listen on sock_fd, new connection on new_fd */struct sockaddr_in my_addr; /* my address information */struct sockaddr_in their_addr; /* connectors address information */int sin_size;sockfd = socket(AF_INET, SOCK_STREAM, 0); /* do some error checking! */my_addr.sin_family = AF_INET; /* host byte order */my_addr.sin_port = htons(MYPORT); /* short, network byte order */my_addr.sin_addr.s_addr = INADDR_ANY; /* auto-fill with my IP */bzero(&(my_addr.sin_zero), 8); /* zero the rest of the struct */* did you remember your error checking? */bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr);listen(sockfd, BACKLOG);sin_size = sizeof(struct sockaddr_in);new_fd = accept(sockfd, &their_addr, &sin_size);- -这里我们要注意的是:我们用new_fd来完成所有的接收和发送的操作。如果在只有一个连接的情况下你可以关闭原来的sockfd用来防止更多的输入请求。=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=输入和输入的完成: send() and recv()在我们完成了上述的工作后,最后一步就是传输数据了:)。在这里,我们通过send()和recv()来实现。- -#include #include int send(int sockfd, const void *msg, int len, int flags);int recv(int sockfd, void *buf, int len, unsigned int flags);- -send():sockfd - socket file descriptormsg - message to sendlen - size of message to sendflags - read man send for more info, set it to 0 for now :)recv():sockfd - socket file descriptorbuf - data to receivelen - size of bufflags - same as flags in send()send() 例程:- -char *msg = Hey there people;int len, send_msg;/* code to create(), bind(), listen() and accept() */ len = strlen(msg);bytes_sent = send(sockfd, msg, len, 0);- -recv() 例程:- -char *buf;int len, recv_msg;/* code to create(), bind(), listen() and accept() */len = strlen(buf);recv_msg = recv(sockfd, buf, len, 0);- -如果你使用的连接类型是SOCK_DGRAM,那么应该使用sendto()和recvfrom()来实现数据传输。=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=最后一步: close() and shutdown()当传输结束时,应当关闭连接。- -#include /* all you code */close(sockfd);- -更保险的方法是用shutdown()来关闭连接。- -int shutdown(int sockfd, int how)- -参数how的选择:1 - 不允许接收更多的数据2 - 不允许发送更多的数据3 - 不允许接收和发送更多的数据(和close()一样)一切就是这么简单:)=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=你是谁: getpeerbyname()可能你还想知道是谁正在和你连接,那么看下面:- -#include int getpeername(int sockfd, struct sockaddr *addr, int *addrlen);- -参数addr是一个指向struct sockaddr或者struct sockaddr_in的指针。如果执行成功,我们就得到了对方的地址了,然后用inet_ntoa()用gethostbyaddr()来得到对方更多的信息。如果还想知道更多的,请参阅RFC 1413。=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=我是谁: gethostname()- -#include int gethostname(char *hostname, size_t size);- -hostname是一个存放主机名字的字符数组返回的hostname可以作为gethostbyname()的参数,这样又可以得到自己的IP地址了。=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=我的IP是多少? 现在把我们所学的集中起来,逐步就可以完成一个实用的程序了。还是来研究一下如下的情况:$ telnet Trying 76 (not the real address but Im too lazy to try :)我们看到,telnet程序所做的第一件事情是域名解析!这个工作是由gethostbyname()完成的。- -#include struct hostent *gethostbyname(const char *name);- -一个特殊的结构hostent:- -struct hostent char *h_name;char *h_aliases;int h_addrtype;int h_length;char *h_addr_list;#define h_addr h_addr_list0- -这个结构可以被分为:h_name - 正式的机器名h_aliases - 一个以NULL结尾的字符串,表示机器的别名。 h_addrtype - 地址所使用的类型,通常是AF_INET。 h_length - 用字节数表示的地址长度。h_addr_list - 一个以0结尾的数组,表示机器的网络地址,以网络的字节顺序排列。 h_addr - 在h_addr_list里的第一个地址。gethostbyname()返回一个指向hostent结构的指针或者是一个NULL指针表示错误(尽管如此,错误代码没有设定!)。现在我们就来完成我们的DNS程序:- -#include #include #include #include #include #include int main(int argc, char *argv)struct hostent *h;if (argc != 2) /* error checking on the command line */fprintf(stderr,Usage: getip n);exit(1);if (h=gethostbyname(argv1) = NULL) /* get the host info */herror(gethostbyname);exit(1);printf(Host name : %sn, h-h_name);printf(IP Address : %sn,inet_ntoa(*(struct in_addr *)h-h_addr);return 0;- -用gcc -o getip getip.c就可以了完成了。=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=客户机和服务器程序好了,让我们用一个小的客户机-服务器应用来结束本文的讨论。这个小程序的目的就是让一个用户和服务器连接,接收预先定义好的数据,然后断开。不过这个程序中有很多错误需要你来修改,作为你的作业来完成:)。- - socket/server.c/* SERVER PROGRAM */#include #include #include #include #include #include #include #include #define PORT 1500 /* the port users will be connecting to */#define BACKLOG 5 /* how many pending connections queue will hold */main()int sockfd, new_fd; /* listen on sock_fd, new connection on new_fd */struct sockaddr_in my_addr; /* our address information */struct sockaddr_in their_addr; /* their address information */int sin_size;sockfd = socket(AF_INET, SOCK_STREAM, 0);/* remember to error check (-1 on error) */my_addr.sin_family = AF_INET; /* host byte order */my_addr.sin_port = htons(PORT); /* short, network byte order */my_addr.sin_addr.s_addr = INADDR_ANY; /* auto-fill with my IP */bzero(&(my_addr.sin_zero), 8); /* zero the rest of the struct */bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr);listen(sockfd, BACKLOG)while(1) /* start out accept() loop */sin_size = sizeof(struct sockaddr_in);new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size)printf(server: got connection from %sn, inet_ntoa(their_addr.sin_addr);fork(); /* this is the child process */send(new_fd, Hello, world!n, 14, 0)close(new_fd);exit(0);while(waitpid(-1,NULL,WNOHANG) 0); /* clean up child processes */* END SERVER PROGRAM, REMEMBER TO DO YOUR ERROR CHECKING */ socket/client.c/* CLIENT PROGRAM */#include #include #include #include #include #include #include #include #define PORT 1500 /* the port client will be connecting to */#define MAXDATASIZE 100 /* max number of bytes we can get at once */int main(int argc, char *argv)int sockfd, numbytes; char bufMAXDATASIZE;struct hostent *he;struct sockaddr_in their_addr; /* connectors address information */if (argc != 2) fprintf(stderr,Usage: client n);exit(1);he = gethostbyname(argv1); /* get the host info */* did you check for errors? */sockfd = socket(AF_INET, SOCK_STREAM, 0);their_addr.sin_family = AF_INET; /* host byte order */their_addr.sin_port = htons(PORT); /* short, network byte order */their_addr.sin_addr = *(struct in_addr *)he-h_addr);bzero(&(their_addr.sin_zero), 8); /* zero the rest of the struct */connect(sockfd, (struct sockaddr *)&their_addr, sizeof(struct sockaddr);numbytes = recv(sockfd, buf, MAXDATASIZE, 0);bufnumbytes = 0;printf(Received: %s,buf);close(sockfd);return 0;/* END CLIENT.YOU CHECKED FOR ERRORS RIGHT? :) */=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=以上是关于SOCKET编程的一些入门的指导,如果还想深入了解的话可以从以下的地方找到资料:-Internetworking with TCP/IP, volumes I-III by Douglas E. Comer and DavidL. Stevens. Published by Prentice Hall. Second edition ISBNs: 0-13-468505-9, 0-13-472242-6, 0-13-474222-2. There is a third edition of this set whichcovers IPv6 and IP over ATM. Using C on the UNIX System by David A. Curry. Published by OReilly &Associates, Inc. ISBN 0-937175-23-4.TCP/IP Network Administration by Craig Hunt. Published by OReilly &Associates, Inc. ISBN 0-937175-82-X. TCP/IP Illustrated, volumes 1-3 by W. Richard Stevens and Gary R. Wright.Published by Addison Wesley. ISBNs: 0-201-63346-9, 0-201-63354-X, 0-201-63495-3. UNIX Network Programming by W. Richard Stevens/ Published by PrenticeHall. ISBN 0-13-949876-1. -相关的RFC的资料:-RFC-768 - The User Datagram Protocol (UDP)(/rfc/rfc768.txt)RFC-791 - The Internet Protocol (IP)(/rfc/rfc791.txt)RFC-793 - The Transmission Control Protocol (TCP)(/rfc/rfc793.txt)RFC-854 - The Telnet Protocol(/rfc/rfc854.txt)RFC-951 - The Bootstrap Protocol (BOOTP)(/rfc/rfc951.txt)RFC-1350 - The Trivial File Transfer Protocol (TFTP)(/rfc/rfc1350.txt)-*by Lamagra repaired by solo Copyright by isbase /*通过文章,自己写了一个C发socket的客户端,而服务器端是一个用delphi写好的ServerSocket。调试此程序时请先启动服务器的socket程序以下代码在IBM AIX5.2 + gcc通过#include #include #include #include #include #include #include #include #define DEST_IP 42#define DEST_PORT 45678int main(int argc, char *argv)int sockfd, port = 45678;struct sockaddr_in my_addr;struct sockaddr_in dest_addr; /* will hold the destination addr */检查socket是否连通if(sockfd=socket(AF_INET, SOCK_STREAM, 0) = -1) printf(Socket Error, %dn, errno); exit(1);/绑定socketmy_addr.sin_family = AF_INET; /* host byte order */my_addr.sin_port = htons(port); /* see man htons for more information */my_addr.sin_addr.s_addr = htonl(INADDR_ANY); /* get our address */bzero(&(my_addr.sin_zero), 8); /* zero out the rest of the space */if(bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr) = -1) printf(Bind Error, %dn, errno); close(sockfd); exit(1);/远程连接dest_addr.sin_family = AF_INET; /* host byte order */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 */if(connect(sockfd, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr) = -1) printf(Connection Error, %dn, errno); close(sockfd); exit(1);/发送char *msg = 花果山,-,2级,性能告警,#559944;int len, send_msg, bytes_sent;len = strlen(msg);bytes_sent = send(sockfd, msg, len, 0);/关闭close(sockfd);用C语言编写Socket程序-入门篇本文的目的在于为初学者提供一个快速的入门指导,用来迅速熟悉用C语言来编写Internet 网络应用程序。本文假设读者已经具备了C语言的基本知识和语法,并且读者有使用Uinx/Linux的经验。尽管Uinx/Linux的Socket编程 与在Windows下的有一些不同的地方,但是在此我并不想展开。另外,本文所有的程序都在Red Hat 5.2下编译通过,并且在 glibc 2.0.7和libc 5.3.12两种环境下都没有问题。现在就开始我们的教程吧:)。 对一个程序员而言,sockets和底层的文件描述符非常类似(可以在sockets里使用read()和write()函数),尽管建立一个socket比打开,读取和写入一个文件更为麻烦,但这是由于网络连接比单纯的本地硬盘的读写复杂的多所造成的。 通常,sockets用来实现客户机/服务器对。服务器的任务是监听某个特定的端口,当接收到客户端的服务请求时完成相应的服务;客户机的任务是请求服务器完成事先设定好的服务。 作为入门级的文章,我们在这里不会使用所有的socket类型和功能,但是我们会向读者提供足够的信息。现在,就让我们开始吧。 =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+= 建立一个socket:socket() 你所要学的socket编程的第一件事就是用socket()建立一个socket: - - #include #include int socket(int af, int type, int protocol) - - int af代表地址族或者称为socket所代表的域,通常有两个选项: AF_UNIX - 只在单机上使用。 AF_INET - 可以在单机或其他使用DARPA协议(UDP/TCP/IP)的异种机通信。 int type代表你所使用的连接类型,通常也有两种情况: SOCK_STREAM - 用来建立面向连接的sockets,可以进行可靠无误的的数据传输 SOCK_DGRAM - 用来建立没有连接的sockets,不能保证数据传输的可靠性。在本文中,我们着重使用AF_INET地址族和SOCK

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论