1.什么是套接字
应用层通过传输层进行数据通信时,TCP和UDP会遇到同时为多个应用程序进程提供并发服务的问题。多个TCP连接或多个应用程序进程可能需要通过同一个TCP协议端口传输数据。为了区别不同的应用程序进程和连接,许多计算机操作系统为应用程序与TCP/IP协议交互提供了称为套接字(Socket)的接口。
区分不同应用程序进程间的网络通信和连接,主要有3个参数:通信的目的IP地址、使用的传输层协议(TCP或UDP)和使用的端口号。Socket原意是“插座”。通过将这3个参数结合起来,与一个“插座”Socket绑定,应用层就可以和传输层通过套接字接口,区分来自不同应用程序进程或网络连接的通信,实现数据传输的并发服务。
简单来说,套接字就是IP地址+端口号。用来标识网络中唯一的一个进程。
2.网络字节序列
网络字节序:网络数据流有大小端之分,发送主机通常将发送缓冲区中的数据按内存地址从低到高的顺序发出,接收主机把从网络上接到的字节依次保存在接收缓冲区中,也是按内存地址从低到高的顺序保存,网络数据流的地址规定:先发出的数据是低地址,后发出的数据是
地址。
TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节。
3.socket地址的数据类型及相关函数
socket API是一层抽象的网络编程接口,适用于各种底层网络协议,如IPv4、IPv6,然而,各种网络协议的地址格式并不相同,如下图所示:
常用第二种底层网络协议。
4.基于TCP下的套接字编程步骤
服务器端的步骤:
1>创建套接字
int socket(int domain,int type,int protocol)//domain:底层采用何种协议,AF_INET;type:什么传输类型,TCP面向字节流 SOCK_STREAM
2>定义数据结构,将网络信息添加进来
struct sockaddr_in
3>绑定:将套接字信息填充到内核中
4>进行监听
当listen状态的套接字不进行accept(),仍然可以连接,但系统会维护一个队列,只允许前面的几个请求可连接,当超过队列容量时,则不允许后来的进行连接。
5>调用accept()函数以阻塞方式来接收客户端的连接请求
6>接收消息
7>当客户端停止发消息时关闭连接
客户端步骤;
对于客户端只是不需要进行绑定和监听,得到远端的信息直接进行相应的连接,连接成功后可以发送消息给服务器端。
5.相应代码实现:
server.c 1 #include2 #include 3 #include 4 #include 5 #include 6 #include 7 #include 8 #include 9 void* thread_run(void *arg) 10 { 11 int new_sock=(int)arg; 12 char buf[1024]; 13 while(1) 14 { 15 memset(buf,'\0',sizeof(buf)); 16 ssize_t size= read(new_sock,buf,sizeof(buf)-1); 17 if(size>0) 18 { 19 buf[size]='\0'; 20 printf("client :%s\n",buf); 22 } 23 else if(size==0) 24 { 25 printf("client close....\n"); 26 exit(0); 27 } 28 } 29 close(new_sock); 30 31 return NULL; 32 } 33 void usage(char* proc) 34 { 35 36 printf("usage:%s [ip] [port]\n",proc); 37 38 } 39 int start(int port,char* ip) 40 { 41 int listen_sock=socket(AF_INET,SOCK_STREAM,0); 42 if(listen_sock<0) 43 { 44 printf("sock"); 45 exit(1);//zhongzhijincheng 46 } 47 //2.定义数据结构,添加网络信息 48 struct sockaddr_in local; 49 local.sin_family=AF_INET; 50 local.sin_port=htons(port); 51 local.sin_addr.s_addr=inet_addr(ip); 52 socklen_t len=sizeof(local); 53 //3.进行绑定 54 if(bind(listen_sock,(struct sockaddr*)&local,len)<0) 55 { 56 printf("bind"); 57 exit(2); 58 59 } 60 //4.进行监听 61 if(listen(listen_sock,5)<0) 62 { 63 printf("listen"); 64 exit(3); 65 } 66 return listen_sock; 67 } 71 int main(int argc, char* argv[]) 72 { 73 if(argc!=3) 74 { 75 usage(argv[0]); 76 return 1; 77 } 78 int port=atoi(argv[2]);//得到端口号 79 //char*ip=argv[1];//得到IP 80 //1.创建socket 81 int sock=start(port,argv[1]); 82 83 struct sockaddr_in client; 84 socklen_t len=sizeof(client); 85 while(1) 86 { 87 int new_fd=accept(sock,(struct sockaddr*)&client,&len);//接受连接请求 88 if(new_fd<0) 89 { 90 printf("accept"); 91 continue; 92 } 93 #ifdef _V1_//单用户模式 94 char buf[1024]; 95 while(1) 96 { 97 memset(buf,'\0',sizeof(buf)); 98 ssize_t size= read(new_fd,buf,sizeof(buf)-1); 99 if(size>0)100 {101 buf[size]='\0';102 printf("client :%s\n",buf);103 // fflush(stdout);104 }105 else if(size==0)106 {107 printf("client close....\n");108 break;109 }110 close(new_fd);111 // exit(0);112 }113 #elif _V2_//让子进程进行后面的数据接收115 pid_t id=fork();116 if(id<0)117 {118 119 printf("fork");120 exit(3);121 122 }123 else if(id==0)124 {125 close(sock);126 char buf[1024];127 while(1)128 {129 memset(buf,'\0',sizeof(buf));130 ssize_t size= read(new_fd,buf,sizeof(buf)-1);131 if(size>0)132 {133 buf[size]='\0';134 printf("client :%s\n",buf);135 136 }137 else if(size==0)138 {139 printf("client close....\n");140 break;141 142 }143 }144 close(new_fd);145 exit(0); 146 }147 else148 {149 close(new_fd);150 }151 #elif _V3_//让一个线程去完成后面的数据传送152 pthread_t tid;153 154 if(pthread_create(&tid,NULL,tnread_run,(void*)new_fd)<0);155 {156 157 158 printf("pthread_create");159 }160 pthread_detach(tid);161 #else162 printf("default");163 #endif164 165 }166 167 return 0;168 169 }client.c 9 void usage(char *proc) 10 { 11 12 printf("usage : %s [ip][port]\n",proc); 13 14 } 15 16 17 int main(int argc, char *argv[]) 18 { 19 if(argc!=3) 20 { 21 usage(argv[0]); 22 return 1; 23 } 24 int sock=socket(AF_INET,SOCK_STREAM,0); 25 if(sock<0) 26 { 27 printf("socket"); 28 exit(1); 29 } 30 int port=atoi(argv[2]); 31 char* ip=argv[1]; 32 struct sockaddr_in remote;//得到远端信息 33 remote.sin_family=AF_INET; 34 remote.sin_port=htons(port); 35 remote.sin_addr.s_addr=inet_addr(ip); 36 int ret=connect(sock,(struct sockaddr*)&remote,sizeof(remote));//建立连接 37 if(ret<0) 38 { 39 printf("conect"); 40 } 41 char buf[1024]; 42 while(1) 43 { 44 printf("please enter: "); 45 scanf("%s",buf); 46 write(sock,buf,sizeof(buf)); 47 fflush(stdin); 48 } 49 return 0; 50 }
总结:利用套接字实现了TCP协议的三次握手机制,从而进行服务器端和客户端的数据传输,但是只能进行单方面的通信,客户端只能发送消息给服务器端,服务器端接受消息并显示,而不能给客户端发消息。
6.UDP下的套接字编程
UDP是面向数据块的,不提供可靠传输的协议。所以不需要进行相应的连接和监听。服务端可以通过recvfrom()函数来接受客户端发送来的数据。然后打印出来。同样的客户端不许进行绑定。直接通过sendto()函数来向服务端发送数据。
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);//flags:以什么方式接收,o表示阻塞方式进行等待
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
代码:
//server_sock 1 #include2 #include 3 #include 4 #include 5 #include 6 #include 7 #include 8 #include 9 void usage(char*proc) 10 { 11 printf("usage: [ip] [port]",proc); 12 } 13 14 int main(int argc,char*argv[]) 15 { 16 if(argc!=3) 17 { 18 usage(argv[0]); 19 return 1; 20 } 21 int port=atoi(argv[2]); 22 char* ip=argv[1]; 23 //1.chuangjian:socket 24 int sock=socket(AF_INET,SOCK_DGRAM,0); 25 if(sock<0) 26 { 27 perror("socket"); 28 exit(1); 29 } 30 //2.tianjiawangluoxinxi 31 struct sockaddr_in local; 32 local.sin_family=AF_INET; 33 local.sin_port=htons(port); 34 local.sin_addr.s_addr=inet_addr(ip); 35 //3.bind 36 if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0) 37 { 38 perror("bind"); 39 exit(2); 40 } 41 struct sockaddr_in client; 42 socklen_t len=sizeof(client); 43 char buf[1024]; 44 while(1) 45 { 46 memset(buf,'\0',sizeof(buf)); 47 //jieshouxiaoxi 48 int size=recvfrom(sock,buf,sizeof(buf)-1,0,(struct sockaddr*)&client,&l en); 49 if(size>0) 50 { 51 buf[size]='\0'; 52 printf("client: %s\n",buf); 53 54 } 55 else if(size==0) 56 { 57 printf("client close.....\n"); 58 } 59 else 60 { 61 printf("failed"); 62 63 } 64 65 } 66 return 0; 67 } //client_sock 1 #include 2 #include 3 #include 4 #include 5 #include 6 #include 7 #include 8 9 void usage(char *proc) 10 { 11 printf("usage: [ip],[port]",proc); 12 } 13 int main(int argc,char* argv[]) 14 { 15 if(argc!=3) 16 { 17 usage(argv[0]); 18 return 1; 19 } 20 int port=atoi(argv[2]); 21 char*ip=argv[1]; 22 int sock=socket(AF_INET,SOCK_DGRAM,0); 23 if(sock<0) 24 { 25 perror("socket"); 26 exit(1); 27 } 28 struct sockaddr_in remote; 29 remote.sin_family=AF_INET; 30 remote.sin_port=htons(port); 31 remote.sin_addr.s_addr=inet_addr(ip); 32 char buf[1024]; 33 while(1) 34 { 35 printf("please enter:"); 36 fflush(stdout); 37 ssize_t size=read(0,buf,sizeof(buf)-1); 38 buf[size]='\0'; 39 ssize_t s=sendto(sock,buf,sizeof(buf),0,(struct sockaddr*)&remote,(sizeof (remote))); 40 if(size=0) 41 { 42 printf("client closing..."); 43 } 44 } 45 return 0; }
程序结果:
本地环回连接: