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;
else
return 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 65534
void 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);
}