C语言实现TCP/IP连接
利用C语言,实现TCP/IP连接。其中采用CS模式+套接字的方式。
先上代码:
Server.c 服务端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
|
#include <fcntl.h> #include <stdlib.h> #include <string.h> #include <ws2tcpip.h> #include <stdio.h>
#define SERVER_PORT 6666
int client;
int serverSocket; unsigned char buffer[200]; int iDataNum;
int sockInit() {
struct sockaddr_in server_addr; struct sockaddr_in clientAddr;
int addr_len = sizeof(clientAddr);
WSADATA wsaData; WSAStartup(MAKEWORD(2, 2), &wsaData); if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) { printf("require version fail!"); return -1; }
if ((serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { perror("socket"); return 1; }
memset(&server_addr, 0, 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);
if (bind(serverSocket, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) { perror("connect"); return 1; }
if (listen(serverSocket, 5) < 0) { perror("listen"); return 1; } while (1) { printf("\n监听端口: %d\n", SERVER_PORT); client = accept(serverSocket, (struct sockaddr *)&clientAddr, (socklen_t *)&addr_len); if (client < 0) { perror("accept"); continue; }
printf("host: %s:%d\n\n", inet_ntoa(clientAddr.sin_addr), htons(clientAddr.sin_port)); break; } }
void get_msg(){ while (1) { memset(buffer, 0, sizeof(buffer)); printf("等待信息...\n"); while (1) { iDataNum = recv(client, buffer, sizeof(buffer), 0); if (iDataNum >= 0) break; } buffer[iDataNum] = '\0'; printf("收到的信息:%s\n\n",buffer);
memset(buffer, 0, sizeof(buffer)); printf("请输入发送的内容:"); scanf("%s",&buffer); send(client, buffer, strlen(buffer), 0); printf("\n\n"); } }
int main() { sockInit(); get_msg(); getchar(); close(serverSocket);
return 0; }
|
Client.c 客户端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
|
#include <fcntl.h> #include <stdlib.h> #include <string.h> #include <ws2tcpip.h> #include <stdio.h>
#define SERVER_PORT 6666
int serverSocket; unsigned char buffer[200]; int iDataNum;
int sockInit() {
struct sockaddr_in serverAddr;
WSADATA wsaData; WSAStartup(MAKEWORD(2, 2), &wsaData); if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) { printf("require version fail!"); return -1; }
if ((serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { perror("socket"); return 1; }
serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(SERVER_PORT);
serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
if (connect(serverSocket, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) < 0) { perror("connect"); return 1; } return 1; }
int main() { if (sockInit() != 1) return -1;
printf("\n连接成功...\n\n");
while(1){
memset(buffer, 0, sizeof(buffer)); printf("请输入发送的内容:"); scanf("%s",&buffer); send(serverSocket, buffer, strlen(buffer), 0);
printf("\n\n");
memset(buffer, 0, sizeof(buffer)); printf("等待信息...\n"); while (1) { iDataNum = recv(serverSocket, buffer, sizeof(buffer), 0); if (iDataNum >= 0) break; } buffer[iDataNum] = '\0'; printf("收到的信息:%s\n\n",buffer);
}
getchar(); close(serverSocket);
return 0; }
|
其中,send()是用来发送数据的,recv()是用来接收数据的
接收数据的时候,通常情况下是不知道大小的,所以使用的是缓冲区的大小sizeof(buffer)
但在发送数据的时候,是可以知道数据的长度/大小的,所以可以直接用*strlen(buffer)*来获取和确定要发送的长度
注:直接在cmd用gcc编译时,记得加上 -lwsock32 (当前是Windows 10)
$ gcc .\Server.c -o .\Server.exe -lwsock32
注意
还有一点需要注意的是,我试过:先写好客户端只发送数据,服务端只接收数据
然后利用Client端 连续不间断地向Server端发送长度较大的数据,这时在Server端打印接收到的数据时,往往会显示(接收)不完全,或许是recv()中的数据并没有被完全读进buffer中去。
怎么说呢,就像是我Server端要接收一大串的数据,我这一大串(来自recv的)数据还没有读到尾(‘\0’) 内存还没读完,buffer就要被拿去printf了,printf完就被填充全0,准备下一次的数据接收。
那怎么办呢,要确保Server端完整读取发送过来的数据呀,那我加延时呗 等内存读完呗,延时完再打印和初始化buffer。
实验结果发现,确实可以,但!这个延时加在Server端却没用,对于Server端来说 每一条语句执行的顺序都是一样的,速度也是相当的,你不可能说加了个延时它就不接收数据了呀,对吧。
然后我把延时加在了Client端发送那里,确保每次发送都有一段不会太短的时间间隔,这样Server端就能完整读取完数据再处理下一次的接收。
真是非常的amazing啊,当初我还是搞网安玩加密的时候试出来的,调了半天,以为是因为加密后乱码 发过去了读不出来,结果才发现可能是内存没读完导致的,郁闷了两天(托腮.jpg)
不过这也只不过是我猜测的啦,真正具体是什么原因导致读不完我还不怎么清楚了啦……(蹲个大佬 我插个眼)