网络编程常见函数解析

最近重学了网络编程,顺便修改了国嵌的网络编程视频一节的tcp服务器程序,颇有心得。

先介绍函数以供以后使用。

函数1int socket(int domain, int type, int protocol);(以后的每个函数后面的英文都是摘自posix标准)

The socket() function shall create an unbound socket in communications domain, and return file descriptor that can be used in later function calls that operate on sockets.

其中第一个形参是协议族,如AF_INETIP协议族),第二个形参是socket的类别,其中有三种:SOCK_STREAM(用于TCP)、SOCK_DGRAM(用于UDP)、SOCK_RAW(主要用于新网络协议的测试),返回值是socket描述符。

例子:int sockfd=socket(AF_INETSOCK_STREAM0);

函数2int bind(int socket, const struct sockaddr *address,socklen_t address_len);

The bind() function shall assign local socket address address to socket identified by descriptor socket that has no local socket address assigned. Sockets created with the socket() function are initially unnamed; they are identified only by their address family.

第一个形参是你刚才创建的套接字描述符,(这个是用在服务器上的函数),第二个形参是结构体struct sockaddr,注意的是我们经常使用sockaddr_in结构体,所以在这里使用时必须强制类型转换。第三个形参是对结构体求大小,用sizeof。这里我说一个困惑:不知道为什么,如果在函数的开头定义部分,将一个指针指向sockaddr_in结构体即struct sockaddr_in *server_sock,会出现段错误的提示,执行不了,不知为什么??

例子:

int bind_ret=bind(sockfd,(struct sockaddr *)(&server_sock),sizeof(struct sockaddr_in));

函数3int listen(int socket, int backlog);

The listen() function shall mark connection-mode socket, specified by the socket argument, as accepting connections.

第一个形参一样,不讲。第二个是指定你的服务器能处理的最大连接数目。

例子: listen_ret=listen(sockfd,MAX_CONNUM)

函数4int accept(int socket, struct sockaddr *restrict address,socklen_t *restrict address_len);

The accept() function shall extract the first connection on the queue of pending connections, create new socket with the same socket type protocol and address family as the specified socket, and allocate new file descriptor for that socket.

第一个形参是你服务器创建的套接字描述符(这个函数也是用在服务器上),第二个形参是你的客户端的sockaddr(sockaddr_in)结构体,第三个形参是该结构体的大小。

例子:

accept_sockfd=accept(sockfd,(struct sockaddr *)(&client_sock),&size))

函数5int connect(int socket, const struct sockaddr *address,socklen_t address_len);
The connect() function shall attempt to make connection on socket. 

第一个形参是你客户端创建的套接字描述符,第二个形参是服务器端的sockaddr结构体,之后第三个是该结构体长度。这个函数用在客户端上,注意了,在客户端上我们需要填写的服务器端的sockaddr结构体,这样与服务器端相匹配到时才能按照网咯地址找到服务器端。

至于读写我们可以用readwrite或是recvsend函数,这里就不介绍了。

例子:

connect_ret=connect(sockfd,(struct sockaddr *)(&server_sock),sizeof(struct sockaddr_in)

函数6htonshtonlntonsntonl。原型分别是:

#include <arpa/inet.h>

uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);

these functions shall convert 16-bit and 32-bit quantities between network byte order and host byte order.On some implementations, these functions are defined as macros.The uint32_t and uint16_t types are defined in <inttypes.h>.

形参是你想转换的值。

函数7、地址转换函数:

#include <arpa/inet.h>

in_addr_t inet_addr(const char *cp);
char *inet_ntoa(struct in_addr in);
The inet_addr() function shall convert the string pointed to by cp, in the standard IPv4 dotted decimal notation, to an integer value suitable for use as an Internet address.

The inet_ntoa() function shall convert the Internet host address specified by in to a string in the Internet standard dot notation.

例子:

函数8、获取主机信息的结构体函数:

struct hostent *gethostbyname(const char *name);

struct hostent *gethostbyaddr(const void *addr, socklen_t len,
 int type);

These functions shall retrieve information about hosts. This information is considered to be stored in database that can be accessed sequentially or randomly. Implementation of this database is unspecified. 

例子:structhosten *host; host=gethostbyname(argv[1]);

