首页 > 代码库 > head first c<11>初探网络编程上

head first c<11>初探网络编程上

服务器连接网络四部曲。

为了与外界沟通,c程序用数据流读写字节,比较常用的数据流有标准输入、标准输出、文件等。

如果想写一个与网络通信的程序,就需要一种新的数据流----------套接字。

 

使用套接字与客户端程序通信前,服务器需要历经四个阶段:绑定、监听、接受、开始。

 

1.绑定端口

        计算机可能同时运行多个服务器程序,为了放置不同的对话混淆,每项服务必须使用不同的端口(port)。端口就好像电视频道,

用不同的频道接受不同的节目。

        服务器在启动时,需要告诉操作系统将要使用哪个端口,这个过程叫端口绑定。为了绑定它你需要两样东西:套接字描述符和套

接字名。

#include<sys/socket.h>int listener_d = socket(PF_INET,SOCK_STREAM,0);if (listene_d  == -1)     error("无法打开套接字");


 


其中listener_d为套接字描述符,绑定用bind函数

 

#include <arpa/inet.h>   //为了创建互联网地址....struct sockaddr_in name;name.sin_family = PF_INET;name.sin_port = (in_port_t)htons(30000);            //创建一个表示互联网30000端口的套接字名name.sin_addr.s_addr =htonl(INADDR_ANY);int c = bind(listener_d,(struct sockaddr *) &name,sizeof(name));if (c == -1)  error("无法绑定端口");


 

 

2.监听

if (listen(listen_d),10) ==1)  //队列长度为10 error("无法监听");


 

  把队列长度设为10,也就是说最多可以有10个客户端同时尝试连接服务器,它们不会立即得到响应,但可以排队等候。而第十一个

客户端会被告知服务器忙。

 

3.接受连接

一旦帮顶完端口,设置为监听队列,唯一可以做的就是等待。

服务器的一生都在等待有客户端来连接他们。直到有客户端连接服务器时,它会返回第二个套接字描述符,然后就可以通信了。

 

struct sockaddr_storage client_addr;  //保存连接客户端的详细信息unsigned int address_size = sizeof (client_addr);int connect_d = accept(listener_d,(struct sockaddr *)&client_addr,&address_size);if (connect_d == -1)   error("无法打开副套接字");


 

服务器将用新的连接描述符connect_d

 

4.开始通信

socket套接字这个数据流是双向的,既可以用作输入也可以用作输出。如果想向套接字输出数据,就要用send()函数,而不是fprint()

char *msg = "xxxxxxxxxxx"if (send(connect_d,msg,strlen(msg),0) == -1) error("send");


 


 

注:如何选择端口号

只有超级用户或管理员才有资格使用1024号一下的端口,因为小号的端口留给了一些知名服务,如网页服务器和邮件服务器。

操作系统只允许管理员用这些端口,防止普通用户启动一些多余的服务。

因此,通常情况下请使用1024以上的端口。

 

绑定端口有延时

      当你绑定某个端口和套接字时,在接下来的30秒内,操作系统不允许任何程序再绑定它,包括上次绑定它的程序。这样万一你的

服务端瞬间重启了一下,也没办法立刻绑定那个端口,造成客户端得不到响应。

int reuse =1;   //需要用一个整形变量来保存选项,设为1,表示重新使用端口if (setsockopt(listener_d,SOL_SOCKET,SO_REUSEADDR,(char *)&reuse,sizeof(int)) == -1)error ("无法设置重新使用");


 

 

从客户端读数据,recv()函数

<读了几个字节> =recv(<描述符>,<缓冲区>,<要读几个字节>,0);

 

注:

<1>字符串不以\0结尾。

<2>在telnet输入文本时,字符串以\r\n结尾

<3>recv()返回字符个数,如发生错误则返回-1,如果客户端关闭了连接,则返回0

<4>recv调用不一定能一次接受所有字符,它意味着可能需要多次调用recv()

 

recv()用起来十分繁琐,最好把它封装在某个函数中,比如下面这个函数,它指定数组中保存以\0结尾的字符串

int read_in(int socket, char *buf,int len){char *s =buf ;int slen =len;int c =recv(socket,s,slen,0);while((c>0) && (s[c-1]!='\n')){s +=c;slen -=c;c=recv(socket,s,slen,0);}if (c<0)    return c;else if (c == 0)   buf[0]='\0';else    s[c-1]='\0';return len-slen;}