int serverSocket = socket(AF_INET, SOCK_STREAM, 0)
其中,第一个参数表示地址类型,AF_INET为IPV4,AF_INET6可支持IPV6;第二个参数表明是TCP【面向连接的稳定数据传输SOCK_STREAM】连接;第三个参数默认0
struct sockaddr_in server_addr; // 结构体 bzero(&server_addr, sizeof(server_addr)); // 判空 server_addr.sin_family = AF_INET; server_addr.sin_port = htons(SERVER_PORT); //server_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 本机通信,INADDR_ANY代表0.0.0.0:所有地址 server_addr.sin_addr.s_addr = inet_addr("186.66.66.66"); //非本机通信
申请了结构体sockaddr_in,将协议族设置为ipv4,传入了port,设置了ip
bind(serverSocket, (struct sockaddr *)&server_addr, sizeof(server_addr)
返回小于0为失败,否则绑定成功
注:
sockaddr在头文件#include <sys/socket.h>
中定义,sockaddr的缺陷是:sa_data把目标地址和端口信息混在一起了
sockaddr_in在头文件#include<netinet/in.h>或#include <arpa/inet.h>
中定义,该结构体解决了sockaddr的缺陷,把port和addr 分开储存在两个变量中
listen(serverSocket, 5)
返回小于0为失败,否则监听成功
struct sockaddr_in client_addr; int addr_len = sizeof(client_addr);
int clientSocket = accept(serverSocket, (struct sockaddr*)&client_addr, (socklen_t*)&addr_len);
返回小于0为失败,否则接受客户端连接
注:
accept接收服务端的链接请求,并返回一个客户端的socket句柄,参数一为服务端socket句柄,参数二为客户端结构体,参数三位客户端结构体长度,其中参数二和参数三为出参,可以解析到客户端的信息,如下:
printf("Client IP %s\n", inet_ntoa(client_addr.sin_addr)); printf("Client Port %d\n", htons(client_addr.sin_port));
char buffer[200]; int dataLen = recv(clientSocket , buffer, 1024, 0); buffer[dataLen] = '\0';
dateLen为接受到的data的大小,buffer为收到的消息
send(clientSocket , buffer, dataLen , 0);
使用while循环返回到accept函数
int clientSocket = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in serverAddr; serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(SERVER_PORT); //指定服务器端的ip,本地测试:127.0.0.1 //inet_addr()函数,将点分十进制IP转换成网络字节序IP serverAddr.sin_addr.s_addr = inet_addr("186.66.66.66");
connect(clientSocket, (struct sockaddr *)&serverAddr, sizeof(serverAddr))
返回小于0为失败,否则连接成功
char sendbuf[200] = {"sendMsg to server"}; char recvbuf[200] = {0}; send(clientSocket, sendbuf, strlen(sendbuf), 0); int recvData = recv(clientSocket, recvbuf, 200, 0); recvbuf[recvData] = '\0';
close(clientSocket);
参考:https://blog.csdn.net/lovekun1989/article/details/41042273
服务端代码如下:
/*socket tcp服务器端*/ #include <sys/stat.h> #include <fcntl.h> #include <errno.h> #include <netdb.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #define SERVER_PORT 12345 /* 监听后,一直处于accept阻塞状态, 直到有客户端连接, 当客户端输入quit后,客户端主动close与服务端的链接 */ int main() { int serverSocket; struct sockaddr_in server_addr; struct sockaddr_in client_addr; int addr_len = sizeof(client_addr); int clientSocket ; char buffer[200]; int dataLen; if((serverSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("socket"); return 1; } bzero(&server_addr, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(SERVER_PORT); //本机通信 //server_addr.sin_addr.s_addr = htonl(INADDR_ANY); //非本机通信 server_addr.sin_addr.s_addr = inet_addr("186.66.66.66"); //对于bind,accept之类的函数,里面套接字参数都是需要强制转换成(struct sockaddr *) if(bind(serverSocket, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) { perror("connect"); return 1; } //设置服务器上的socket为监听状态 if(listen(serverSocket, 5) < 0) { perror("listen"); return 1; } while(1) { printf("Listening on port: %d\n", SERVER_PORT); //调用accept函数后,会进入阻塞状态 //accept返回一个套接字的文件描述符,这样服务器端便有两个套接字的文件描述符, //serverSocket和clientSocket 。 //serverSocket仍然继续在监听状态,clientSocket 则负责接收和发送数据 //client_addr是一个传出参数,accept返回时,传出客户端的地址和端口号 //addr_len是一个传入-传出参数,传入的是调用者提供的缓冲区的client_addr的长度,以避免缓冲区溢出。 //传出的是客户端地址结构体的实际长度。 //出错返回-1 clientSocket = accept(serverSocket, (struct sockaddr*)&client_addr, (socklen_t*)&addr_len); if(clientSocket < 0) { perror("accept"); continue; } printf("\nrecv clientSocket data...n"); //inet_ntoa ip地址转换函数,将网络字节序IP转换为点分十进制IP //表达式:char *inet_ntoa (struct in_addr); printf("IP is %s\n", inet_ntoa(client_addr.sin_addr)); printf("Port is %d\n", htons(client_addr.sin_port)); while(1) { dataLen = recv(clientSocket , buffer, 1024, 0); if(dataLen < 0) { perror("recv"); continue; } buffer[dataLen] = '\0'; if(strcmp(buffer, "quit") == 0) break; printf("%drecv data is %s\n", dataLen, buffer); send(clientSocket , buffer, dataLen, 0); } } return 0; }
客户端代码如下:
/*socket tcp客户端*/ #include <sys/stat.h> #include <fcntl.h> #include <errno.h> #include <netdb.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #define SERVER_PORT 12345 /* 连接到服务器后,会不停循环,等待输入, 输入quit后,断开与服务器的连接 */ int main() { //客户端只需要一个套接字文件描述符,用于和服务器通信 int clientSocket; //描述服务器的socket struct sockaddr_in serverAddr; char sendbuf[200]; char recvbuf[200]; int recvData; if((clientSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("socket"); return 1; } serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(SERVER_PORT); //指定服务器端的ip,本地测试:127.0.0.1 //inet_addr()函数,将点分十进制IP转换成网络字节序IP serverAddr.sin_addr.s_addr = inet_addr("186.66.66.66"); if(connect(clientSocket, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) < 0) { perror("connect"); return 1; } printf("connect with destination host...\n"); while(1) { printf("Input your world:>"); scanf("%s", sendbuf); printf("\n"); send(clientSocket, sendbuf, strlen(sendbuf), 0); if(strcmp(sendbuf, "quit") == 0) break; recvData = recv(clientSocket, recvbuf, 200, 0); recvbuf[recvData] = '\0'; printf("recv data of my world is: %s\n", recvbuf); } close(clientSocket); return 0; }