方案一TCP 完成聊天室的編寫

完成聊天室 需要聊天 那就要建立連接 建立連接後 就發送信息

建立連接 和 發送信息 需要什麼函數 ?

服務器 客戶端 各自需要什麼?

服務器:

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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章