tcp/ip是当前网络通信方式最常见的一种通信协议族。我们最常听说ip、端口号、域名。
现在我们就来讲一讲基础通信是如何实现的。
编程主通过socket套接字实现的,使用下面两个结构体,sockaddr,sockaddr_in。这两个是等价的。
struct sockaddr{/*地址族,就是一些协议类型的集合*/unsigned short sa_family;/*14 字节的协议地址,包含该 socket 的 IP 地址和端口号*/char sa_data[14];};struct sockaddr_in{short int sa_family; /*地址族*/unsigned short int sin_port; /*端口号*/struct in_addr sin_addr; /*IP 地址*//*全填 0,保持与 struct sockaddr 同样大小*/unsigned char sin_zero[8];}套接字类型流式 socket(SOCK_STREAM)流式套接字提供可靠的、面向连接的通信流;它使用 TCP 协议,从而保证了数据传输的正确性和顺序性。数据报 socket(SOCK_DGRAM)数据报套接字定义了一种无连接的服务,数据通过相互独立的报文进行传输,是无序的,并且不保证是可靠、无差错的。它使用的数据报协议是 UDP。
sa_family的范围
AF_INET: IPv4 协议
AF_INET6:IPv6 协议
AF_LOCAL: UNIX 域协议
这里我们先讲tcp,后来再说udp.
tcp server端
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <pthread.h>pthread_mutex_t mutex;int DataCmp(char *des,char *src,int num){int i;for(i=0;i<num;i++){if(*(des+i)!=*(src+i))break;}if(i==num)return 1;elsereturn 0;}void *client_process(void *arg){int recv_len = 0;char recv_buf[1024] = \"\"; // 接收缓冲区int connfd = *(int *)arg; // 传过来的已连接套接字char send_buf[1024] = \"\";// 解锁,pthread_mutex_lock()唤醒,不阻塞pthread_mutex_unlock(&mutex);// 接收数据while((recv_len = recv(connfd, recv_buf, sizeof(recv_buf), 0)) > 0){printf(\"recv_buf: %s\\n\", recv_buf); // 打印数据if(DataCmp((&recv_buf[4]),\"10\",2)==1){sprintf(send_buf,\"%s\",\"00111000001\");send(connfd, send_buf,strlen(send_buf), 0); // 给客户端回数据}if(DataCmp((&recv_buf[4]),\"05\",2)==1){sprintf(send_buf,\"%s\",\"0010050000\");send(connfd, send_buf,strlen(send_buf), 0); // 给客户端回数据}}printf(\"client closed!\\n\");close(connfd); //关闭已连接套接字return NULL;}int main(int argc, char *argv[]){int sockfd = 0; // 套接字int connfd = 0;int err_log = 0;struct sockaddr_in my_addr; // 服务器地址结构体unsigned short port = 8080; // 监听端口pthread_t thread_id;pthread_mutex_init(&mutex, NULL); // 初始化互斥锁,互斥锁默认是打开的printf(\"TCP Server Started at port %d!\\n\", port);sockfd = socket(AF_INET, SOCK_STREAM, 0); // 创建TCP套接字if(sockfd < 0){perror(\"socket error\");exit(-1);}bzero(&my_addr, sizeof(my_addr)); // 初始化服务器地址my_addr.sin_family = AF_INET;my_addr.sin_port = htons(port);my_addr.sin_addr.s_addr = htonl(INADDR_ANY);printf(\"Binding server to port %d\\n\", port);// 绑定err_log = bind(sockfd, (struct sockaddr*)&my_addr, sizeof(my_addr));if(err_log != 0){perror(\"bind\");close(sockfd);exit(-1);}err_log = listen(sockfd, 10);if( err_log != 0){perror(\"listen\");close(sockfd);exit(-1);}printf(\"Waiting client...\\n\");while(1){char cli_ip[INET_ADDRSTRLEN] = \"\"; // 用于保存客户端IP地址struct sockaddr_in client_addr; // 用于保存客户端地址socklen_t cliaddr_len = sizeof(client_addr);pthread_mutex_lock(&mutex);//获得一个已经建立的连接connfd = accept(sockfd, (struct sockaddr*)&client_addr, &cliaddr_len);if(connfd < 0){perror(\"accept this time\");continue;}// 打印客户端的 ip 和端口inet_ntop(AF_INET, &client_addr.sin_addr, cli_ip, INET_ADDRSTRLEN);printf(\"----------------------------------------------\\n\");printf(\"client ip=%s,port=%d\\n\", cli_ip,ntohs(client_addr.sin_port));if(connfd > 0){//给回调函数传的参数,&connfd,地址传递pthread_create(&thread_id, NULL, (void *)client_process, (void *)&connfd); //创建线程pthread_detach(thread_id); // 线程分离,结束时自动回收资源}}close(sockfd);return 0;}
tcp 客户端
#include <stdio.h>#include <unistd.h>#include <sys/socket.h>#include <sys/types.h>#include <netinet/in.h>#include <pthread.h>#include <sys/ioctl.h>#include <netdb.h>#include <stdlib.h>#include <string.h>int sock_fd;#define PORT 65534void test(void *arg){while(1){int connfd = *(int *)arg; // 传过来的已连接套接字send(connfd, \"123\",3, 0);sleep(2);}}int main(int argc,char argv[]){struct sockaddr_in server_addr;pthread_t id1;if((sock_fd=socket(AF_INET,SOCK_STREAM,0))==-1){printf(\"socket create fail\\r\\n\");exit(1);}server_addr.sin_family=AF_INET;server_addr.sin_port=htons(PORT );server_addr.sin_addr.s_addr=inet_addr(\"139.196.34.130\");//根据自己的自行修改,随便写的bzero(&(server_addr.sin_zero),8);if(connect(sock_fd,(struct sockaddr *)&server_addr,sizeof(struct sockaddr))==-1){printf(\"connect fail\\r\\n\");exit(1);}pthread_create(&id1,NULL,(void *)test,(void *)&sock_fd);pthread_join(id1,NULL);}