server连接网络四部曲。
为了与外界沟通,c程序用数据流读写字节。比較经常使用的数据流有标准输入、标准输出、文件等。
假设想写一个与网络通信的程序,就须要一种新的数据流----------套接字。
?
使用套接字与client程序通信前,server须要历经四个阶段:绑定、监听、接受、開始。
?
1.绑定port
??????? 计算机可能同一时候执行多个server程序,为了放置不同的对话混淆,每项服务必须使用不同的port(port)。port就好像电视频道,
用不同的频道接受不同的节目。
??????? server在启动时,须要告诉操作系统将要使用哪个port,这个过程叫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); //创建一个表示互联网30000port的套接字名 name.sin_addr.s_addr =htonl(INADDR_ANY); int c = bind(listener_d,(struct sockaddr *) &name,sizeof(name)); if (c == -1) error("无法绑定port");
?
?
2.监听
if (listen(listen_d),10) ==1) //队列长度为10 error("无法监听");
?
??把队列长度设为10,也就是说最多能够有10个client同一时候尝试连接server,它们不会马上得到响应,但能够排队等候。而第十一个
client会被告知server忙。
?
3.接受连接
一旦帮顶完port。设置为监听队列,唯一能够做的就是等待。
server的一生都在等待有client来连接他们。直到有client连接server时,它会返回第二个套接字描写叙述符。然后就能够通信了。
?
struct sockaddr_storage client_addr; //保存连接client的具体信息 unsigned int address_size = sizeof (client_addr); int connect_d = accept(listener_d,(struct sockaddr *)&client_addr,&address_size); if (connect_d == -1) error("无法打开副套接字")。
?
server将用新的连接描写叙述符connect_d
?
4.開始通信
socket套接字这个数据流是双向的,既能够用作输入也能够用作输出。假设想向套接字输出数据。就要用send()函数。而不是fprint()
char *msg = "xxxxxxxxxxx" if (send(connect_d,msg,strlen(msg),0) == -1) error("send");
?
?
注:怎样选择port号
仅仅有超级用户或管理员才有资格使用1024号一下的port。由于小号的port留给了一些知名服务,如网页server和邮件server。
操作系统仅仅同意管理员用这些port,防止普通用户启动一些多余的服务。
因此,通常情况下请使用1024以上的port。
?
绑定port有延时
????? 当你绑定某个port和套接字时,在接下来的30秒内,操作系统不同意不论什么程序再绑定它,包含上次绑定它的程序。这样万一你的
服务端瞬间重新启动了一下,也没办法立马绑定那个port,造成client得不到响应。
int reuse =1; //须要用一个整形变量来保存选项。设为1,表示又一次使用port if (setsockopt(listener_d,SOL_SOCKET,SO_REUSEADDR,(char *)&reuse,sizeof(int)) == -1) error ("无法设置又一次使用")。
?
?
从client读数据,recv()函数
<读了几个字节> =recv(<描写叙述符>,<缓冲区>,<要读几个字节>。0);
?
注:
<1>字符串不以\0结尾。
<2>在telnet输入文本时,字符串以\r\n结尾
<3>recv()返回字符个数。如错误发生则返回-1。假设client关闭了连接。则返回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; }
?
?
?
?