深圳门户网站,wordpress软件下载主题,网站开发付款方式和比例,手工制作大全简单socket又叫套接字或者插口#xff0c;它也是进程间通信的一种方式#xff0c;实际上就是网络上的通信节点#xff0c;应用程序只需要链接到socket就可以和网络上任何一个通信端点连接、传送数据。socket封装了通信的细节#xff0c;我们可以不必关心通信协议内容而专注于应…socket又叫套接字或者插口它也是进程间通信的一种方式实际上就是网络上的通信节点应用程序只需要链接到socket就可以和网络上任何一个通信端点连接、传送数据。socket封装了通信的细节我们可以不必关心通信协议内容而专注于应用程序开发。根据数据传送方式socket分为面向连接的数据流通信和无连接的数据报通信。
socket编程前需添加头文件
#include arpa/inet.h //struct sockaddr_in #include sys/socket.h #include sys/types.h #include fcntl.h // open #include unistd.h // close 1.创建socket
使用socket函数创建socket对象函数定义如下:
int socket(int domain, int type, int protocol);
domain指定使用的域通常取值为AF_INET和AF_INET6AF_INET表示使用IPv4协议AF_INET6表示使用IPv6协议。type指定数据传输方式type取值SOCK_STREAM表示面向连接的数据流方式type取值SOCK_DGRAM表示无连接的数据报方式type取值SOCK_RAW表示原始模式。protocol一般取0。socket成功返回创建的函数句柄值。 2.面向连接的socket通信实现TCP 图1 面向连接的socket数据流通信
所有的面向连接socket数据流通信都遵循这个过程。
2.1服务器端工作流程
1使用socket创建socket
2使用bind把创建的socket绑定到指定TCP端口
3调用listen使socket处于监听状态
4客户端发送请求后调用accept接受客户端请求建立连接
5与客户端发送或接收数据
6通信完毕关闭socket
2.2客户端工作流程
1使用socket创建socket
2调用connect向服务器端socket发起连接
3建立连接后进行数据读写
4通信完毕关闭socket 2.3通信过程使用了不同的函数下面分别进行介绍
2.3.1 bind函数
int bindint sockfd, struct sockaddr *my_addr, socklen_t addrlen;
sockfd是要绑定的socket句柄my_addr指向一个sockaddr结构里面保存IP地址和端口号addrlen是sockaddr结构的大小。bind如果绑定TCP端口成功返回0失败返回-1。 2.3.2 listen函数
int listent(int s, int backlog);
s 是要监听的socket句柄backlog指定最多可以监听的链接数量默认是20个。如果listen调用成功返回0失败就返回-1. 2.3.3 accept函数
int accept(int s, struct sockaddr *addr, socklen_t addrlen);
accept函数用于面向连接的套接字类型。accept将从连接请求队列中获得连接信息创建新的套接字并返回该套接字的描述符。accept返回的是一个新套接字描述符客户端可以通过这个描述符和服务器通信而最初通过socket创建的套接字描述符依然用于监听客户端请求。
参数s是监听的套接字描述符参数addr是sockaddr结构的指针addrlen是结构的大小。如果accept调用成功返回创建的套接字句柄失败返回-1并设置全局变量为errno。 2.3.4 connect函数
客户端创建套接字后就可以用connect连接服务器。
int connect (int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen);
connect()用于和服务器建立连接。sockfd是套接字句柄serv_addr指向sockaddr结构指定了服务器IP和端口参数addlen是serv_addr结构大小。 2.3.5 客户端和服务器可以使用相同的发送和接受数据的函数read和write就不再讲了下面讲一下send和recv函数。
1发送函数send
ssize_t send(int s, const void *buf, size_t len, int flags);
参数s是套接字描述符,buf是要发送的数据缓冲len是数据缓冲长度flags一般置0send如果发送成功返回发送的字节数失败返回-1. 2接收函数recv
int recv(int s, void *buf, size_t len, int flags);
参数s指定要读取的套接字句柄buf是存放数据的缓冲首地址len指定接收缓冲大小flags一般置0recv读取到数据时返回读取到的字节数失败返回-1.另外如果对方关闭了套接字recv返回0 3.面向连接的套接字实例
下面将以一个实例来说明socket通信其中serv.c是服务器端代码当收到客户端发送的字符就在屏幕上打印出来并把字符串发送给客户端如果客户端发送quit就结束。
1服务器端程序serv.c
#include string.h
#include arpa/inet.h //struct sockaddr_in #include sys/socket.h #include sys/types.h #include fcntl.h // open #include unistd.h // close
#define ECHO_PORT 8080 #define MAX_CLIENT_NUM 10
int main() { int sock_fd; struct sockaddr_in serv_addr; int clientfd; struct sockaddr_in clientAdd; char buff[101]; socklen_t len; int n; /* creat socket */ sock_fd socket(AF_INET, SOCK_STREAM, 0); if( sock_fd -1) { perror(creat socket error!); return 0; } else { printf(success to creat socket %d\n, sock_fd); } /*设置server地址结构*/ bzero(serv_addr, sizeof(serv_addr)); serv_addr.sin_family AF_INET; serv_addr.sin_port htons(ECHO_PORT); serv_addr.sin_addr.s_addr htons(INADDR_ANY); //表示监听所有客户端ip如需指定监听指定的客户端ip可在此处指定也可指定一个范围 bzero((serv_addr.sin_zero), 8); /*把地址和套接字绑定*/ if(bind(sock_fd, (struct sockaddr*)serv_addr, sizeof(serv_addr)) ! 0) { printf(bind address fail! %d\n,errno); close(sock_fd); return 0; } else { printf(success to bind address! \n); } /*设置套接字监听*/ if(listen(sock_fd, MAX_CLIENT_NUM) ! 0) { perror(listen socket error!); close(sock_fd); return 0; } else { printf(success to listen!\n); } /*创建新链接对应的套接字*/ len sizeof(clientAdd); clientfd accept(sock_fd, (struct sockaddr*)clientAdd, len); if(clientfd 0) { perror(accept() error!\n); close(sock_fd); return 0; } /*接收用户发来的数据*/ while((n recv(clientfd, buff, 100, 0)) 0) { buff[n] \n; printf(number of receive bytes %d data %s\n,n, buff); fflush(stdout); send(clientfd, buff, n, 0); if(strncmp(buff,quit,4) 0) break; } close(clientfd); close(sock_fd); return 0; }
INADDR_ANY选项 网络编程中常用到bind函数需要绑定IP地址这时可以设置INADDR_ANY. INADDR_ANY就是指定地址为0.0.0.0的地址这个地址事实上表示不确定地址或“所有地址”、“任意地址”。
也就是表示本机的所有IP因为有些机子不止一块网卡多网卡的情况下这个就表示所有网卡ip地址的意思。比如一台电脑有3块网卡分别连接三个网络那么这台电脑就有3个ip地址了如果某个应用程序需要监听某
个端口那他要监听哪个网卡地址的端口呢如果绑定某个具体的ip地址你只能监听你所设置的ip地址所在的网
卡的端口其它两块网卡无法监听端口如果我需要三个网卡都监听那就需要绑定3个ip也就等于需要管理3个
套接字进行数据交换这样岂不是很繁琐所以你只需绑定INADDR_ANY管理一个套接字就行不管数据是从哪个网卡过来的
只要是绑定的端口号过来的数据都可以接收到。当然 客户端connect时不能使用INADDR_ANY选项。必须指明要连接哪个服务器IP。
2下面是客户端程序client.c
#include arpa/inet.h //struct sockaddr_in #include sys/socket.h #include sys/types.h #include fcntl.h // open #include unistd.h // close
#define ECHO_PORT 8080 #define MAX_COMMAND 5
int main() { int sock_fd; struct sockaddr_in serv_addr; char *buff[MAX_COMMAND] {abc, def, test, hello, quit}; char tmp_buf[100]; int n,i; /*creat socke*/ sock_fd socket(AF_INET, SOCK_STREAM, 0); if( sock_fd -1) { perror(creat socket error!); return 0; } else { printf(success to creat socket %d\n, sock_fd); } /*设置server地址结构*/ bzero(serv_addr, sizeof(serv_addr)); serv_addr.sin_family AF_INET; serv_addr.sin_port htons(ECHO_PORT); serv_addr.sin_addr.s_addr htons(INADDR_ANY); bzero((serv_addr.sin_zero), 8); /*链接到服务器*/ if(-1 connect(sock_fd, (struct sockaddr*)serv_addr,sizeof(serv_addr))) { printf(connect error!\n); close(sock_fd); return 0; } printf(success connect to server!\n); /*发送并接收缓冲的数据*/ for(i 0; i MAX_COMMAND; i) { send(sock_fd, buff[i], 100, 0); n recv(sock_fd, tmp_buf, 100, 0); tmp_buf[n] \n; printf(data send:%s receive %s\n,buff[i], tmp_buf); if(0 strncmp(tmp_buf, quit, 4)) break; } close(sock_fd); return 0; }
编译客户端和服务器端程序
$gcc client.c -o client
$gcc serv.c -o serv
运行服务器输出 $./serv success to creat socket 3 success to bind address! success to listen! number of receive bytes 100 data abc number of receive bytes 100 data def number of receive bytes 100 data test number of receive bytes 100 data hello number of receive bytes 100 data quit
在另一终端运行客户端输出
$ ./client success to creat socket 3 success connect to server! data send: receive abc data send:def receive def data send:test receive test data send:hello receive hello data send:quit receive quit
4. socket通信十分重要应用广泛它还有一种通信方式即无连接的socket通信UDP
无连接的socket通信比较简单它使用UDP协议不保证数据能否到达用在数据要求不高的地方如在线视频。无连接的socket通信不需要建立连接省去了维护连接的开销所以速率更快。
无连接的socket数据报通信流程如图2 图2 无连接的socket数据报通信 和面向连接的数据流通信不同无连接的socket数据报通信在服务器绑定socket到指定IP和端口后没有调用listen()函数进行监听也没有调用accept()函数对每个新的请求建立连接因为没有连接的概念传输层无法区分不同的连接也就不需要对每个新的请求建立连接在客户端创建socket后可以直接向服务器发送数据或者读取数据。
无连接的socket数据报通信发送和接收数据的函数和面向连接的套接字通信有点不同分别使用recvfrom()和sendto()函数进行数据的发送和接收。它们的定义如下
int recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen);
int sendto(int s, const void *msg, size_t len, int flags, struct sockaddr *to, socklen_t *tolen);
recvfrom用来从指定的IP和端口接收数据s是socket句柄buf是存放接收数据的缓冲首地址len是接收缓冲大小from是发送数据方的IP和端口号fromlen是sockaddr结构大小。如果接收到数据recvfrom返回接收到的字节数失败返回-1
sendto发送数据到指定的IP和端口s指定socket句柄msg是发送的数据的缓冲首地址len是缓冲大小to指定接收数据的IP和端口号tolen是sockaddr结构大小。sendto()如果调用成功返回发送的字节数失败返回-1。
无连接的时间服务通信实例
该例子服务器负责创建socket绑定IP和端口然后等待客户端发出请求当收到客户端的请求“time”后生成当前时间发送给客户端。客户端创建socket后直接向服务器发送请求时间命令之后等待服务器返回发送退出命令关闭连接。
1服务器端time_serv.c
#include arpa/inet.h //struct sockaddr_in #include sys/socket.h #include sys/types.h #include fcntl.h // open #include unistd.h // close #include netinet/in.h #include stdio.h #include errno.h #include string.h #include time.h
#define TIME_PORT 9090 #define DATA_SIZE 256
int main() { int sock_fd; struct sockaddr_in local; struct sockaddr_in from; int n; socklen_t fromlen; char buff[DATA_SIZE]; time_t cur_time; sock_fd socket(AF_INET, SOCK_DGRAM, 0); if( sock_fd 0) { perror(creat socket error!); return 0; } perror(Creat socket); /*设置server地址结构*/ bzero(local, sizeof(local)); local.sin_family AF_INET; local.sin_port htons(TIME_PORT); local.sin_addr.s_addr htons(INADDR_ANY); //local.sin_addr.s_addr inet_addr(192.168.1.21); bzero((local.sin_zero), 8); if(0 ! bind(sock_fd, (struct sockaddr*)local, sizeof(local))) { perror(bind address fail!\n); close(sock_fd); return 0; } printf(bind socket!); fromlen sizeof(from); printf(waiting request from client...\n); while(1) { n recvfrom(sock_fd, buff, sizeof(buff), 0, (struct sockaddr*)from, fromlen); if(n 0) { perror(recv data!\n); lose(sock_fd); return 0; } buff[n]\n; printf(client request:%s\n, buff); if(0 strncmp(buff, quit, 4)) break; if(0 strncmp(buff, time, 4)) { cur_time time(NULL); printf(w1\n); // strcpy(buff, asctime(gmtime(cur_time))); strcpy(buff, weishusheng); printf(now time is:%s,buff); sendto(sock_fd, buff, sizeof(buff), 0, (struct sockaddr*)from, fromlen); printf(w2\n); } } close(sock_fd); return 0;
} 2客户端程序time_client.c
#define TIME_PORT 9090 #define DATA_SIZE 256 int main() { int sock_fd; struct sockaddr_in serv; int n; socklen_t servlen; char buff[DATA_SIZE]; sock_fd socket(AF_INET, SOCK_DGRAM, 0); if( sock_fd 0) { perror(creat socket error!); return 0; } perror(Creat socket); bzero(serv, sizeof(serv)); serv.sin_family AF_INET; serv.sin_port htons(TIME_PORT); serv.sin_addr.s_addr htons(INADDR_ANY); //serv.sin_addr.s_addr inet_addr(192.168.1.21); bzero((serv.sin_zero), 8); servlen sizeof(serv); strcpy(buff, time); if(-1 sendto(sock_fd, buff, sizeof(buff), 0, (struct sockaddr*)serv, servlen)) { perror(send data!); close(sock_fd); return 0; } printf(send time request\n); n recvfrom(sock_fd, buff, sizeof(buff), 0, (struct sockaddr*)serv, servlen); printf(program goes to recvfrom()\n); if(n 0) { perror(recv data!\n); close(sock_fd); return 0; } buff[n]\n; printf(time from server:%s\n, buff); strcpy(buff,quit); if(-1 sendto(sock_fd, buff, sizeof(buff),0 , (struct sockaddr*)serv, servlen)) { perror(send data!); close(sock_fd); return 0; } printf(send quit command\n); close(sock_fd); return 0;
}
编译运行即可看到客户端和服务器的通信过程。 参考链接
https://www.cnblogs.com/thinkinglife/p/4518353.html