套接字编程实验
为取用方便,代码包含:
(1)路径斜杠替换 => 方便文件读取
(2)图片像素数据用文件保存
(3)遍历文件夹下所有文件名
(4)分块文件数据的组合
(5)套接字面向连接的 TCP 数据传输
(6)多线程
(7)字符串数据的分割
(8)字符串与整数的相互转换
(9)字符串相关操作 => string.h
(10)图像打印与保存 => EGE图形库
(11)文件大小获取、文件读取
(12)字符串格式规范 => sprintf
实验答辩 PPT
附:
(1)服务器代码:
1 #include <stdio.h> 2 #include <string.h> 3 #include <pthread.h> 4 // Winsock是一个基于Socket模型的API 5 #include <winsock2.h> 6 #include <windows.h> 7 #include "graphics.h" 8 9 // 要包含头文件 Winsock2.h,就需要调用库 ws2_32.lib 10 #pragma comment( lib, "ws2_32.lib" ) 11 12 13 14 /* Winsock启动测试,检查系统协议栈安装情况 */ 15 void TestWinsockStartup(); 16 17 18 // 分块大小固定为100KB 19 20 21 /* 定义全局变量 */ 22 // 待获取的文件名后缀 (除序号外的信息) 23 char getFileName[] = "_28#1440_665.myt"; 24 // 用来存储图像数据 25 int width=1440, height=665; // 也可通过填写的待获取文件名后缀获取图片宽高信息 26 // 待维护的要获取文件数组 (数组大小也可通过上面方法获得) 27 const int block_num = 28 + 1; 28 // block该由哪个序号的 peer上传 (上传名额被谁抢到了) 29 int blocks[block_num]; 30 // 每个分块的数据 31 char block_data[block_num][100*1024]; // 每个分块为 100KB 32 33 34 35 /* 要经常检查分块是否已全部收到 */ 36 bool isReceiveAll() { 37 bool flag = true; 38 for(int i=0; i<block_num; i++) { 39 if( blocks[i] == -1 ) { 40 flag = false; 41 } 42 } 43 return flag; 44 } 45 46 47 48 /* 保存连接的 client信息 */ 49 struct SocketInfo { 50 SOCKADDR_IN cli; // 其实只有 accept()时才用到 cli,连接后几乎可以不用 51 SOCKET sAccept; 52 } sAccepts[20]; // 最多 20个client连接 53 int peer_id = 0; // peer序号计数 54 55 56 // 每个 client拥有的文件 57 // 下标就是 client的序号 58 struct ClientFiles { 59 int len; // 拥有总文件数 60 char file_name[50][50]; // 文件名 61 int idx[50]; // 文件序号 62 bool getFile[50]; // 文件是否要索要 63 } clientFiles[20]; // 最多 20个client连接 64 65 66 67 // 获取文件名列表 68 void *AskFilesName( void *arg ) { 69 int id = *(int *)(arg); 70 // printf("id: %d\n", id); 71 72 SOCKET *sAccept = &sAccepts[id].sAccept; 73 char command[] = "AskFilesName"; 74 75 // 1. 索要所有文件名 76 int iSend = send( *sAccept, command, sizeof(command), 0 ); 77 if( iSend == SOCKET_ERROR ) { 78 printf("send() Failed: %d\n", WSAGetLastError()); 79 } else if( iSend == 0) { 80 printf("发送失败!\n"); 81 } else { 82 printf(">>> peer%d所有文件名给我看看!\n", id); 83 } 84 85 // 2. 接收所有文件名 86 char str[1024*3]; 87 int iLen = recv( *sAccept, str, sizeof(str), 0 ); 88 if( iLen == SOCKET_ERROR ) { 89 printf("recv() Failed: %d\n", WSAGetLastError()); 90 } else if( iLen == 0 ) { 91 printf("接收失败!\n"); 92 } else { 93 printf("%s\n", str); 94 } 95 96 // 3. 将收到的字符串分割成文件名存放在结构体中 97 char num_str[10]; 98 int i, j; 99 for(i=0; str[i]!='&'; i++) { 100 num_str[i] = str[i]; 101 } 102 num_str[i++] = '\0'; 103 clientFiles[peer_id].len = atoi(num_str); 104 // 分割 105 char file_name[50]; 106 int index=0, cnt=0; 107 for( ; str[i]!='\0'; i++) { 108 if( str[i]=='&' ) { 109 file_name[index] = '\0'; 110 // printf("%s\n", file_name); 111 // 得到序号 112 char ttmp[10]; 113 for(j=0; file_name[j]!='_'; j++) { 114 ttmp[j] = file_name[j]; 115 } 116 ttmp[j] = '\0'; 117 // 添加上序号 118 clientFiles[peer_id].idx[cnt] = atoi(ttmp); 119 // 添加上文件名 120 strcpy( clientFiles[peer_id].file_name[cnt], file_name ); 121 index = 0; 122 cnt++; 123 } else { 124 file_name[index++] = str[i]; 125 } 126 } 127 // 文件总数目 128 clientFiles[peer_id].len = cnt; 129 // 索要文件初始化 130 for(i=0; i<50; i++) { 131 clientFiles[peer_id].getFile[i] = false; 132 } 133 134 // 4. 打印看看有没有错误 135 // printf("peer%d:\nlen: %d\n", peer_id, clientFiles[peer_id].len); 136 // for(i=0; i<clientFiles[peer_id].len; i++) { 137 // printf("file index: %d\n", clientFiles[peer_id].idx[i]); 138 // printf("file name: %s\n", clientFiles[peer_id].file_name[i]); 139 // } 140 141 // 5. 为该 peer抢分块上传名额 142 char send_str[1024] = ""; 143 int count = 0; // 要传输的文件计数 144 for(i=0; i<clientFiles[peer_id].len; i++) { 145 // peer拥有的第 i个文件的分块序号 146 int idx = clientFiles[peer_id].idx[i]; 147 // 这个分块还没被抢 148 if( blocks[idx] == -1 ) { 149 blocks[idx] = peer_id; // 这个分块被 peer_id抢了 150 clientFiles[peer_id].getFile[idx] = true; // 待上传文件 151 count++; // 该 peer将要传的文件数量 152 // 待索要的文件名 加入待传输字符串中 153 char tmp[10]; 154 itoa(idx, tmp, 10); // 十进制 155 strcat(send_str, tmp); 156 strcat(send_str, getFileName); // 文件后缀 (除序号外的信息) 157 strcat(send_str, "&"); 158 } 159 strcat(send_str, "\0"); 160 } 161 // for(i=0; i<block_num; i++) printf("%d: %d\n", i, blocks[i]); 162 163 // 6. 接收文件 164 // 发出所有需要的文件的文件名 165 iSend = send( *sAccept, send_str, sizeof(send_str), 0 ); 166 if( iSend == SOCKET_ERROR ) { 167 printf("send() Failed: %d\n", WSAGetLastError()); 168 } else if( iSend == 0 ) { 169 printf("发送失败!\n"); 170 } else { 171 printf(">>> 文件传输列表:%s\n", send_str); 172 } 173 short *img_mx = (short *)malloc(width*height*3*sizeof(short)); 174 // 接收所有 getFile = true的文件 175 for(i=0; i<clientFiles[peer_id].len; i++) { 176 int index = clientFiles[peer_id].idx[i]; 177 // 索要的文件 178 if( clientFiles[peer_id].getFile[index] ) { 179 iLen = recv( *sAccept, block_data[index], sizeof(block_data[index]), 0 ); 180 if( iLen == SOCKET_ERROR ) { 181 printf("recv() Failed: %d\n", WSAGetLastError()); 182 } else if( iLen == 0 ) { 183 printf("第%d个分块丢失了\n", index); 184 } else { 185 printf("第%d个分块已接收\n", index); 186 } 187 } 188 } 189 190 if( isReceiveAll() ) { 191 printf(">>> 所有分块均已收到!\n"); 192 193 // 7. 将所有分块数据转化成图像数据 194 // 分块拼接起来 195 char *total = (char *)malloc(width*height*3*sizeof(char)); 196 int tcnt = 0; 197 for(i=0; i<block_num; i++) { 198 for(j=0; j<100*1024; j++) { 199 if( i*100*1024+j >= width*height*3 ) { 200 break; 201 } 202 *(img_mx+tcnt) = (unsigned char)block_data[i][j]; 203 tcnt++; 204 // printf("%hd ", *(img_mx+tcnt)); 205 } 206 // printf("j:%d\n", j); 207 } 208 // printf("tcnt: %d", tcnt); 209 210 // 8. 打印图像矩阵 211 short red, green, blue; 212 for(i=0; i<width; i++) { 213 for(j=0; j<height; j++) { 214 red = *(img_mx+3*(height*i+j)+0); 215 green = *(img_mx+3*(height*i+j)+1); 216 blue = *(img_mx+3*(height*i+j)+2); 217 putpixel(i, j, EGERGB(red, green, blue)); 218 } 219 } 220 // 保存图像 221 PIMAGE pimg = newimage(); 222 getimage(pimg, 0, 0, getwidth(), getheight()); 223 saveimage(pimg, "receive.jpg"); 224 225 // 在此关闭连接 226 closesocket( *sAccept ); 227 free(img_mx); 228 delimage(pimg); 229 return NULL; 230 } 231 232 // 在此关闭连接 233 closesocket( *sAccept ); 234 return NULL; 235 } 236 237 238 239 int main() { 240 initgraph(width, height, 0); 241 setcaption("ByHansel"); 242 243 // Winsock启动测试 244 TestWinsockStartup(); 245 246 // 分块初始化 247 for(int i=0; i<block_num; i++) blocks[i]=-1; // -1代表还没人抢到 248 249 SOCKET sListen; 250 struct sockaddr_in ser; 251 252 // 1. 创建服务器端通信套接字 253 sListen = socket( AF_INET, SOCK_STREAM, 0 ); 254 if( sListen == INVALID_SOCKET ) { 255 printf("socket() Failed: %d\n", WSAGetLastError()); 256 return -1; 257 } 258 259 // 2. 将创建的套接字与服务器地址进行绑定 260 ser.sin_family = AF_INET; 261 ser.sin_port = htons( 44965 ); // 端口号 262 ser.sin_addr.s_addr = htonl( INADDR_ANY ); 263 if( bind(sListen, (LPSOCKADDR)&ser, sizeof(ser)) == SOCKET_ERROR ){ 264 printf("bind() Failed: %d\n", WSAGetLastError()); 265 return -1; 266 } 267 268 // 3. 进入监听状态 269 if( listen(sListen, 5) == SOCKET_ERROR ) { 270 printf("lisiten() Failed: %d\n", WSAGetLastError()); 271 return -1; 272 } 273 274 275 // 4. 获取所有文件列表 276 peer_id = 0; 277 printf(">>> 等待peer接入!\n"); 278 while(1) { 279 // 4.1. 接收到客户连接请求 280 SOCKET *sAccept = &sAccepts[peer_id].sAccept; 281 SOCKADDR_IN *cli = &sAccepts[peer_id].cli; 282 int iLen = sizeof(*cli); 283 *sAccept = accept( sListen, (struct sockaddr *)cli, &iLen ); 284 if( *sAccept == INVALID_SOCKET ) { 285 printf("accept() Failed: %d\n", WSAGetLastError()); 286 return -1; 287 } 288 // 输出客户 IP地址和端口号 289 printf("Accepted client IP:[%s], port:[%d]\n", inet_ntoa(cli->sin_addr), ntohs(cli->sin_port)); 290 291 // 4.2. 创建新进程处理连接请求 292 pthread_t tid; 293 // 接收分块 0的文件名和数据 294 int id = peer_id; // 不能直接传 peer_id的地址 295 pthread_create(&tid, NULL, AskFilesName, &id); 296 // 主线程与子线程分离,子线程结束后,资源自动回收 297 pthread_detach(tid); 298 peer_id++; 299 } 300 301 getch(); 302 closegraph(); 303 closesocket(sListen); 304 WSACleanup(); 305 return 0; 306 } 307 308 309 310 /* Winsock启动测试,检查系统协议栈安装情况 */ 311 void TestWinsockStartup() { 312 WORD wVersionRequested; 313 WSADATA wsaData; 314 wVersionRequested = MAKEWORD(2, 2); 315 316 if( WSAStartup(wVersionRequested, &wsaData) != 0 ) { 317 printf("Winsock初始化错误!\n"); 318 return ; 319 } 320 if( wsaData.wVersion != wVersionRequested ) { 321 printf("Winsock版本不匹配!\n"); 322 WSACleanup(); 323 return ; 324 } 325 // printf("WinsockDLL正确加载!\n"); 326 }
(2)客户端代码:
1 #include <stdio.h> 2 #include <string.h> 3 #include <io.h> 4 // Winsock是一个基于Socket模型的API 5 #include <winsock2.h> 6 7 // 要包含头文件 Winsock2.h,就需要调用库 ws2_32.lib 8 #pragma comment( lib, "ws2_32.lib" ) 9 10 11 12 /* Winsock启动测试,检查系统协议栈安装情况 */ 13 void TestWinsockStartup(); 14 15 16 17 char IPaddr[] = "127.0.0.1"; 18 int main() { 19 // Winsock启动测试 20 TestWinsockStartup(); 21 22 SOCKET sClient; 23 // 从服务器端接收的数据长度 24 int iLen; 25 // 接收数据的缓冲区 26 char buf[50]; 27 //接收缓冲区初始化 28 memset( buf, 0, sizeof(buf) ); 29 // 服务器端地址 30 struct sockaddr_in ser; 31 32 33 // 1. 填写要连接的服务器地址信息 34 ser.sin_family = AF_INET; 35 ser.sin_port = htons( 44965 ); 36 ser.sin_addr.s_addr = inet_addr( IPaddr ); 37 38 // 2. 建立客户端流式套接字 39 sClient = socket( AF_INET,SOCK_STREAM, 0 ); 40 if( sClient == INVALID_SOCKET ) { 41 printf("socket() Failed: %d\n", WSAGetLastError()); 42 return -1; 43 } 44 45 // 3. 请求与服务器端建立 TCP连接 46 if( connect(sClient, (struct sockaddr *)&ser, sizeof(ser)) == INVALID_SOCKET ) { 47 printf("connect() Failed: %d\n", WSAGetLastError()); 48 return -1; 49 } else { 50 // 从服务器端接收命令 51 iLen = recv( sClient, buf, sizeof(buf), 0 ); 52 if( iLen == SOCKET_ERROR ) { 53 printf("recv() Failed: %d\n", WSAGetLastError()); 54 return -1; 55 } else if( iLen == 0) { 56 printf("接收失败!\n"); 57 } else { 58 // 4. 交出所有文件名 59 if( strcmp(buf, "AskFilesName") == 0 ) { 60 /* 拥有的所有文件名发过去 */ 61 char str[1024] = ""; // 要发送的字符串 62 // 遍历文件夹 63 _finddata_t fileDir; 64 long lfDir; 65 int len = 0; // 文件总数量 66 // _findfirst():如果查找成功的话,将返回一个 long型的唯一的查找用的句柄 67 if( ( lfDir=_findfirst("upload//*.myt", &fileDir) ) == -1l ) { 68 printf("No file is found\n"); 69 } else { 70 // _findnext():若成功返回0,否则返回-1 71 do { 72 len++; 73 // &为每个文件名的分隔符 74 strcat(str, "&"); 75 strcat(str, fileDir.name); 76 } while( !_findnext( lfDir, &fileDir ) ); 77 } 78 // 末尾加上 79 strcat(str, "&\0"); 80 _findclose(lfDir); 81 82 // 将文件总数量放在最前面 83 char tmp[1024]; 84 itoa(len, tmp, 10); // 十进制 85 strcat(tmp, str); 86 // 将包含所有文件名的字符串传过去 87 int iSend = send( sClient, tmp, sizeof(str), 0 ); 88 if( iSend == SOCKET_ERROR ) { 89 printf("send() Failed: %d\n", WSAGetLastError()); 90 } else if( iSend == 0 ) { 91 printf("发送失败!\n"); 92 } else { 93 printf(">>> 我全上报了!\n"); 94 } 95 } 96 } 97 // 5. 接收文件传输列表 98 char recv_str[1024]; 99 iLen = recv( sClient, recv_str, sizeof(recv_str), 0 ); 100 if( iLen == SOCKET_ERROR ) { 101 printf("recv() Failed: %d\n", WSAGetLastError()); 102 return -1; 103 } else if( iLen == 0 ) { 104 printf("接收失败!\n"); 105 } else { 106 printf("待传输文件:%s\n", recv_str); 107 } 108 109 // 6. 传输给定文件 110 // 分割文件名 111 char file_name[50][50]; 112 int count=0, index=0; 113 for(int i=0; recv_str[i]!='\0'; i++) { 114 if( recv_str[i] == '&' ) { 115 file_name[count][index] = '\0'; 116 // printf("%s\n", file_name[count]); 117 count++; 118 index = 0; 119 } else { 120 file_name[count][index++] = recv_str[i]; 121 } 122 } 123 // 传输文件 124 for(int i=0; i<count; i++) { 125 // 读取文件 126 char read_data[100*1024]; // 一次最多读 100KB数据 127 char PATH[50]; 128 sprintf(PATH, "%s%s", "upload/", file_name[i]); 129 FILE *fp; 130 fp = fopen(PATH, "rb"); 131 printf("%s\n", PATH); 132 // 读取文件 133 fseek( fp, 0, SEEK_END ); 134 int file_size = ftell( fp ); 135 fseek( fp, 0, SEEK_SET ); 136 fread( read_data, sizeof(char), file_size, fp ); 137 fclose(fp); 138 // 发送文件流 139 int iSend = send( sClient, read_data, sizeof(read_data), 0 ); 140 if( iSend == SOCKET_ERROR ) { 141 printf("send() Failed: %d\n", WSAGetLastError()); 142 } else if( iSend == 0 ) { 143 printf("发送失败!\n"); 144 } else { 145 printf("%s已成功发送!\n", file_name[count]); 146 } 147 Sleep(1); 148 } 149 } 150 151 closesocket(sClient); 152 WSACleanup(); 153 system("pause"); 154 return 0; 155 } 156 157 158 159 /* Winsock启动测试,检查系统协议栈安装情况 */ 160 void TestWinsockStartup() { 161 WORD wVersionRequested; 162 WSADATA wsaData; 163 wVersionRequested = MAKEWORD(2, 2); 164 165 if( WSAStartup(wVersionRequested, &wsaData) != 0 ) { 166 printf("Winsock初始化错误!\n"); 167 return ; 168 } 169 if( wsaData.wVersion != wVersionRequested ) { 170 printf("Winsock版本不匹配!\n"); 171 WSACleanup(); 172 return ; 173 } 174 // printf("WinsockDLL正确加载!\n"); 175 }
(3)图片分块代码:
1 #include <stdio.h> 2 #include <string.h> 3 #include <math.h> 4 #include "graphics.h" 5 6 char PATH[] = "building.jpg"; 7 int size = 100; // KB 8 9 int main() { 10 color_t color; 11 int i, j; 12 short width, height, red, green, blue; 13 14 for(i=0; i<strlen(PATH); i++) { 15 if(PATH[i] == '\\') { 16 PATH[i] = '/'; 17 } 18 } 19 20 PIMAGE pimg = newimage(); 21 getimage(pimg, PATH); 22 width = getwidth(pimg); 23 height = getheight(pimg); 24 short *img_mx = (short *)malloc(width*height*3*sizeof(short)); 25 26 for(i=0; i<width; i++) { 27 for(j=0; j<height; j++) { 28 color = getpixel(i, j, pimg); 29 red = EGEGET_R(color); 30 green = EGEGET_G(color); 31 blue = EGEGET_B(color); 32 *(img_mx+3*(height*i+j)+0) = red; 33 *(img_mx+3*(height*i+j)+1) = green; 34 *(img_mx+3*(height*i+j)+2) = blue; 35 } 36 } 37 38 39 40 41 // 待保存数据 42 char *save = (char *)malloc(width*height*3*sizeof(char)); 43 for(i=0; i<width*height*3; i++) { 44 // 用 char保存图像灰度值数据 45 *(save+i) = (char)*(img_mx+i); 46 } 47 48 // 文件分块数量 49 int num_block = ceil(width*height*3.0/size/1024.0); 50 char file_name[50]; 51 for(i=0; i<num_block; i++) { 52 // 文件取名 53 sprintf(file_name, "%d_%d#%d_%d", i, num_block-1, width, height); 54 strcat(file_name, ".myt"); 55 printf("%s\n", file_name); 56 FILE *fp; 57 fp = fopen(file_name, "wb"); 58 59 for(j=0; j<size*1024; j++) { 60 if( i*size*1024+j < width*height*3 ) { 61 fprintf(fp, "%c", *(save+i*size*1024+j)); 62 } else { 63 break; 64 } 65 } 66 67 fclose(fp); 68 } 69 70 71 free(img_mx); 72 free(save); 73 delimage(pimg); 74 return 0; 75 }
(4)图片组合代码:
1 #include <stdio.h> 2 #include <string.h> 3 #include <math.h> 4 #include <io.h> 5 #include "graphics.h" 6 7 int width=1440, height=665; 8 int size = 100; // KB 9 10 11 void SortByIndex(char files[][50], int n) { 12 int i, j; 13 char tmp[n][50], ch[50]; 14 15 int index[n]; 16 17 // 数组复制 18 for(i=0; i<n; i++) { 19 strcpy(tmp[i], files[i]); 20 for(j=0; tmp[i][j]!='_'; j++) { 21 ch[j] = tmp[i][j]; 22 } 23 ch[j] = '\0'; 24 index[i] = atoi(ch); 25 } 26 27 // 排序 28 for(i=0; i<n-1; i++) { 29 for(j=i+1; j<n; j++) { 30 if( index[i] > index[j] ) { 31 int tt = index[i]; 32 index[i] = index[j]; 33 index[j] = tt; 34 strcpy(ch, files[i]); 35 strcpy(files[i], files[j]); 36 strcpy(files[j], ch); 37 } 38 } 39 } 40 } 41 42 43 int main() { 44 initgraph(width, height, 0); 45 setcaption("ByHansel"); 46 47 char ch; 48 int i, j; 49 short red, green, blue; 50 short *img_mx = (short *)malloc(width*height*3*sizeof(short)); 51 52 char files[50][50]; 53 int idx = 0; 54 55 // 遍历 upload文件夹内所有文件 56 _finddata_t fileDir; 57 long lfDir; 58 // _findfirst():如果查找成功的话,将返回一个long型的唯一的查找用的句柄 59 if( ( lfDir=_findfirst("download//*.myt", &fileDir) ) == -1l ) { 60 printf("No file is found\n"); 61 } else { 62 // printf("file list:\n"); 63 // _findnext():若成功返回0,否则返回-1 64 do { 65 strcpy(files[idx++], fileDir.name); 66 } while( !_findnext( lfDir, &fileDir ) ); 67 } 68 _findclose(lfDir); 69 70 71 // 根据文件序号将文件在数组中排序 72 SortByIndex(files, idx); 73 74 75 76 char PATH[50]; 77 int num_block = ceil(width*height*3.0/size/1024.0); 78 79 // 组合分块 80 for(i=0; i<num_block; i++) { 81 sprintf(PATH, "%s%s", "download/", files[i]); 82 FILE *fp; 83 fp = fopen(PATH, "rb"); 84 printf("%s\n", PATH); 85 86 for(j=0; j<size*1024; j++) { 87 if( i*size*1024+j < width*height*3 ) { 88 // 可以在此将分块数据进行发送 89 fscanf(fp, "%c", &ch); 90 *(img_mx+i*size*1024+j) = (unsigned char)ch; 91 } else { 92 break; 93 } 94 } 95 fclose(fp); 96 } 97 98 99 // 打印图像矩阵 100 for(i=0; i<width; i++) { 101 for(j=0; j<height; j++) { 102 red = *(img_mx+3*(height*i+j)+0); 103 green = *(img_mx+3*(height*i+j)+1); 104 blue = *(img_mx+3*(height*i+j)+2); 105 putpixel(i, j, EGERGB(red, green, blue)); 106 } 107 } 108 109 // 保存图像 110 PIMAGE pimg = newimage(); 111 getimage(pimg, 0, 0, getwidth(), getheight()); 112 saveimage(pimg, "receive.jpg"); 113 114 // 图像移动功能 115 int x=0, y=0, num=50; 116 for (; is_run(); delay_fps(20)) { 117 key_msg keyMsg = {0}; 118 while(kbhit()) { 119 keyMsg = getkey(); 120 if(keyMsg.key == key_left) { 121 x -= num; 122 } else if(keyMsg.key == key_up) { 123 y -= num; 124 } else if(keyMsg.key == key_right) { 125 x += num; 126 } else if(keyMsg.key == key_down) { 127 y += num; 128 } 129 cleardevice(); 130 for(i=0; x+i<width && i<width; i++) { 131 for(j=0; y+j<height && j<height; j++) { 132 if(x+i>=0 && y+j>=0) { 133 red = *(img_mx+3*(height*i+j)+0); 134 green = *(img_mx+3*(height*i+j)+1); 135 blue = *(img_mx+3*(height*i+j)+2); 136 putpixel(x+i, y+j, EGERGB(red,green, blue)); 137 } 138 } 139 } 140 } 141 } 142 143 getch(); 144 free(img_mx); 145 delimage(pimg); 146 closegraph(); 147 return 0; 148 }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了