以上就是网络编程需要常用的函数,这是应用层的。以下是我的改编函数,还有很多不足,原理编写一个简单的并发服务器,可以同时处理多个客户端的要求,并且在客户端中,如果你什么都不输入而直接按ENTER键的话会提示你是否真要退出,若否则按ENTER键确认默认选择,否则按y退出,在服务器端也会判断客户端是否真要退出,判断不成立的话继续接受客户端的信息,可能写的不是很好,但愿看得懂!

服务器函数:

#include <stdlib.h>

#include <stdio.h>

#include <error.h>

#include <strings.h>

#include <netdb.h>

#include <sys/types.h>

#include <netinet/in.h>

#include <sys/socket.h>

#define portnumber 12345

#define MAX_BUF 1024

#define MAX_CONNUM 10

int main(int argc,char *argv[])

{

int sockfd;

struct sockaddr_in server_sock;

struct sockaddr_in client_sock;

char buff[MAX_BUF];

int nbytes;

int listen_ret;

int client_num=0;

 

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

{

  fprintf(stderr,"create server socket fail\n");

   exit(1);

}

 

bzero(&server_sock,sizeof(&server_sock));

server_sock.sin_family=AF_INET;

server_sock.sin_port=htons(portnumber);

server_sock.sin_addr.s_addr=htonl(INADDR_ANY);

//server_sock.sin_addr.s_addr=inet_addr("192.168.0.117");

       int bind_ret=bind(sockfd,(struct sockaddr *)(&server_sock),sizeof(struct sockaddr_in));

if (bind_ret==-1)

{

perror("bind failed\n");

exit(1);

}

 

if( listen_ret=listen(sockfd,MAX_CONNUM)<0)

{

perror("listen failed\n");

exit(1);

}

while(1)

{

int accept_sockfd;

int size=sizeof(struct sockaddr_in);

if((accept_sockfd=accept(sockfd,(struct sockaddr *)(&client_sock),&size))<0)

{

fprintf(stderr,"server accept error!!\n");

exit(1);

}

client_num++;

printf("server get connection from %s,the num of client is %d\n",inet_ntoa(client_sock.sin_addr),client_num);

if(fork()==0)

{

do

     {

nbytes=read(accept_sockfd,buff,MAX_BUF);

   buff[nbytes-1]='\0';

 

if(buff[0]=='\0')

    {

printf("this time we recieve nothing from the client!\nwe wait the client quit or not\n");

          nbytes=read(accept_sockfd,buff,MAX_BUF);

buff[nbytes-1]='\0';

if(buff[0]!='\0')

   printf("client %d still not quit,we can continue recieve msg\n",client_num);

    }

else

      printf("we recieve the string of %s from the client %d\n",buff,client_num);

 }while(buff[0]!='\0');   

close(accept_sockfd);

printf("so the num of client %d has quit!\n",client_num);

exit(0);

}

 

}

close(sockfd);

exit(0);

}

客户端:

#include <stdlib.h>

#include <stdio.h>

#include <error.h>

#include <string.h>

#include <netdb.h>

#include <sys/types.h>

#include <netinet/in.h>

#include <sys/socket.h>

#define portnumber 12345

int main(int argc,char *argv[])

{

int sockfd;

struct sockaddr_in server_sock;

struct hostent *host;

int nbytes;

char buf[1024];

char str;

 

if(argc!=2)

{

fprintf(stderr,"Usage:%s hostneme \a\n",argv[0]);

exit(1);

}

 

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

{

fprintf(stderr,"get hostname error\n");

exit(1);

}

sockfd=socket(AF_INET,SOCK_STREAM,0);

 

if(sockfd==-1)

{

fprintf(stderr,"create client socket fail\n");

exit(1);

}

bzero(&server_sock,sizeof(&server_sock));

 

server_sock.sin_family=AF_INET;

server_sock.sin_port=htons(portnumber);

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

//server_sock->sin_addr.S_addr=inet_aton("192.168.1.11",ip_addr);

 

int connect_ret;

char *reply="not exit";

if(connect_ret=connect(sockfd,(struct sockaddr *)(&server_sock),sizeof(struct sockaddr_in))<0)

{

fprintf(stderr,"client get connection failed\n");

exit(1);

}

while(1)

{

printf("please input something\n");

 

fgets(buf,1024,stdin);

nbytes=write(sockfd,buf,strlen(buf));

if (nbytes<0)

{

    printf("send failed\n");

 

 

 

    exit(1);

}

buf[strlen(buf)-1]='\0';

if(strlen(buf)==0)

        {

printf("You input nothing and press ENTER key,so we consider you want to quit the client!\n");

printf("Are you sure to quit the client?the default choice is not,if you do not want to exit,you can press ENTER key!\n");

str=getchar();

if(str=='y')

     break;

else

    if(str=='\n')

{

  printf("you still not want to quit the client!\n");

   write(sockfd,reply,8);

 }

}

else

printf("we send %d octets from the client,string is %s\n",strlen(buf),buf);

}

printf("the client has quit!you need not input anything!\n");

close(sockfd);

exit(0);

}

