完成聊天室 需要聊天 那就要建立連接 建立連接後 就發送信息
建立連接 和 發送信息 需要什麼函數 ?
服務器 和 客戶端 各自需要什麼?
服務器:
1.1 socket :創建一個Socket 用以監聽 (一個空的通道 一頭將要連服務器 一頭將要連客戶)
1.2 Bind :綁定 IP 和 端口號
1.3 Listen :監聽 兩個參數 參數1 :socket 那個通道
參數2 :數字 監聽隊列的大小 : 同一時刻 允許的最大連接數
1.4 accept :接受客戶發起的連接 參數 1 : socket
參數 2 :客戶端的IP 和端口...
參數 3 :結構體的長度
1.5 send 和 recv
客戶端:
2.1 socket : 創建一個socket
2.2 connect : 連接
具體函數都可以利用man (manual)手冊進行查詢 :例如man 2 socket 記得把其中的頭文件加入自己的代碼中
標準的man手冊主要分爲8個章節,分別爲:
1 User Commands // 用戶命令
2 System Calls // 系統調用
3 C Library Functions // C函數庫調用
4 Devices and Special Files // 設備文件和特殊文件
5 File Formats and Conventions // 配置文件及格式
6 Games et. Al. // 遊戲
7 Miscellanea // 雜項
8 System Administration tools and Deamons // 管理類命令
使用格式:
man [章節] COMMAND
q Q ZZ 退出
基本的函數
TCPserver.c :
1.1 Socket
創建一個int 型socketid接socket的返回值 返回值是一個文件描述符
一般的,文件描述符: 0 標準輸入 1 標準輸出 2 標準錯誤輸出
0、1、2是整數表示的,對應的FILE *結構的表示就是stdin、stdout、stderr,
0就是stdin,1就是stdout,2就是stderr
(函數原型)int socket(int domain, int type, int protocol);
Domain: 地址族協議 我們一般用PF_INET (ipv4協議)
Type: 套接字的類型 (TCP用SOCK_STREAM)(UDP用 SOCK_DGRAM)
前面博客中已經告知所有相關知識點
Protocol: 一般默認爲0 這個參數是由前面兩個就決定好的 不需要特意再去指定
1.2 Bind
創建一個int型 ret 接bind的返回值 返回值 代表是否綁定成功
(函數原型)int bind ( int sockfd, const struct sockaddr *my_addr, socklen_t addrlen);
Sockfd: 文件描述符 指定綁定誰 (在服務器創建了socket之後當然先把自己綁在通道的一端,等待客戶向自己發起連接)
my_addr: 是一個結構體:struct sockaddr
Addrlen: 是第二個參數 結構體的大小
**所以在第二個參數這裏 我們就需要在開始bind前創建一個結構體
Struct sockaddr_in server_addr;
並且對這個結構體 內部的數據 進行初始化
memset(&server_addr, 0, sizeof(struct sockaddr_in)); //清空原始數據 防止出錯(這個函數需要+string.h頭文件)
server_addr.sin_family = AF_INET; //地址族協議 用ipv4
server_addr.sin_port = PORT; //指定端口號 PORT自己在最開始指定(我是8888)
server_addr.sin_addr.s_addr = inet_addr("192.168.222.128");
//指定服務器的ip地址爲 192.168.222.128
//這裏 因爲系統內存放的數據是二進制(長整型)所以用到轉換函數 inet_addr( )
//函數原型in_addr_t inet_addr(const char* strptr);
//返回:若字符串有效則將字符串轉換爲32位二進制網絡字節序的IPV4地址
1.3 Listen
創建一個int型 ret 接Listen的返回值 返回值 代表是否綁定成功
(函數原型)int listen(int sockfd, int backlog);
Sockfd: 監聽服務器創建的通道上是否有請求
Backlog: 同一時刻 允許的最大連接數
1.4 Accept
創建一個int型 fd [ i ] 接Listen的返回值 返回值 代表接受到哪個客戶端發送來的請求
//fd [ i ] 因爲你接受到的客戶請求可能不止一個 你需要把他們都記下來 一個個處理
//但是fd [ i ] 的值是從4開始的 前面說過了 012 標準--輸入、輸出、錯誤輸出 3 是自己創建的sockfd
(函數原型)int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
Sockfd: 文件描述符 指示哪個客戶的文件描述符有變化
ddr: 是一個結構體:struct sockaddr 代表接受到的 客戶的地址
Addrlen: 是第二個參數 結構體的大小
**所以在第二個參數這裏 我們同樣要在開始前創建一個結構體
Struct sockaddr_in client_addr;
memset(&server_addr, 0, sizeof(struct sockaddr_in)); //清空原始數據 防止出錯
1.5 創建線程pthread_create
創建一個int型 ret接pthread_create的返回值 返回值 代表線程是否創建成功
(函數原型)int pthread_create(pthread_t *restrict thread,
const pthread_attr_t *restrict attr,
void *(*start_routine)(void*), void *restrict arg);
restrict thread:開始就創建一個pthread_t tid[100]; 用以創建線程 因爲一次接受就需要一次線程的創建;
restrict attr: 用於指定各種不同的線程屬性,默認爲NULL
*(*start_routine)(void*): 線程創建後 用此處對應的函數 進行線程的處理(完成此線程需要的功能)
restrict arg: &fd[ i ] : 最後一個參數是運行函數的參數 哪個客戶調用這個函數了
TCPclient.c
2.1 soket()
同上面服務器的一樣
2.2 connect()
(函數原型)int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen);
Socket: 很明顯和上面差不多 這裏的sockfd就是客戶自己創建的
serv_addr: 和上面一樣 自己一開始就創建一個結構體就行 記得也要初始化
Addrlen: 大小就是結構體的大小
最後就是寫一個 send 一個recv 函數用以接收和發送信息了。
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 8888
struct 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 8888
struct 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]);//自己從運行程序的時候傳進來
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;
}