方案一TCP 完成聊天室的编写
完成聊天室 需要聊天 那就要建立連接?建立連接后 就發(fā)送信息
建立連接 和 發(fā)送信息 需要什么函數(shù)??
服務(wù)器 和 客戶端 各自需要什么?
服務(wù)器:
1.1 ?socket :創(chuàng)建一個Socket 用以監(jiān)聽 (一個空的通道 ?一頭將要連服務(wù)器 一頭將要連客戶)
1.2 ?Bind :綁定 IP 和 端口號
1.3 ?Listen :監(jiān)聽 ?兩個參數(shù) ?參數(shù)1 :socket 那個通道
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 參數(shù)2 :數(shù)字 ?監(jiān)聽隊列的大小 : 同一時刻 允許的最大連接數(shù)
?
1.4 ?accept :接受客戶發(fā)起的連接 ?參數(shù) 1 : ?socket
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?參數(shù) 2 :客戶端的IP 和端口...
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?參數(shù) 3 :結(jié)構(gòu)體的長度
?
1.5 ?send 和 recv
客戶端:
2.1 ?socket : 創(chuàng)建一個socket
2.2 ?connect : 連接
?
具體函數(shù)都可以利用man (manual)手冊進(jìn)行查詢 :例如man 2 socket 記得把其中的頭文件加入自己的代碼中
標(biāo)準(zhǔn)的man手冊主要分為8個章節(jié),分別為:
1 User Commands // 用戶命令
2 System Calls // 系統(tǒng)調(diào)用
3 C Library Functions // C函數(shù)庫調(diào)用
4 Devices and Special Files // 設(shè)備文件和特殊文件
5 File Formats and Conventions // 配置文件及格式
6 Games et. Al. // 游戲
7 Miscellanea // 雜項
8 System Administration tools and Deamons // 管理類命令
使用格式:
man [章節(jié)] COMMAND
q Q ZZ 退出
?
基本的函數(shù)
TCPserver.c :
?
1.1 ?Socket
創(chuàng)建一個int 型socketid接socket的返回值 返回值是一個文件描述符
一般的,文件描述符: 0 標(biāo)準(zhǔn)輸入 1 標(biāo)準(zhǔn)輸出 2 標(biāo)準(zhǔn)錯誤輸出
0、1、2是整數(shù)表示的,對應(yīng)的FILE *結(jié)構(gòu)的表示就是stdin、stdout、stderr,
0就是stdin,1就是stdout,2就是stderr
(函數(shù)原型)int socket(int domain, int type, int protocol);
Domain: 地址族協(xié)議 我們一般用PF_INET (ipv4協(xié)議)
Type: 套接字的類型 (TCP用SOCK_STREAM)(UDP用 SOCK_DGRAM)
前面博客中已經(jīng)告知所有相關(guān)知識點
Protocol: 一般默認(rèn)為0 這個參數(shù)是由前面兩個就決定好的 不需要特意再去指定
?
?
1.2 ?Bind
創(chuàng)建一個int型 ret 接bind的返回值 返回值 代表是否綁定成功
(函數(shù)原型)int bind ( int ?sockfd, ?const ?struct ?sockaddr ?*my_addr, ?socklen_t addrlen);
Sockfd: 文件描述符 指定綁定誰 (在服務(wù)器創(chuàng)建了socket之后當(dāng)然先把自己綁在通道的一端,等待客戶向自己發(fā)起連接)
my_addr: 是一個結(jié)構(gòu)體:struct ?sockaddr
Addrlen: 是第二個參數(shù) 結(jié)構(gòu)體的大小
?
**所以在第二個參數(shù)這里 我們就需要在開始bind前創(chuàng)建一個結(jié)構(gòu)體
Struct ??sockaddr_in ??server_addr;
并且對這個結(jié)構(gòu)體 內(nèi)部的數(shù)據(jù) 進(jìn)行初始化
memset(&server_addr, 0, sizeof(struct sockaddr_in)); //清空原始數(shù)據(jù) 防止出錯(這個函數(shù)需要+string.h頭文件)
server_addr.sin_family = AF_INET; //地址族協(xié)議 用ipv4
server_addr.sin_port = PORT; //指定端口號 PORT自己在最開始指定(我是8888)
server_addr.sin_addr.s_addr =?inet_addr("192.168.222.128");
//指定服務(wù)器的ip地址為 192.168.222.128
//這里 因為系統(tǒng)內(nèi)存放的數(shù)據(jù)是二進(jìn)制(長整型)所以用到轉(zhuǎn)換函數(shù) inet_addr( )
//函數(shù)原型in_addr_t inet_addr(const char* strptr);
//返回:若字符串有效則將字符串轉(zhuǎn)換為32位二進(jìn)制網(wǎng)絡(luò)字節(jié)序的IPV4地址
?
?
1.3 ?Listen
創(chuàng)建一個int型 ret 接Listen的返回值 返回值 代表是否綁定成功
(函數(shù)原型)int listen(int sockfd, int backlog);
Sockfd: 監(jiān)聽服務(wù)器創(chuàng)建的通道上是否有請求
Backlog: 同一時刻 允許的最大連接數(shù)
?
?
1.4 ?Accept
創(chuàng)建一個int型 fd [ i ] ?接Listen的返回值 返回值 代表接受到哪個客戶端發(fā)送來的請求
//fd [ i ] 因為你接受到的客戶請求可能不止一個 你需要把他們都記下來 一個個處理
//但是fd [ i ] 的值是從4開始的 前面說過了 012 標(biāo)準(zhǔn)--輸入、輸出、錯誤輸出 3 是自己創(chuàng)建的sockfd
(函數(shù)原型)int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
Sockfd: 文件描述符 指示哪個客戶的文件描述符有變化
ddr: 是一個結(jié)構(gòu)體:struct ?sockaddr 代表接受到的 客戶的地址
Addrlen: 是第二個參數(shù) 結(jié)構(gòu)體的大小
?
**所以在第二個參數(shù)這里 我們同樣要在開始前創(chuàng)建一個結(jié)構(gòu)體
Struct ??sockaddr_in ??client_addr;
memset(&server_addr, 0, sizeof(struct sockaddr_in)); //清空原始數(shù)據(jù) 防止出錯
?
?
1.5 ?創(chuàng)建線程pthread_create
創(chuàng)建一個int型 ret接pthread_create的返回值 返回值 代表線程是否創(chuàng)建成功
(函數(shù)原型)int pthread_create(pthread_t *restrict thread,
?????????????? const pthread_attr_t *restrict attr,
?????????????? void *(*start_routine)(void*), void *restrict arg);
restrict thread:開始就創(chuàng)建一個pthread_t tid[100]; 用以創(chuàng)建線程 因為一次接受就需要一次線程的創(chuàng)建;
restrict attr: 用于指定各種不同的線程屬性,默認(rèn)為NULL
*(*start_routine)(void*): 線程創(chuàng)建后 用此處對應(yīng)的函數(shù) 進(jìn)行線程的處理(完成此線程需要的功能)
restrict arg: &fd[ i ] ?: 最后一個參數(shù)是運行函數(shù)的參數(shù) 哪個客戶調(diào)用這個函數(shù)了
?
?
TCPclient.c
2.1 soket()
同上面服務(wù)器的一樣
2.2 connect()
(函數(shù)原型)int ?connect(int ?sockfd, ?const ?struct sockaddr *serv_addr, socklen_t ?addrlen);
Socket:?很明顯和上面差不多 這里的sockfd就是客戶自己創(chuàng)建的
serv_addr:?和上面一樣 自己一開始就創(chuàng)建一個結(jié)構(gòu)體就行 記得也要初始化
Addrlen:?大小就是結(jié)構(gòu)體的大小
?
?
最后就是寫一個 send 一個recv 函數(shù)用以接收和發(fā)送信息了。
?
TCPserver.c
#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <stdlib.h> #include <string.h> #include <netinet/in.h> #include <arpa/inet.h> #include <pthread.h>#define PORT 8888struct info {char buf[100];int to_fd; }; struct info SendBuf;void *HandlerClientThread(void *arg) {int ret;while (1){ret = recv(*(int *)arg, &SendBuf, sizeof(SendBuf), 0);if (-1 == ret){perror("recv");return;}//printf("Receive From %d : %s\n", *(int *)arg, buf);ret = send(SendBuf.to_fd, &SendBuf, sizeof(SendBuf), 0);if (-1 == ret){perror("send");return;}memset(&SendBuf, 0, sizeof(SendBuf));}}int main() {int sockfd, ret, fd[100] = {0}, length, i = 0;struct sockaddr_in server_addr;struct sockaddr_in client_addr;char buf[100] = {0};pthread_t tid[100];printf("START SERVER!\n");sockfd = socket(PF_INET, SOCK_STREAM, 0);if (-1 == sockfd){perror("sockfd");exit(1);}int opt = 1;setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));memset(&server_addr, 0, sizeof(struct sockaddr_in));server_addr.sin_family = AF_INET;server_addr.sin_port = PORT; // server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");server_addr.sin_addr.s_addr = inet_addr("192.168.222.128");ret = bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));if (-1 == ret){perror("bind");exit(1);}printf("WAITTING FOR CONNECT...\n");while(1){ret = listen(sockfd, 5);if (-1 == ret){perror("listen");exit(1);}length = sizeof(client_addr);fd[i] = accept(sockfd, (struct sockaddr *)&client_addr, &length);if (-1 == fd[i]){perror("accept");exit(1);}printf("ACCEPT %d ,Port = %d!\n", fd[i], client_addr.sin_port);ret = pthread_create(&tid[i], NULL, HandlerClientThread, (void *)&fd[i]);if (0 != ret){perror("create");exit(1);}i++;}return 0; }TCPclient.c
#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <stdlib.h> #include <string.h> #include <netinet/in.h> #include <arpa/inet.h> #include <pthread.h>#define PORT 8888struct info {char buf[100];int to_fd; };struct info SendBuf;void *MyReceive(void *arg) {int ret;while (1){ret = recv(*(int *)arg, &SendBuf, sizeof(SendBuf), 0);if (-1 == ret){perror("recv");return;}printf("\t\t\t%s\n", SendBuf.buf);memset(&SendBuf, 0, sizeof(SendBuf));} }//int main() int main(int argc, char *argv[]) {int sockfd, ret;struct sockaddr_in server_addr;char buf[100] = {0};pthread_t tid;sockfd = socket(PF_INET, SOCK_STREAM, 0);if (-1 == sockfd){perror("socket");exit(1);}memset(&server_addr, 0, sizeof(struct sockaddr_in));server_addr.sin_family = AF_INET;server_addr.sin_port = PORT; // server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");server_addr.sin_addr.s_addr = inet_addr(argv[1]);//自己從運行程序的時候傳進(jìn)來ret = connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));if (-1 == ret){perror("connect");exit(1);}ret = pthread_create(&tid, NULL, MyReceive, (void *)&sockfd);if (0 != ret){perror("create");exit(1);} while(1){scanf("%s %d", SendBuf.buf, &SendBuf.to_fd);ret = send(sockfd, &SendBuf, sizeof(SendBuf), 0);if (-1 == ret){perror("sand");exit(1);}}return 0; }?
總結(jié)
以上是生活随笔為你收集整理的方案一TCP 完成聊天室的编写的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 8-7复习 stl常用算法
- 下一篇: 8-9设计模式复习