Linux下简单的socket通讯实例html
If you spend too much time thinking about a thing, you’ll never get it done.git
—Bruce Leegithub
学习网络编程也一段时间了,刚开始看《UNIX网络编程》的时候,以为这本厚厚的书好难啊!看到后来,发现并无想象中的那么难。若是你是新手,建议你看到第二部分结束后,开始着手写代码。不写代码确定是不行的。看100遍也没有敲一遍实现一遍来的清楚。敲完之后,带着问题去看书,你会更加有针对性。提升的速度是飞快的,这也是学习任何一本书、一门语言的惟一手段。编程
写这个博客也是由于刚开始学的时候,查了好多别人写的东西,百度了之后,发现你们只是把全部的代码一贴。并无讲解每一个函数的功能。我甚至不知道哪一个函数是哪一个头文件下的。形成我对函数很不理解。下面我会对每一个函数的功能,和它的头文件以及函数原型写出来,让你们参考,第一次写博客,有什么错误的地方,但愿你们指正。能够在下面给我留言,也是我继续写下去的动力。服务器
我很但愿和你们一块儿分享学习网络编程遇到的种种困难与不顺,也但愿和你们一块儿讨论其中遇到的问题,一块儿成长,若是你刚开始打算学习网络编程,那这篇文章必定能给你一些帮助。网络
个人邮箱:cvmimi_linhai@foxmail.com,转载请注明出处:http://www.cnblogs.com/yusenwu/p/4579167.html。socket
关于怎样介绍这个简单的实例:(基本上涵盖了《UNIX网络编程》1-5章的内容,更深,更细的,须要咱们再细读这本书)函数
--> 一、代码展现,功能介绍学习
--> 二、首先介绍一下客户端和服务端中函数的功能以及函数的原形。网站
--> 三、关于链接三次握手和TCP链接关闭时候的分组交换
--> 四、IPv四、IPv6套接字的地址结构
--> 五、一些好的学习网站总结
--> 六、代码下载
--> 七、总结
--> 八、实现一个echo的实例,代码能够到Github上下载
client.c
1 #include <stdio.h> 2 #include <sys/socket.h> 3 #include <sys/types.h> 4 #include <stdlib.h> 5 #include <netinet/in.h> 6 #include <errno.h> 7 #include <string.h> 8 #include <arpa/inet.h> 9 #include <unistd.h> 10 #define MAXLINE 1024 11 int main(int argc,char **argv) 12 { 13 char *servInetAddr = "127.0.0.1"; 14 int socketfd; 15 struct sockaddr_in sockaddr; 16 char recvline[MAXLINE], sendline[MAXLINE]; 17 int n; 18 19 if(argc != 2) 20 { 21 printf("client <ipaddress> \n"); 22 exit(0); 23 } 24 25 socketfd = socket(AF_INET,SOCK_STREAM,0); 26 memset(&sockaddr,0,sizeof(sockaddr)); 27 sockaddr.sin_family = AF_INET; 28 sockaddr.sin_port = htons(10004); 29 inet_pton(AF_INET,servInetAddr,&sockaddr.sin_addr)
30 if((connect(socketfd,(struct sockaddr*)&sockaddr,sizeof(sockaddr))) < 0 )
31 { 31 printf("connect error %s errno: %d\n",strerror(errno),errno); 32 exit(0); 33 } 34 35 printf("send message to server\n"); 36 37 fgets(sendline,1024,stdin); 38 39 if((send(socketfd,sendline,strlen(sendline),0)) < 0) 40 { 41 printf("send mes error: %s errno : %d",strerror(errno),errno); 42 exit(0); 43 } 44 45 close(socketfd); 46 printf("exit\n"); 47 exit(0); 48 }
-执行:gcc client.c -o client 后启动 ./client 客户端程序 启动前先启动./server-----------------------------------------
server.c
1 #include <stdio.h> 2 #include <sys/socket.h> 3 #include <sys/types.h> 4 #include <string.h> 5 #include <netinet/in.h> 6 #include <stdlib.h> 7 #include <errno.h> 8 #include <unistd.h> 9 #include <arpa/inet.h> 10 11 #define MAXLINE 1024 12 int main(int argc,char **argv) 13 { 14 int listenfd,connfd; 15 struct sockaddr_in sockaddr; 16 char buff[MAXLINE]; 17 int n; 18 19 memset(&sockaddr,0,sizeof(sockaddr)); 20 21 sockaddr.sin_family = AF_INET; 22 sockaddr.sin_addr.s_addr = htonl(INADDR_ANY); 23 sockaddr.sin_port = htons(10004); 24 25 listenfd = socket(AF_INET,SOCK_STREAM,0); 26 27 bind(listenfd,(struct sockaddr *) &sockaddr,sizeof(sockaddr)); 28 29 listen(listenfd,1024); 30 31 32 printf("Please wait for the client information\n"); 33 34 for(;;) 35 { 36 if((connfd = accept(listenfd,(struct sockaddr*)NULL,NULL))==-1) 37 { 38 printf("accpet socket error: %s errno :%d\n",strerror(errno),errno); 39 continue; 40 } 41 42 n = recv(connfd,buff,MAXLINE,0); 43 buff[n] = '\0'; 44 printf("recv msg from client:%s",buff); 45 close(connfd); 46 } 47 close(listenfd); 48 }
-执行:gcc server.c -o server 后启动 ./server 服务端程序-------------------------------------------------------
> 一、代码展现,功能介绍
上面这个简单的socket通讯的代码要实现的功能:从客户端发送一条消息后,服务端接收这条消息,并在服务端显示(recv msg from client:****)。
> 二、首先介绍一下客户端和服务端中函数的功能以及函数的原形。
#include <sys/socket.h> int socket(int family, int type, int protocol); //指按期望的通讯协议类型,返回的文件描述符和套接字描述符相似,咱们成为套接字描述符,简称sockfd
family:协议族
family | 说明 |
AF_INET | IPv4协议 |
AF_INET6 | IPv6 |
AF_LOCAL | Unix域协议(15章) |
AF_ROUTE | 路由套接字(18章) |
AF_KEY | 密钥套接字(19章) |
type:套接字的类型
type | 说明 |
SOCK_STREAM(经常使用) | 字节流套接字 |
SOCK_DGRAM | 数据报套接字 |
SOCK_SEQPACKET | 有序分组套接字 |
SOCK_RAW | 原始套接字 |
protocol:协议类型的常量或设置为0,以选择给定的family和type组合的系统默认值
protocol | 说明 |
IPPROTO_TCP | TCP传输协议 |
IPPROTO_UDP | UDP传输协议 |
IPPROTO_SCTP | SCTP传输协议 |
#include<arpa/inet.h> int inet_pton(int family,const char *strptr,void *addrptr);//成功返回1,格式不对返回0,出错返回-1
//做用:p表明表达式 n表明数值 之后所写的全部代码中都有可能会须要这个函数,因此这个函数很重要
//将char所指向的字符串,经过addrptr指针存放
//他的反函数: inet_ntop()做用相反。能够百度查阅这个函数的功能。由于例子里咱们没有涉及到,就不介绍了。之后用到的时候再说
//须要注意的是:当他发生错误的时候,errno的值会被置为EAFNOSUPPORT 关于errno值咱们一下子介绍。
#include <sys/socket.h> int connect(int sockfd,const struct sockaddr* servaddr,socklen_t addrlen);//用connect函数来创建与TCP服务器的链接
#include<unistd.h> int close(int sockfd);//关闭socket,并终止TCP链接
#include <sys/socket.h> int bind(int sockfd,const struct* myaddr,socklen_t addrlen);//把本地协议地址赋予一个套接字。也就是将32位的IPv4或128位ipv6与16位的TCP或者UDP组合。
#include<sys/socket.h> int listen(int sockfd,int backlog)//成功返回0,失败返回-1 listen函数仅由TCP服务器调用
//listen函数将会作两件事:
//1:咱们在建立套接字的时候使用了socket函数,它建立的套接字是主动套接字,bind函数的功能就是经过这个将主动套接字,变成被动套接字,告诉内核应该接受指向这个套接字的请//求,CLOSED状态变成LISTEN状态
//2:本函数的第二个参数规定了内核要为该套接字排队的最大链接个数。
#include <sys/socket.h> int accept(int sockfd,struct sockaddr* cliaddr,socklen_t *addrlen);//成功返回描述符,失败返回-1
//一、若是第二三个参数为空,表明了,咱们对客户的身份不感兴趣,所以置为NULL;
//二、第一个参数为socket建立的监听套接字,返回的是已链接套接字,两个套接字是有区别的,并且很是重要。区别:咱们所建立的监听套接字通常服务器只建立一个,而且一直存在。而内核会为每个服务器进程的客户链接创建一个链接套接字,当服务器完成对某个给定客户的服务时,链接套接字就会被关闭。
总结:咱们学校的实验室是云计算实验室,有不少的集群,我在上面开了2台虚拟机,在两台Linux系统上跑。能够成功接收。只要将IP设置好便可,注意,关掉防火墙:service iptables stop;
> 三、关于链接三次握手和TCP链接关闭时候的分组交换
三次握手:
为了更好的理解connect、bind、close三个函数,了解一下TCP链接的创建和终止是颇有必要的。(请务必理解理解上面的全部的函数后,再看这节)。
一、服务器首先必须被打开,等待准备接受外来的链接。咱们上面的例子用到了socket、bind、listen这3个函数。以后,咱们称为服务端被被动打开了。
二、客户端是经过connect发起主动打开。
三、主动打开后,客户TCP发送了一个SYN(同步)分节,它告诉服务器客户将在链接中只发送的数据的初始序列号,SYN分节不携带数据。它发送的IP数据报,只有一个IP首部、一个TCP首部以及TCP选项。
四、服务器必须确认(ACK)客户的SYN,同时本身也发送一个SYN分节,它含有服务器将在同一链接中发送的数据的初始序列号。服务器在单个分节中发送SYN和对客户SYN的ACK确认(+1)。
五、客户必须确认服务器的SYN分节。
上面的过程称为TCP的三次握手。
注:SYN(synchronous)是TCP/IP创建链接时使用的握手信号。在客户机和服务器之间创建正常的TCP网络链接时,客户机首先发出一个SYN消息,服务器使用SYN+ACK应答表示接收到了这个消息,最后客户机再以ACK消息响应。这样在客户机和服务器之间才能创建起可靠的TCP链接,数据才能够在客户机和服务器之间传递
TCP链接终止
终止一个链接须要4个分节。
一、经过调用close,咱们执行主动关闭,TCP发送一个FIN(finish,表示结束),表示数据发送完毕。
二、对端接收到FIN后,执行被动关闭。
三、一段时候后,接收到文件结束符的应用进程,将调用close关闭它的套接字。因而套接字也发送一个了FIN。
四、确认这个FIN ACK+1 下图很清楚的表达了。
五、咱们也称它为TCP四次握手。
> 四、IPv四、IPv6套接字的地址结构
IPv4地址结构:
1 struct in_addr { 2 in_addr_t s_addr; 3 }; 4 5 struct sockaddr_in { 6 uint8_t sin_len; //无符号8位整型 7 sa_family_t sin_famliy; /*AF_INET*/ 8 in_port_t sin_port;
9 struct in_addr sin_addr; /*32位 IPv4 地址*/ 10 char sin_zero[8]; /*unuse*/ 11 };
//头文件 #include <sys/types.h>
//sa_family_t和socklen_t 头文件 #include <sys/socket.h>
//in_addr_t in_port_t 头文件 #include <netinet/in.h>
IPv6地址结构:
struct in6_addr { uint8_t s6_addr[16]; }; #define SIN6_LEN struct sockaddr_in6 { uint8_t sin6_len; sa_family_t sin6_famliy; in_port_t sin6_port; uint32_t sin6_flowinfo; struct in6_addr sin6_addr; uint32_t sin6_scope_id; };
> 五、一些好的学习网站总结
一、关于51CTO上的这个视频http://edu.51cto.com/course/course_id-903.html,我买了,可是讲的很是烂,建议你们不要购买。教课的老师也就是照着书念,还不如本身。浪费钱。
二、http://www.cnblogs.com/skynet/archive/2010/12/12/1903949.html
http://blog.csdn.net/hguisu/article/details/7445768/
http://www.oschina.net/code/snippet_97047_675
这几篇博客不错,能带你入门。
> 六、代码下载
Github: https://github.com/micwu/Demo
> 七、总结
学习之路是很蛮长的。想要学好,很是难,须要长期的积累。我也正在学习中。通过了不少的挫折,可是有理想,就必定能成功。但愿你们想走Linux下服务器编程的同志们,一块儿加油吧。
> 八、echo实现
代码下载:Github