主干部分
服务器端:socket—>bind—>listen—>accept—>close;
客户端: socket—>connect—>close;
按照上面建立连接后,就是进行数据的传输了。。。。
涉及主干函数如下:
socket函数用于创建套接字文件标识号;
bind函数用于套接字和地址的绑定;
listen函数用于监听该套接字
accept函数用于等待客户端连接请求;
connect函数向服务器申请请求。
具体函数解说
socket成功返回该套接字的文件标示符;失败返回-1;
参数:domain表示本机采用的协议族:例如AF_INET表示internet网络协议族;AF_UNIX表示系统unix的进程通信协议族
type表示具体协议SOCK_STREAM表示TCP协议,SOCK_DGRAM表示UDP协议
protocal表示也是指定具体协议的,如果type指定了则为0;
上诉参数的值都是通过宏给出的;
补充:
正常下,protocol为0,则具体协议通过type给出;
若设置原始套接字某个协议时,通过设置type将其说明是原始套接字,用protocol来说明创建具体的协议的套接字;
bind成功则返回0;失败-1;将本地端口绑定到该socket字上。
参数:sockfd表示由socket函数产生的套接字标识号;
my_addr表示本地端口信息;后一个参数就是该类型长度;
sockaddr结构:linux/socket.h头文件
as_family表示协议族;
sa_data 表示具体地址;
sockaddr_in结构体:linux/in.h头文件
sin_family表示协议族;sin_port表示要监听的端口;sin_addr表示吧本机可以和哪些主机通信若为INADDR_ANY表示任何
补充:
选择sockaddr_in是因为兼容性
listen函数将套接字变成监听套接字;成功为0;失败-1
参数:sockfd表示已经绑定的套接字;
backlog表示多个客户端时用它来表示最多有个多套接字可以被同时监听
accept函数表示等待客户端请求;成功则放回已经接受到了客户端请求后的套接字标识号;
参数:sockfd表示已经监听的套接字;
addr表示请求连接客户端的地址信息;后一个是该信息大小
connect函数表示客户端向服务端请求连接,成功则为0,失败为-1
参数:sockfd表示客户端产生的套接字;
serv_addr表示服务器的地址信息;后一个是该结构的大小;
write函数用于写信息,写入到fd中;
read函数用于读取fd信息;
采用的模式:阻塞模式,后期还会讲解一些其他模式
程序来源:http://blog.chinaunix.net/uid-23069658-id-3273673.html
服务器端:
//TCP示例服务器端 tcpSrv.c #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <unistd.h> #include <netdb.h> #include <sys/socket.h> #include <netinet/in.h> #include <sys/types.h> #include <arpa/inet.h> int main(int argc, char *argv[]) { int skfd,cnfd,addr_len; struct sockaddr_in srv_addr,clt_addr; int portnumber; char hello[]="Hello! Long time no see. "; if(2 != argc || 0 > (portnumber=atoi(argv[1]))) { printf("Usage:%s port ",argv[0]); exit(1); } /* 创建IPv4的流式套接字描述符 */ if(-1 == (skfd=socket(AF_INET,SOCK_STREAM,0))) { perror("Socket Error:"); exit(1); } /* 填充服务器端sockaddr地址结构 */ bzero(&srv_addr,sizeof(struct sockaddr_in)); srv_addr.sin_family=AF_INET; srv_addr.sin_addr.s_addr=htonl(INADDR_ANY); srv_addr.sin_port=htons(portnumber); /* 将套接字描述符skfd和地址信息结构体绑定起来 */ if(-1 == bind(skfd,(struct sockaddr *)(&srv_addr),sizeof(struct sockaddr))) { perror("Bind error:"); exit(1); } /* 将skfd转换为被动建通模式 */ if(-1 == listen(skfd,4)) { perror("Listen error:"); exit(1); } while(1) { /* 调用accept,服务器端一直阻塞,直到客户程序与其建立连接成功为止*/ addr_len=sizeof(struct sockaddr_in);//此处sockaddr也可以通过。。。 if(-1 == (cnfd=accept(skfd,(struct sockaddr *)(&clt_addr),&addr_len))) { perror("Accept error:"); exit(1); } printf("Connect from %s:%u ...! ",inet_ntoa(clt_addr.sin_addr),ntohs(clt_addr.sin_port)); if(-1 == write(cnfd,hello,strlen(hello))){ perror("Send error:"); exit(1); } close(cnfd); } close(skfd); exit(0); }
客户端:
//TCP示例客户端 tcpclt.c #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <netdb.h> #include <sys/socket.h> #include <netinet/in.h> #include <sys/types.h> #include <arpa/inet.h> int main(int argc, char *argv[]) { int skfd; char buf[1024] = {0};//----此处尤其重要注意,应该先分配这个变量空间,不然不能正常存储数据 struct sockaddr_in server_addr; struct hostent *host; int portnumber,nbytes; if(3 != argc || 0>(portnumber=atoi(argv[2]))) { printf("Usage:%s hostname portnumber "); exit(1); } if(NULL == (host=gethostbyname(argv[1]))) { perror("Gethostname error:"); exit(1); } /* 创建socket描述符 */ if(-1 == (skfd=socket(AF_INET,SOCK_STREAM,0))) { perror("Socket Error:"); exit(1); } /* 客户端填充需要连接的服务器的地址信息结构体 */ bzero(&server_addr,sizeof(server_addr)); server_addr.sin_family=AF_INET; server_addr.sin_port=htons(portnumber); server_addr.sin_addr=*((struct in_addr *)host->h_addr); /* 客户端调用connect主动发起连接请求 */ if(-1 == connect(skfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))) { perror("Connect Error:"); exit(1); } /*客户端只接收服务器发来的数据,然后就退出*/ if(-1 == read(skfd,buf,1024)){ perror("Recv Error:"); } printf("Date arrived:%s",buf); /* 拆除TCP连接 */ close(skfd); exit(0); }
------------------------------------------------------------分割线----------------------------------------------------
信息辅助函数
此类是字节转换函数:
函数字母含义:h表示机器host;n表示网络;s表示short;l表示long
这里需要字节转换主要是不同机器的字节顺序是不一样的,所以通过统一网络的字节顺序,从而能沟通不同机器的通信。
详细说明:
不同操作系统会有不同的字节顺序,目前有2中字节顺序,一种是小头;一种是大头;网络字节顺序是大头字节顺序。
小头字节顺序是指随地址增加,高的字节是放在后面的,低字节放在前面;
大头字节顺序是指地址增加,高字节放在前面,低字节放在后面;
----------------------分割线---------------------
此类函数为IP与域名的转换:
第一个是将域名转为网络可识别的机器结构指针;第二是将IP形式的转换;
关键点是返回的结构体hostent---netdb.h头文件
详细说明:
结构体在头文件netdb.h中;
----------------------分割线---------------------
此类函数为字符十点制IP转为32位IP--字符串与in_addr结构体互相转换
详细说明:
参数中inp参数的结构in_addr其实里面就是一个成员为s_addr类型为无符号的长整型;
----------------------分割线---------------------
此类函数目的是获取服务器信息:
服务器结构体如下:
---------------------------------------------------我是分割线-----------上述为TCP基础上讲解的-----------------------
UDP中的通信函数:
服务器端:socket—>bind—>close;
客户端: socket—>close;
这两个函数是UDP中关键函数
第一个表示接受数据函数:
参数:sockfd表示接受收数据的套接字;
buf:表示接受数据的指针;
len:数据大小;
flags:如下解释
from:是来源的地址信息
fromlen:表示from的大小;
第二个函数表示发送函数:
参数很第一个相似;----
程序来源:http://blog.chinaunix.net/uid-23069658-id-3280895.html
服务器:
#include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <unistd.h> #include <netdb.h> #include <sys/socket.h> #include <netinet/in.h> #include <sys/types.h> #include <arpa/inet.h> #define MAX_MSG_SIZE 1024 int main(int argc,char** argv){ int skfd,addrlen,ret; struct sockaddr_in addr,cltaddr; char buf[MAX_MSG_SIZE]={0}; char sndbuf[MAX_MSG_SIZE]={0}; //创建数据报式套接字skfd if(0>(skfd=socket(AF_INET,SOCK_DGRAM,0))){ perror("Create Error"); exit(1); } bzero(&addr,sizeof(struct sockaddr_in)); addr.sin_family = AF_INET; addr.sin_addr.s_addr=htonl(INADDR_ANY); addr.sin_port=htons(atoi(argv[1])); //将socket文件描述符skfd和本地端口和地址绑定起来 if(0>(bind(skfd,(struct sockaddr*)&addr,sizeof(struct sockaddr_in)))){ perror("Bind Error"); exit(1); } //开始收发数据 while(1){ ret=recvfrom
(skfd,buf,MAX_MSG_SIZE,0,(struct sockaddr*)&cltaddr,&addrlen); if(ret < 0){ printf("recv data from %s:%d error!",inet_ntoa(cltaddr.sin_addr),ntohs(cltaddr.sin_port)); }else if(ret == 0){ perror("client has been closing socket!"); }else{ printf("From %s:%d,%s",inet_ntoa(cltaddr.sin_addr),ntohs(cltaddr.sin_port),buf); memset(sndbuf,0,MAX_MSG_SIZE); switch(buf[0]){ case 'a': strcpy(sndbuf,"After u ,lady..."); break; case 'b': strcpy(sndbuf,"Before u ,sir..."); break; case 'c': strcpy(sndbuf,"Can u?"); break; default: strcpy(sndbuf,"I dont't know what u want!"); } sendto(skfd,sndbuf,strlen(sndbuf),0,(struct sockaddr*)&cltaddr,addrlen); } memset(buf,0,MAX_MSG_SIZE); } return 0; }
客户端:
#include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <unistd.h> #include <netdb.h> #include <sys/socket.h> #include <netinet/in.h> #include <sys/types.h> #include <arpa/inet.h> #define MAX_MSG_SIZE 1024 int main(int argc,char** argv){ int skfd,ret,len; struct sockaddr_in srvaddr; char buf[MAX_MSG_SIZE]={0}; char sndbuf[MAX_MSG_SIZE]={0}; struct in_addr addr; //创建数据报式套接字skfd if(0>(skfd=socket(AF_INET,SOCK_DGRAM,0))){ perror("Create Error"); exit(1); } if(0 == inet_aton(argv[1],&addr)){ perror("server addr invalid!"); exit(1); } bzero(&srvaddr,sizeof(struct sockaddr_in)); srvaddr.sin_family = AF_INET; srvaddr.sin_addr=addr; srvaddr.sin_port=htons(atoi(argv[2])); //我们的客户端只接收从服务器地址是srvaddr的主机发来的数据 if(0>(connect(skfd,(struct sockaddr*)&srvaddr,sizeof(struct sockaddr_in)))){ perror("Connect Error"); exit(1); } //开始收发数据 while(1){ memset(sndbuf,0,MAX_MSG_SIZE); len=read(0,sndbuf,MAX_MSG_SIZE); ret=sendto
(skfd,sndbuf,strlen(sndbuf),0,(struct sockaddr*)&srvaddr,sizeof(struct sockaddr)); if(ret == len){ memset(buf,0,MAX_MSG_SIZE); //我们已经知道服务器地址信息了,所以最后两个参数为NULL ret=recvfrom(skfd,buf,MAX_MSG_SIZE,0,NULL,NULL); if(ret < 0){ perror("read error from server!"); }else if(ret == 0){ perror("server has been closing socket!"); }else{ buf[ret]='