接下来介绍几个上周用过的函数:

函数9、绝对值函数:int abs(int a);long int labs(long int b);

函数10、求商与余数函数:div_t div(int numerator,int denominator);ldiv_t div(long int numerator,long int denominator);

div_t是一个结构体,包含了商与余数两个字段。第一个形参是分子,第二个是分母。

函数11、随机数函数:int rand(void)void srand(unsigend int seed)

函数12、字符串转换函数:int atoi(char const *string);------将合法的字符转换为指定的值。

long int atol(char const *string);

以上两个函数均是以10为基数,待会在介绍怎么转换的。

long int strtol(char const *string ,char **unused,int base)

unsigned long int strtoul(char const *string ,char ** unused ,int base)

这两个函数功能比前面两个强一点,可以指出转换到哪结束,并将指针指向那里,而且基数可以自己指定。这里附上一个例子程序。

怎么转换的呢?举个例子:用第一个函数,输入“13ab”,输出是13ab对于基数10来讲是非法数值。而采用第三个函数时,基数我们假定为15,则输出的是4211,这样算的((1*15+3)*15+10)*15+11,查看源码可以知道为什么?

还有就是第二个形参,函数希望你传递给它的是一个指针变量的地址。我们可以这么想,第一个函数的形参是char * string ,所以传递给的是一个指针(地址),所以这样调用:char *stringatoi(string)string的值是存放在该地址(具体是哪个编译器决定)的值,即是它指向的变量的地址。以此类推,第三个函数的第二个形参需要的是指针变量的地址,既然这样,我们就需要的是string这个名字的内存地址所在,因为string是指针变量,地址那就是&string这个值了!!!!!!!懂不???YES!以后遇到这种形参就会填写了!记住了!建议不懂可以画个调用图。以下是例子程序,里面便有如上所说的情况:

#include <stdlib.h>

#include <stdio.h>

void third_ptr(char ***string);

void fourth_ptr(char ****string);

void main(void)

{

int atoi_ret;

long int atol_ret,strtol_ret;

char *ptr=NULL;

unsigned long strtoul_ret;

 

const char *atoi_str="89";

atoi_ret=atoi(atoi_str);

printf("atoi_ret=%d\n",atoi_ret);

 

const char *atol_str="901";

atol_ret=atol(atol_str);

printf("atol_ret=%d\n",atol_ret);

 

const char *strtol_str=" aft?pl";

strtol_ret=strtol(strtol_str,&ptr,16);

printf("strtol_ret=%d,unused=%s\n",strtol_ret,ptr);

//printf("unused=%d\n",unused);

 

const char *strtoul_str="ab::i";

strtoul_ret=strtoul(strtoul_str,NULL,20);

printf("strtoul_ret=%d\n",strtoul_ret);

 

char *str=NULL;

char **str1;

str1=&str;

third_ptr(&str1);

printf("str=%s\n",str);

 

char *str2=NULL;

char **str3;

str3=&str2;

char ***str4;

str4=&str3;

fourth_ptr(&str4);

printf("str4=%s\n",str2);//this str2 is the first ptr in this relation!we get this complex 

 // relationship step by step 

 

}

 

void third_ptr(char ***string)

{

char *str="according to this test program,we can understand the multiptr well!";

**string=str;

 

}

 

void fourth_ptr(char ****string)

{

char *str="test again!";

***string=str;

}

函数13、断言函数:assert(int expression),很有用的!!再次不再说了。

到此结束,还有一些不常用的不说了。


你可能感兴趣的:(网络编程常见函数解析)